|
|
1.1 root 1: /*
2: * LISTHSCR.C
3: *
4: * Added functions to support horizontal listbox scrolling. This
5: * DLL is generalized to support any listbox. The FInitListboxExtents
6: * function allocates local memory (from the DLLs DATA segment) for
7: * the list of string extents to go in the listbox. The local handle
8: * is then assigned as a property of the window, so every other
9: * function first looks at this property.
10: *
11: * This means that any number of horizontal scrolling listboxes can
12: * be used in the system and make use of these functions, as long
13: * as the DLLs memory is not full.
14: *
15: */
16:
17:
18: #include <windows.h>
19: #include "listhscr.h"
20:
21:
22: /*
23: * This is just the label of the property given to each listbox
24: * that asks for an extent list.
25: */
26:
27: char szXTList[]="XTList";
28:
29:
30:
31:
32:
33: /*
34: * FInitListboxExtents
35: *
36: * Purpose:
37: * Simple helper function to initialize everything for maintaining
38: * horizontal extents in a listbox. This function allocates memory
39: * to hold the list of extents and assigns it to the window as a property.
40: *
41: * Parameters:
42: * hList HWND of the listbox concerned.
43: *
44: * Return Value:
45: * BOOL TRUE if the function was successful.
46: * FALSE if memory could not be allocated.
47: */
48:
49: BOOL FAR PASCAL FInitListboxExtents(HWND hList)
50: {
51: HANDLE hMem;
52: WORD *pw;
53:
54: /*
55: * Initially allocate 260 bytes, or 130 WORDs since the majority
56: * of listbox usage will not require a reallocation, and
57: * allocating 256 bytes is just as efficient as allocating 2
58: * bytes, if not more so because of reduces overhead.
59: *
60: * The extra two words store the current number of extent entries
61: * and the maximum number possible in this memory block.
62: *
63: */
64:
65: hMem=LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, CBALLOCUNIT + sizeof(WORD)<<1);
66:
67: if (hMem==NULL)
68: return FALSE;
69:
70: /*
71: * Set the first two words in the memory to the appropriate values.
72: * If we can't lock it we can;'t use it!
73: */
74: pw=(WORD *)LocalLock(hMem);
75:
76: if (pw==NULL)
77: {
78: LocalFree(hMem);
79: return FALSE;
80: }
81:
82: *pw=0; //cExtentEntries
83: *(pw+1)=CALLOCUNITS; //cExtentEntriesMax
84:
85: LocalUnlock(hMem);
86:
87: /*
88: * Assign the memory handle as a property of the listbox. This allows
89: * this code to take any hList and get it's extent entry list,
90: * therefore having full support for multiple listboxes.
91: */
92: SetProp(hList, (LPSTR)szXTList, hMem);
93: return TRUE;
94: }
95:
96:
97:
98:
99:
100: /*
101: * FFreeListboxExtents
102: *
103: * Purpose:
104: * Release any memory used for storing the extents of the
105: * horizontal listbox. This MUST be called when the listbox
106: * is destroyed, like in the WM_DESTROY case of the parent window.
107: *
108: * Parameters:
109: * hList HWND of the listbox concerned.
110: *
111: * Return Value:
112: * BOOL TRUE if the function was successful.
113: * FALSE if there is an error.
114: */
115:
116: BOOL FAR PASCAL FFreeListboxExtents(HWND hList)
117: {
118: HANDLE hMem;
119: BOOL fSuccess;
120:
121: //Load the handle to free.
122: hMem=GetProp(hList, (LPSTR)szXTList);
123:
124: /*
125: * Return a BOOL on the result. An app could keep calling this
126: * function until it worked since hMem is still around.
127: */
128: fSuccess=(BOOL)LocalFree(hMem);
129:
130: if (fSuccess)
131: RemoveProp(hList, (LPSTR)szXTList); //Only if handle was freed!
132:
133: return fSuccess;
134: }
135:
136:
137:
138:
139: /*
140: * ResetListboxExtents
141: *
142: * Purpose:
143: * Deletes all extents in the extent list to be used AFTER an
144: * LB_RESETCONTENT is sent to the listbox.
145: *
146: * Parameters:
147: * hList HWND of the listbox.
148: *
149: * Return Value:
150: * none
151: *
152: */
153:
154: void FAR PASCAL ResetListboxExtents(HWND hList)
155: {
156: FFreeListboxExtents(hList);
157: FInitListboxExtents(hList);
158:
159: SendMessage(hList, LB_SETHORIZONTALEXTENT, 0, 0L);
160:
161: //This is required to remove the scrollbar.
162: SendMessage(hList, LB_DELETESTRING, 0, 0L);
163: return;
164: }
165:
166:
167:
168:
169:
170: /*
171: * WAddExtentEntry
172: *
173: * Purpose:
174: * Facilitates handling of the horizontal listbox by keeping
175: * track of the pixel width of the longest string in the listbox.
176: * The number of pixels that the listbox scrolls is the width
177: * of the longest string.
178: *
179: * Parameters:
180: * hList HWND of the listbox.
181: * psz Pointer to string that is added. This must be passed
182: * instead of an index into the listbox since this must
183: * be called before the string is added if the scrollbar
184: * is to be maintained properly.
185: *
186: * Return Value:
187: * WORD 0 if the string added was not the longest string in
188: * the listbox and therefore did not change the visibility
189: * of the horizontal scrollbar.
190: *
191: * wExtent if the added string was the longest, thus either
192: * making the scrollbar visible or changing the extent.
193: *
194: * -1 on an error.
195: *
196: */
197:
198: WORD FAR PASCAL WAddExtentEntry(HWND hList, LPSTR psz)
199: {
200: HANDLE hMem;
201: WORD cExtentEntries;
202: WORD cExtentEntriesMax;
203: WORD *pw; //Pointer to extent memory.
204: WORD wExtent;
205: WORD i=0;
206: WORD iRev;
207:
208:
209: hMem=GetProp(hList, (LPSTR)szXTList);
210:
211: if (hMem==NULL)
212: return ((WORD)-1);
213:
214:
215: pw=(WORD *)LocalLock(hMem);
216:
217: if (pw==NULL)
218: return ((WORD)-1);
219:
220: //Load the values and set pointer to start of list.
221: cExtentEntries=*pw++;
222: cExtentEntriesMax=*pw++;
223:
224: //Reallocate if necessary.
225: if (cExtentEntries==cExtentEntriesMax)
226: {
227: LocalUnlock(hMem);
228:
229: //This call takes care of cExtentEntriesMax
230: if (!FReAllocExtentList(hMem, TRUE))
231: return ((WORD)-1);
232:
233: cExtentEntriesMax += CALLOCUNITS;
234: pw=(WORD *)LocalLock(hMem);
235:
236: if (pw==NULL)
237: return ((WORD)-1);
238:
239: pw+=2; //Skip the two counters.
240: }
241:
242: wExtent=WGetListboxStringExtent(hList, psz);
243:
244:
245: /*
246: * Insert the new extent into the list. This list is just a sorted
247: * list (descending) of the largest to smallest extents in the
248: * listbox. When deleting a string, we just need to look in this
249: * list for it's extent and remove that entry.
250: *
251: * Yeah, this can be inefficient, but this is not a real case for
252: * optimization.
253: *
254: */
255:
256: if (cExtentEntries==0)
257: pw[0]=wExtent;
258: else
259: {
260: i=IFindExtentInList(pw, wExtent, cExtentEntries);
261:
262: for (iRev=cExtentEntries+1; iRev > i; iRev--)
263: pw[iRev]=pw[iRev-1];
264:
265: pw[i]=wExtent;
266: }
267:
268: cExtentEntries++;
269:
270: //Save these values back. pw must be decremented first.
271: *(--pw)=cExtentEntriesMax;
272: *(--pw)=cExtentEntries;
273:
274: LocalUnlock(hMem);
275:
276:
277: /*
278: * Check if the one we added is now the first. If so, then
279: * we need to reset the horizontal extent.
280: */
281:
282: if (i==0)
283: {
284: SendMessage(hList, LB_SETHORIZONTALEXTENT, wExtent, 0L);
285: return wExtent;
286: }
287:
288: return ((WORD)0);
289: }
290:
291:
292:
293:
294:
295:
296:
297:
298: /*
299: * WRemoveExtentEntry
300: *
301: * Purpose:
302: * Facilitates handling of the horizontal listbox by keeping
303: * track of the pixel width of the longest string in the listbox.
304: * The number of pixels that the listbox scrolls is the width
305: * of the longest string.
306: *
307: * Parameters:
308: * hList HWND of the listbox.
309: * iSel WORD index of the string to be removed.
310: *
311: * Return Value:
312: * WORD 0 if the string removed did not affect the visibilty
313: * of the horizontal scrollbar, i.e. if there still is
314: * a longer string or there is no scrollbar in the first
315: * place.
316: *
317: * wExtent of the new longest string if the one removed
318: * was the longest.
319: *
320: * -1 on an error.
321: */
322:
323: WORD FAR PASCAL WRemoveExtentEntry(HWND hList, WORD iSel)
324: {
325: WORD *pw; //Pointer to extent memory.
326: WORD cExtentEntries;
327: WORD cExtentEntriesMax;
328: WORD wExtent;
329: WORD i;
330: WORD iSave;
331: HANDLE hMem;
332: HANDLE hMemT;
333: char *pch;
334: WORD cb;
335:
336:
337: hMem=GetProp(hList, (LPSTR)szXTList);
338:
339: if (hMem==NULL)
340: return ((WORD)-1);
341:
342:
343: pw=(WORD *)LocalLock(hMem);
344:
345: if (pw==NULL)
346: return ((WORD)-1);
347:
348: //Load the values and set pointer to start of list.
349: cExtentEntries=*pw++;
350: cExtentEntriesMax=*pw++;
351:
352: if (cExtentEntries==0)
353: {
354: LocalUnlock(hMem);
355: return ((WORD)-1);
356: }
357:
358:
359: //Free up memory if necessary. No reallocating smaller is not fatal.
360: if ((cExtentEntriesMax-cExtentEntries)==CALLOCUNITS)
361: {
362: LocalUnlock(hMem);
363:
364: if (!FReAllocExtentList(hMem, FALSE))
365: return ((WORD)-1);
366:
367: cExtentEntriesMax += CALLOCUNITS;
368: pw=(WORD *)LocalLock(hMem);
369:
370: if (pw==NULL)
371: return ((WORD)-1);
372:
373: pw+=2; //Skip the two counters.
374: }
375:
376: cb=(WORD)SendMessage(hList, LB_GETTEXTLEN, iSel, 0L);
377:
378: //Temporary memory to copy the listbox string so we can get the extent.
379: hMemT=LocalAlloc(LMEM_MOVEABLE, cb+2); //One extra to be safe.
380: pch=LocalLock(hMemT);
381:
382: if (pch==NULL)
383: {
384: LocalUnlock(hMem);
385: LocalFree(hMemT);
386: return ((WORD)-1);
387: }
388:
389: cb=(WORD)SendMessage(hList, LB_GETTEXT, iSel, (LONG)(LPSTR)pch);
390:
391: wExtent=WGetListboxStringExtent(hList, (LPSTR)pch);
392:
393: LocalUnlock(hMemT);
394: LocalFree(hMemT);
395:
396:
397: /*
398: * Find the extent in the list and remove it. If it's the first,
399: * then reset the horizontal extent to the second.
400: */
401:
402: i=IFindExtentInList(pw, wExtent, cExtentEntries);
403: iSave=i;
404:
405: while (i < cExtentEntries)
406: pw[i++]=pw[i+1];
407:
408: cExtentEntries--;
409:
410: //Save these values back. pw must be decremented first.
411: *(--pw)=cExtentEntriesMax;
412: *(--pw)=cExtentEntries;
413:
414: LocalUnlock(hMem);
415:
416: if (iSave==0)
417: {
418: /*
419: * Before we change the horizontal extent, we must make sure that
420: * the origin of the listbox is visible through forcing a scroll.
421: * If this is not done, and the listbox is scrolled one or
422: * more pixels to the right, the scrollbar WILL NOT disappear
423: * even if all remaining strings fit inside the client area
424: * of the listbox.
425: *
426: * This is only done here since this the only case where this
427: * might happen is when we change the extent.
428: */
429: SendMessage(hList, WM_HSCROLL, SB_TOP, MAKELONG(0, hList));
430: SendMessage(hList, LB_SETHORIZONTALEXTENT, pw[2], 0L);
431:
432: return pw[2];
433: }
434:
435:
436: return ((WORD)0);
437: }
438:
439:
440:
441:
442:
443:
444: /*
445: * FReAllocExtentList
446: *
447: * Purpose:
448: * Handles reallocation of the list in blocks of +/- CBALLOCUNIT
449: *
450: * Parameters:
451: * fGrow BOOL if TRUE, instructs this function to allocate
452: * an additional ALLOCUNIT.
453: * If FALSE, shrinks the memory block by an ALLOCUNIT.
454: *
455: * Return Value:
456: * BOOL TRUE if successfully reallocated. FALSE otherwise.
457: *
458: */
459:
460: BOOL FReAllocExtentList(HANDLE hMem, BOOL fGrow)
461: {
462: WORD wSize;
463:
464: /*
465: * Allocate an additional 128 entries. A 256 byte block is a
466: * decent reallocation size.
467: */
468: wSize=LocalSize((HLOCAL)hMem);
469: wSize+=(fGrow) ? ((int)CBALLOCUNIT) : (-(int)CBALLOCUNIT);
470:
471: /*
472: * This returns FALSE if the realloc was unsuccessful. TRUE
473: * otherwise because the return handle is !=0
474: *
475: */
476: return (BOOL)LocalReAlloc(hMem, wSize, LMEM_MOVEABLE | LMEM_ZEROINIT);
477: }
478:
479:
480:
481:
482:
483:
484: /*
485: * WGetListboxStringExtent
486: *
487: * Purpose:
488: * Returns the extent, in pixels, of a string that will be or is
489: * in a listbox. The hDC of the listbox is used and an extra
490: * average character width is added to the extent to insure that
491: * a horizontal scrolling listbox that is based on this extent
492: * will scroll such that the end of the string is visible.
493: *
494: * Parameters:
495: * hList HWND handle to the listbox.
496: * psz LPSTR pointer to string in question.
497: *
498: * Return Value:
499: * WORD Extent of the string relative to listbox.
500: *
501: */
502:
503: WORD WGetListboxStringExtent(HWND hList, LPSTR psz)
504: {
505: TEXTMETRIC tm;
506: HDC hDC;
507: HFONT hFont;
508: WORD wExtent;
509:
510:
511: /*
512: * Make sure we are using the correct font.
513: */
514: hDC=GetDC(hList);
515: hFont=(HFONT)SendMessage(hList, WM_GETFONT, 0, 0L);
516:
517: if (hFont!=NULL)
518: SelectObject(hDC, hFont);
519:
520: GetTextMetrics(hDC, &tm);
521:
522: /*
523: * Add one average text width to insure that we see the end of the
524: * string when scrolled horizontally.
525: */
526:
527: //
528: // changed to GetTextExtentPoint - KoryG - 3/24/92
529: //
530: {
531: SIZE Size;
532:
533: GetTextExtentPoint(hDC, psz, lstrlen(psz), &Size);
534: // fudge for tabs!
535: wExtent=(WORD)Size.cx + (WORD)tm.tmAveCharWidth + 180;
536: }
537:
538: ReleaseDC(hList, hDC);
539:
540: return wExtent;
541: }
542:
543:
544:
545:
546:
547:
548: /*
549: * IFindExtentInList
550: *
551: * Purpose:
552: * Does an binary search on the sorted extent list and returns
553: * an index to the one that matches. If there is no match,
554: * the index gives the point where the extent entry should go.
555: *
556: * Note that an altered search algorithm is used since the list
557: * is descending instead of ascending.
558: *
559: * Parameters:
560: * pw WORD * pointer to extent list.
561: * wExtent WORD extent to find or find an index for.
562: * cExtentEntries WORD count of entries in list.
563: *
564: * Return Value:
565: * iExtent WORD index into lpw where wExtent exists or where
566: * it should be inserted.
567: *
568: */
569:
570: WORD IFindExtentInList(WORD *pw, WORD wExtent, WORD cExtentEntries)
571: {
572: int i = 0; //These MUST be signed!
573: int iPrev;
574: int iMin;
575: int iMax;
576:
577: //Set upper limits on search.
578: iMin=0;
579: iMax=cExtentEntries+1;
580:
581:
582: do
583: {
584: iPrev=i;
585: i=(iMin + iMax) >> 1;
586:
587: if (i==iPrev)
588: {
589: i++;
590: break;
591: }
592:
593: //Change the min and max depending on which way we need to look.
594: if (wExtent < pw[i]) // < since list is descending. > otherwise
595: iMin=i;
596: else
597: iMax=i;
598:
599: if (iMax==iMin)
600: break;
601: }
602: while (wExtent != pw[i]);
603:
604:
605: /*
606: * When we get here, i is either where wExtent is or where it should
607: * go--so return it.
608: */
609: return i;
610: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.