Release 951212
[wine] / controls / listbox.c
1 /*
2  * Listbox controls
3  * 
4  * Copyright  Martin Ayotte, 1993
5  *            Constantine Sapuntzakis, 1995
6  *            Alex Korobka, 1995 
7  * 
8  */
9
10  /*
11   * FIXME: 
12   * - check if multi-column listboxes work
13   * - implement more messages and styles (LB_EXTENDEDSEL for instance)
14   * - exterminate evil InvalidateRect(whole listbox) where possible!!!!
15   */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <ctype.h>
21 #include "windows.h"
22 #include "user.h"
23 #include "win.h"
24 #include "msdos.h"
25 #include "listbox.h"
26 #include "dos_fs.h"
27 #include "stddebug.h"
28 #include "debug.h"
29 #include "xmalloc.h"
30
31 #if 0
32 #define LIST_HEAP_ALLOC(lphl,f,size) ((int)HEAP_Alloc(&lphl->Heap,f,size) & 0xffff)
33 #define LIST_HEAP_FREE(lphl,handle) (HEAP_Free(&lphl->Heap,LIST_HEAP_ADDR(lphl,handle)))
34 #define LIST_HEAP_ADDR(lphl,handle) \
35     ((void *)((handle) ? ((handle) | ((int)lphl->Heap & 0xffff0000)) : 0))
36 #else
37 /* FIXME: shouldn't each listbox have its own heap? */
38 #if 0
39 #define LIST_HEAP_ALLOC(lphl,f,size) USER_HEAP_ALLOC(size)
40 #define LIST_HEAP_FREE(lphl,handle)  USER_HEAP_FREE(handle)
41 #define LIST_HEAP_ADDR(lphl,handle)  USER_HEAP_LIN_ADDR(handle)
42 #define LIST_HEAP_SEG_ADDR(lphl,handle) USER_HEAP_SEG_ADDR(handle)
43 #else
44 /* Something like this maybe ? */
45 #define LIST_HEAP_ALLOC(lphl,f,size) \
46             LOCAL_Alloc( lphl->HeapSel, LMEM_FIXED, (size) )
47 #if 0
48 #define LIST_HEAP_REALLOC(handle,size) \
49             LOCAL_ReAlloc( USER_HeapSel, (handle), (size), LMEM_FIXED )
50 #endif
51 #define LIST_HEAP_FREE(lphl,handle) \
52             LOCAL_Free( lphl->HeapSel, (handle) )
53 #define LIST_HEAP_ADDR(lphl,handle)  \
54             ((handle) ? PTR_SEG_OFF_TO_LIN(lphl->HeapSel, (handle)) : NULL)
55 #define LIST_HEAP_SEG_ADDR(lphl,handle)  \
56             ((handle) ? MAKELONG((handle), lphl->HeapSel) : 0)
57 #endif
58 #endif
59
60 #define LIST_HEAP_SIZE 0x10000
61
62 static void ListBoxInitialize(LPHEADLIST lphl)
63 {
64   lphl->lpFirst        = NULL;
65   lphl->ItemsCount     = 0;
66   lphl->ItemsVisible   = 0;
67   lphl->FirstVisible   = 0;
68   lphl->ColumnsVisible = 1;
69   lphl->ItemsPerColumn = 0;
70   lphl->ItemFocused    = -1;
71   lphl->PrevFocused    = -1;
72 }
73
74 void CreateListBoxStruct(HWND hwnd, WORD CtlType, LONG styles, HWND parent)
75 {
76   LPHEADLIST lphl;
77
78   lphl = (LPHEADLIST)xmalloc(sizeof(HEADLIST));
79   SetWindowLong(hwnd, 0, (LONG)lphl);
80   ListBoxInitialize(lphl);
81   lphl->DrawCtlType    = CtlType;
82   lphl->CtlID          = GetWindowWord(hwnd,GWW_ID);
83   lphl->bRedrawFlag    = TRUE;
84   lphl->iNumStops      = 0;
85   lphl->TabStops       = NULL;
86   lphl->hFont          = GetStockObject(SYSTEM_FONT);
87   lphl->hSelf          = hwnd;  
88   lphl->hParent        = parent;
89   lphl->StdItemHeight  = 15; /* FIXME: should get the font height */
90   lphl->OwnerDrawn     = styles & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE);
91   lphl->HasStrings     = (styles & LBS_HASSTRINGS) || !lphl->OwnerDrawn;
92
93   if (lphl->OwnerDrawn) {
94     LISTSTRUCT dummyls;
95     
96     lphl->hDrawItemStruct = USER_HEAP_ALLOC(sizeof(DRAWITEMSTRUCT));
97     lphl->needMeasure = TRUE;
98     dummyls.mis.CtlType    = lphl->DrawCtlType;
99     dummyls.mis.CtlID      = lphl->CtlID;
100     dummyls.mis.itemID     = -1;
101     dummyls.mis.itemWidth  = 0; /* ignored */
102     dummyls.mis.itemData   = 0;
103
104     ListBoxAskMeasure(lphl,&dummyls);
105   } else {
106     lphl->hDrawItemStruct = 0;
107   }
108
109 #if 0
110   HeapHandle = GlobalAlloc(GMEM_FIXED, LIST_HEAP_SIZE);
111   HeapBase = GlobalLock(HeapHandle);
112   HEAP_Init(&lphl->Heap, HeapBase, LIST_HEAP_SIZE);
113 #endif
114 /* WINELIBS list boxes do not operate on local heaps */
115 #ifndef WINELIB
116   lphl->HeapSel = GlobalAlloc(GMEM_FIXED,LIST_HEAP_SIZE);
117   LocalInit( lphl->HeapSel, 0, LIST_HEAP_SIZE-1);
118 #else
119   lphl->HeapSel = 0;
120 #endif
121 }
122
123 void DestroyListBoxStruct(LPHEADLIST lphl)
124 {
125   if (lphl->hDrawItemStruct)
126     USER_HEAP_FREE(lphl->hDrawItemStruct);
127
128   /* XXX need to free lphl->Heap */
129   GlobalFree(lphl->HeapSel);
130   free(lphl);
131 }
132
133 static LPHEADLIST ListBoxGetStorageHeader(HWND hwnd)
134 {
135     return (LPHEADLIST)GetWindowLong(hwnd,0);
136 }
137
138 /* Send notification "code" as part of a WM_COMMAND-message if hwnd
139    has the LBS_NOTIFY style */
140 void ListBoxSendNotification(LPHEADLIST lphl, WORD code)
141 {
142   DWORD dwStyle = GetWindowLong(lphl->hSelf,GWL_STYLE);
143
144   if (dwStyle & LBS_NOTIFY)
145 #ifdef WINELIB32
146     SendMessage(lphl->hParent, WM_COMMAND,
147                 MAKEWPARAM(lphl->CtlID,code), (LPARAM)lphl->hSelf);
148 #else
149     SendMessage(lphl->hParent, WM_COMMAND,
150                 lphl->CtlID, MAKELONG(lphl->hSelf, code));
151 #endif
152 }
153
154
155 /* get the maximum value of lphl->FirstVisible */
156 int ListMaxFirstVisible(LPHEADLIST lphl)
157 {
158     int m = lphl->ItemsCount-lphl->ItemsVisible;
159     return (m < 0) ? 0 : m;
160 }
161
162
163 void ListBoxUpdateWindow(HWND hwnd, LPHEADLIST lphl, BOOL repaint)
164 {
165   SetScrollRange(hwnd, SB_VERT, 0, ListMaxFirstVisible(lphl), TRUE);
166   if (lphl->ItemsPerColumn != 0) {
167     SetScrollRange(hwnd, SB_HORZ, 1, lphl->ItemsVisible /
168                    lphl->ItemsPerColumn + 1, TRUE);
169   }
170   if (repaint && lphl->bRedrawFlag) {
171     InvalidateRect(hwnd, NULL, TRUE);
172   }
173 }
174
175 /* Returns: 0 if nothing needs to be changed */
176 /*          1 if FirstVisible changed */
177
178 int ListBoxScrollToFocus(LPHEADLIST lphl)
179 {
180   short       end;
181
182   if (lphl->ItemsCount == 0) return 0;
183   if (lphl->ItemFocused == -1) return 0;
184
185   end = lphl->FirstVisible + lphl->ItemsVisible - 1;
186
187   if (lphl->ItemFocused < lphl->FirstVisible ) {
188     lphl->FirstVisible = lphl->ItemFocused;
189     return 1;
190   } else {
191     if (lphl->ItemFocused > end) {
192       WORD maxFirstVisible = ListMaxFirstVisible(lphl);
193
194       lphl->FirstVisible = lphl->ItemFocused;
195       
196       if (lphl->FirstVisible > maxFirstVisible) {
197         lphl->FirstVisible = maxFirstVisible;
198       }
199       return 1;
200     }
201   } 
202   return 0;
203 }
204
205
206 LPLISTSTRUCT ListBoxGetItem(LPHEADLIST lphl, UINT uIndex)
207 {
208   LPLISTSTRUCT lpls;
209   UINT         Count = 0;
210
211   if (uIndex >= lphl->ItemsCount) return NULL;
212
213   lpls = lphl->lpFirst;
214   while (Count++ < uIndex) lpls = lpls->lpNext;
215   return lpls;
216 }
217
218
219 void ListBoxDrawItem (HWND hwnd, LPHEADLIST lphl, HDC hdc, LPLISTSTRUCT lpls, 
220                       RECT *rect, WORD itemAction, WORD itemState)
221 {
222   LONG dwStyle = GetWindowLong(hwnd,GWL_STYLE);
223
224   if (lphl->OwnerDrawn) {
225     DRAWITEMSTRUCT   *dis = USER_HEAP_LIN_ADDR(lphl->hDrawItemStruct);
226
227     dis->CtlID    = lpls->mis.CtlID;
228     dis->CtlType  = lpls->mis.CtlType;
229     dis->itemID   = lpls->mis.itemID;
230     dis->hDC      = hdc;
231     dis->hwndItem = hwnd;
232     dis->itemData = lpls->mis.itemData;
233     dis->itemAction = itemAction;
234     dis->itemState  = itemState;
235     dis->rcItem     = *rect;
236     SendMessage(lphl->hParent, WM_DRAWITEM,
237                 0, (LPARAM)USER_HEAP_SEG_ADDR(lphl->hDrawItemStruct));
238   } else {
239     if (itemAction == ODA_DRAWENTIRE || itemAction == ODA_SELECT) {
240       int       OldBkMode;
241       DWORD     dwOldTextColor = 0;
242
243       OldBkMode = SetBkMode(hdc, TRANSPARENT);
244
245       if (itemState != 0) {
246         dwOldTextColor = SetTextColor(hdc, 0x00FFFFFFL);
247         FillRect(hdc, rect, GetStockObject(BLACK_BRUSH));
248       }
249
250       if (dwStyle & LBS_USETABSTOPS) {
251         TabbedTextOut(hdc, rect->left + 5, rect->top + 2, 
252                       (char *)lpls->itemText, strlen((char *)lpls->itemText), 
253                       lphl->iNumStops, lphl->TabStops, 0);
254       } else {
255         TextOut(hdc, rect->left + 5, rect->top + 2,
256                 (char *)lpls->itemText, strlen((char *)lpls->itemText));
257       }
258
259       if (itemState != 0) {
260         SetTextColor(hdc, dwOldTextColor);
261       }
262       
263       SetBkMode(hdc, OldBkMode);
264     } else DrawFocusRect(hdc, rect);
265   }
266
267   return;
268 }
269
270
271 int ListBoxFindMouse(LPHEADLIST lphl, int X, int Y)
272 {
273   LPLISTSTRUCT lpls = lphl->lpFirst;
274   int          i, j;
275   POINT        point;
276   
277   point.x = X; point.y = Y;
278   if (lphl->ItemsCount == 0) return LB_ERR;
279
280   for(i = 0; i < lphl->FirstVisible; i++) {
281     if (lpls == NULL) return LB_ERR;
282     lpls = lpls->lpNext;
283   }
284   for(j = 0; j < lphl->ItemsVisible; i++, j++) {
285     if (lpls == NULL) return LB_ERR;
286     if (PtInRect(&lpls->itemRect,point)) {
287       return i;
288     }
289     lpls = lpls->lpNext;
290   }
291   dprintf_listbox(stddeb,"ListBoxFindMouse: not found\n");
292   return LB_ERR;
293 }
294
295
296 void ListBoxAskMeasure(LPHEADLIST lphl, LPLISTSTRUCT lpls)  
297 {
298   HANDLE hTemp = USER_HEAP_ALLOC( sizeof(MEASUREITEMSTRUCT) );
299   MEASUREITEMSTRUCT *lpmeasure = (MEASUREITEMSTRUCT *) USER_HEAP_LIN_ADDR(hTemp);
300
301   if (lpmeasure == NULL) {
302     fprintf(stdnimp,"ListBoxAskMeasure() out of memory !\n");
303     return;
304   }
305  
306   *lpmeasure = lpls->mis;
307   lpmeasure->itemHeight = lphl->StdItemHeight;
308   SendMessage(lphl->hParent, WM_MEASUREITEM, 0, (LPARAM)USER_HEAP_SEG_ADDR(hTemp));
309
310   if (GetWindowLong(lphl->hSelf,GWL_STYLE) & LBS_OWNERDRAWFIXED) {
311     lphl->StdItemHeight = lpmeasure->itemHeight;
312     lphl->needMeasure = FALSE;
313   }
314
315   USER_HEAP_FREE(hTemp);                        
316 }
317
318 /* -------------------- strings and item data ---------------------- */
319
320 LPLISTSTRUCT ListBoxCreateItem(LPHEADLIST lphl, int id)
321 {
322   LPLISTSTRUCT lplsnew = (LPLISTSTRUCT)malloc(sizeof(LISTSTRUCT));
323
324   if (lplsnew == NULL) return NULL;
325   
326   lplsnew->itemState      = 0;
327   lplsnew->mis.CtlType    = lphl->DrawCtlType;
328   lplsnew->mis.CtlID      = lphl->CtlID;
329   lplsnew->mis.itemID     = id;
330   lplsnew->mis.itemHeight = lphl->StdItemHeight;
331   lplsnew->mis.itemWidth  = 0; /* ignored */
332   lplsnew->mis.itemData   = 0;
333   SetRect(&lplsnew->itemRect, 0, 0, 0, 0);
334
335   return lplsnew;
336 }
337
338
339 int ListBoxInsertString(LPHEADLIST lphl, UINT uIndex, LPSTR newstr)
340 {
341   LPLISTSTRUCT *lppls, lplsnew, lpls;
342   HANDLE       hStr;
343   LPSTR str;
344   UINT  Count;
345     
346   dprintf_listbox(stddeb,"ListBoxInsertString(%d, %p);\n", uIndex, newstr);
347     
348   if (!newstr) return -1;
349
350   if (uIndex == (UINT)-1)
351     uIndex = lphl->ItemsCount;
352
353   lppls = &lphl->lpFirst;
354   for(Count = 0; Count < uIndex; Count++) {
355     if (*lppls == NULL) return LB_ERR;
356     lppls = (LPLISTSTRUCT *) &(*lppls)->lpNext;
357   }
358     
359   lplsnew = ListBoxCreateItem(lphl, Count);
360   
361   if (lplsnew == NULL) {
362     fprintf(stdnimp,"ListBoxInsertString() out of memory !\n");
363     return LB_ERRSPACE;
364   }
365
366   lplsnew->lpNext = *lppls;
367   *lppls = lplsnew;
368   lphl->ItemsCount++;
369   
370   hStr = 0;
371   if (lphl->HasStrings) {
372     dprintf_listbox(stddeb,"  string: %s\n", newstr);
373     hStr = LIST_HEAP_ALLOC(lphl, LMEM_MOVEABLE, strlen(newstr) + 1);
374     str = (LPSTR)LIST_HEAP_ADDR(lphl, hStr);
375     if (str == NULL) return LB_ERRSPACE;
376     strcpy(str, newstr);
377     lplsnew->itemText = str;
378     /* I'm not so sure about the next one */
379     lplsnew->mis.itemData = 0;
380   } else {
381     lplsnew->itemText = NULL;
382     lplsnew->mis.itemData = (DWORD)newstr;
383   }
384
385   lplsnew->mis.itemID = uIndex;
386   lplsnew->hData = hStr;
387   
388   /* adjust the itemID field of the following entries */
389   for(lpls = lplsnew->lpNext; lpls != NULL; lpls = lpls->lpNext) {
390       lpls->mis.itemID++;
391   }
392  
393   if (lphl->needMeasure) {
394     ListBoxAskMeasure(lphl, lplsnew);
395   }
396
397   dprintf_listbox(stddeb,"ListBoxInsertString // count=%d\n", lphl->ItemsCount);
398   return uIndex;
399 }
400
401
402 int ListBoxAddString(LPHEADLIST lphl, LPSTR newstr)
403 {
404     LONG dwStyle = GetWindowLong(lphl->hSelf,GWL_STYLE);
405     UINT pos = (UINT) -1;
406     
407     if (lphl->HasStrings && (dwStyle & LBS_SORT)) {
408         LPLISTSTRUCT lpls = lphl->lpFirst;
409         for (pos = 0; lpls != NULL; lpls = lpls->lpNext, pos++)
410             if (strcmp(lpls->itemText, newstr) >= 0)
411                 break;
412     }
413     return ListBoxInsertString(lphl, pos, newstr);
414 }
415
416
417 int ListBoxGetText(LPHEADLIST lphl, UINT uIndex, LPSTR OutStr)
418 {
419   LPLISTSTRUCT lpls;
420
421   if (!OutStr) {
422     dprintf_listbox(stddeb, "ListBoxGetText // OutStr==NULL\n");
423     return 0;
424   }
425
426   lpls = ListBoxGetItem (lphl, uIndex);
427   if (lpls == NULL) return LB_ERR;
428
429   if (!lphl->HasStrings) {
430     *((long *)OutStr) = lpls->mis.itemData;
431     return 4;
432   }
433         
434   strcpy(OutStr, lpls->itemText);
435   return strlen(OutStr);
436 }
437
438
439 DWORD ListBoxGetItemData(LPHEADLIST lphl, UINT uIndex)
440 {
441   LPLISTSTRUCT lpls;
442
443   lpls = ListBoxGetItem (lphl, uIndex);
444   if (lpls == NULL) return LB_ERR;
445   return lpls->mis.itemData;
446 }
447
448
449 int ListBoxSetItemData(LPHEADLIST lphl, UINT uIndex, DWORD ItemData)
450 {
451   LPLISTSTRUCT lpls = ListBoxGetItem(lphl, uIndex);
452
453   if (lpls == NULL) return LB_ERR;
454   lpls->mis.itemData = ItemData;
455   return 1;
456 }
457
458
459 int ListBoxDeleteString(LPHEADLIST lphl, UINT uIndex)
460 {
461   LPLISTSTRUCT lpls, lpls2;
462   UINT  Count;
463
464   if (uIndex >= lphl->ItemsCount) return LB_ERR;
465
466   lpls = lphl->lpFirst;
467   if (lpls == NULL) return LB_ERR;
468
469   if (uIndex == 0)
470     lphl->lpFirst = lpls->lpNext;
471   else {
472     LPLISTSTRUCT lpls2 = NULL;
473     for(Count = 0; Count < uIndex; Count++) {
474       if (lpls->lpNext == NULL) return LB_ERR;
475
476       lpls2 = lpls;
477       lpls = (LPLISTSTRUCT)lpls->lpNext;
478     }
479     lpls2->lpNext = lpls->lpNext;
480   }
481
482   /* adjust the itemID field of the following entries */
483   for(lpls2 = lpls->lpNext; lpls2 != NULL; lpls2 = lpls2->lpNext) {
484       lpls2->mis.itemID--;
485   }
486  
487   lphl->ItemsCount--;
488
489   if (lpls->hData != 0) LIST_HEAP_FREE(lphl, lpls->hData);
490   free(lpls);
491   
492   return lphl->ItemsCount;
493 }
494
495
496 int ListBoxFindString(LPHEADLIST lphl, UINT nFirst, SEGPTR MatchStr)
497 {
498   LPLISTSTRUCT lpls;
499   UINT         Count;
500   UINT         First = nFirst + 1;
501   LPSTR        lpMatchStr = (LPSTR)MatchStr;
502   LONG         dwStyle = GetWindowLong(lphl->hSelf,GWL_STYLE);
503
504   if (First > lphl->ItemsCount) return LB_ERR;
505
506   if (lphl->HasStrings) lpMatchStr = PTR_SEG_TO_LIN(MatchStr);
507   
508   lpls = ListBoxGetItem(lphl, First);
509   Count = 0;
510   while(lpls != NULL) {
511     if (lphl->HasStrings) {
512       if (strstr(lpls->itemText, lpMatchStr) == lpls->itemText) return Count;
513     } else if (dwStyle & LBS_SORT) {
514       /* XXX Do a compare item */
515     }
516     else
517       if (lpls->mis.itemData == (DWORD)lpMatchStr) return Count;
518
519     lpls = lpls->lpNext;
520     Count++;
521   }
522
523   /* Start over at top */
524   Count = 0;
525   lpls = lphl->lpFirst;
526
527   while (Count < First) {
528     if (lphl->HasStrings) {
529       if (strstr(lpls->itemText, lpMatchStr) == lpls->itemText) return Count;
530     } else if (dwStyle & LBS_SORT) {
531       /* XXX Do a compare item */
532     } else {
533       if (lpls->mis.itemData == (DWORD)lpMatchStr) return Count;
534     }
535     lpls = lpls->lpNext;
536     Count++;
537   }
538
539   return LB_ERR;
540 }
541
542
543 int ListBoxResetContent(LPHEADLIST lphl)
544 {
545     LPLISTSTRUCT lpls;
546     int i;
547
548     if (lphl->ItemsCount == 0) return 0;
549
550     dprintf_listbox(stddeb, "ListBoxResetContent // ItemCount = %d\n",
551         lphl->ItemsCount);
552
553     for(i = 0; i < lphl->ItemsCount; i++) {
554       lpls = lphl->lpFirst;
555       if (lpls == NULL) return LB_ERR;
556       
557       lphl->lpFirst = lpls->lpNext;
558       if (lpls->hData != 0) LIST_HEAP_FREE(lphl, lpls->hData);
559       free(lpls);
560     }
561     ListBoxInitialize(lphl);
562
563     return TRUE;
564 }
565
566 /* --------------------- selection ------------------------- */
567
568 int ListBoxSetCurSel(LPHEADLIST lphl, WORD wIndex)
569 {
570   LPLISTSTRUCT lpls;
571   DWORD        dwStyle = GetWindowWord(lphl->hSelf,GWL_STYLE);
572
573   /* use ListBoxSetSel instead */
574   if (dwStyle & LBS_MULTIPLESEL ) return 0;
575
576   /* unselect all previously selected */
577   if (dwStyle & LBS_EXTENDEDSEL )
578       ListBoxSetSel(lphl,-1,0);
579   else 
580       /* unselect previous item */
581   if (lphl->ItemFocused != -1) {
582     lpls = ListBoxGetItem(lphl, lphl->ItemFocused);
583     if (lpls == 0) return LB_ERR;
584     lpls->itemState = 0;
585   }
586
587   if (wIndex != (UINT)-1) {
588     lphl->ItemFocused = wIndex;
589     lpls = ListBoxGetItem(lphl, wIndex);
590     if (lpls == 0) return LB_ERR;
591     lpls->itemState = ODS_SELECTED | ODS_FOCUS;
592
593     return 0;
594   }
595
596   return LB_ERR;
597 }
598
599
600 int ListBoxSetSel(LPHEADLIST lphl, WORD wIndex, WORD state)
601 {
602   LPLISTSTRUCT lpls;
603
604   if (!(GetWindowLong(lphl->hSelf,GWL_STYLE) & 
605        (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)  )) 
606         return 0;
607
608   if (wIndex == (UINT)-1) {
609     for (lpls = lphl->lpFirst; lpls != NULL; lpls = lpls->lpNext) {
610       lpls->itemState = state;
611     }
612     return 0;
613   }
614
615   if (wIndex >= lphl->ItemsCount) return LB_ERR;
616
617   lpls = ListBoxGetItem(lphl, wIndex);
618   lpls->itemState = state;
619
620   return 0;
621 }
622
623
624 int ListBoxGetSel(LPHEADLIST lphl, WORD wIndex)
625 {
626   LPLISTSTRUCT lpls = ListBoxGetItem(lphl, wIndex);
627
628   if (lpls == NULL) return LB_ERR;
629   return lpls->itemState;
630 }
631
632 /* ------------------------- dir listing ------------------------ */
633
634 int ListBoxDirectory(LPHEADLIST lphl, UINT attrib, LPSTR filespec)
635 {
636   struct dosdirent *dp, *dp_old;
637   char temp[256];
638   int   drive;
639   LPSTR tstr;
640
641   dprintf_listbox(stddeb,"ListBoxDirectory: %s, %4x\n",filespec,attrib);
642
643   if (strchr(filespec, '\\') || strchr(filespec, ':')) {
644     drive = DOS_GetDefaultDrive();
645     if (filespec[1] == ':') {
646       drive = toupper(filespec[0]) - 'A';
647       filespec += 2;
648     }
649     strcpy(temp,filespec);
650     tstr = strrchr(temp, '\\');
651     if (tstr != NULL) {
652       *(tstr+1) = 0;
653       filespec += tstr - temp + 1;
654       if (!DOS_ChangeDir( drive, temp )) return 0;
655     }
656     DOS_SetDefaultDrive( drive );
657     dprintf_listbox(stddeb,"Changing directory to %c:%s, filemask is %s\n",
658                     drive+'A', temp, filespec);
659   }
660
661   if ((dp = (struct dosdirent *)DOS_opendir(filespec)) ==NULL) return 0;
662   dp_old = dp;
663   while ((dp = (struct dosdirent *)DOS_readdir(dp))) {
664     if (!dp->inuse) break;
665     dprintf_listbox(stddeb, "ListBoxDirectory %p '%s' !\n", dp->filename, 
666                     dp->filename);
667     if (dp->attribute & FA_DIREC) {
668       if (attrib & DDL_DIRECTORY && strcmp(dp->filename, ".") != 0) {
669         sprintf(temp, "[%s]", dp->filename);
670         if (ListBoxAddString(lphl, temp) == LB_ERR) break;
671       }
672     } 
673     else {
674       if (attrib & DDL_EXCLUSIVE) {
675         if (attrib & (DDL_READWRITE | DDL_READONLY | DDL_HIDDEN | DDL_SYSTEM)) {
676           if (ListBoxAddString(lphl, dp->filename) == LB_ERR) break;
677         }
678       } else {
679         if (ListBoxAddString(lphl, dp->filename) == LB_ERR) break;
680       }
681     }
682   }
683   DOS_closedir(dp_old);
684   
685   if (attrib & DDL_DRIVES) {
686     int x;
687     for (x = 0; x != MAX_DOS_DRIVES ; x++) {
688       if (DOS_ValidDrive(x)) {
689         sprintf(temp, "[-%c-]", 'a'+x);
690         if (ListBoxInsertString(lphl, (UINT)-1, temp) == LB_ERR) break;
691       }         
692     }
693   }
694   return 1;
695 }
696
697 /* ------------------------- dimensions ------------------------- */
698
699 int ListBoxGetItemRect(LPHEADLIST lphl, WORD wIndex, LPRECT lprect)
700 {
701   LPLISTSTRUCT lpls = ListBoxGetItem(lphl,wIndex);
702
703   if (lpls == NULL) return LB_ERR;
704   *lprect = lpls->itemRect;
705   return 0;
706 }
707
708
709 int ListBoxSetItemHeight(LPHEADLIST lphl, WORD wIndex, long height)
710 {
711   LPLISTSTRUCT lpls;
712
713   if (!(GetWindowLong(lphl->hSelf,GWL_STYLE) & LBS_OWNERDRAWVARIABLE)) {
714     lphl->StdItemHeight = (short)height;
715     return 0;
716   }
717   
718   lpls = ListBoxGetItem(lphl, wIndex);
719   if (lpls == NULL) return LB_ERR;
720   
721   lpls->mis.itemHeight = height;
722   return 0;
723 }
724
725 /* -------------------------- string search ------------------------ */  
726
727 int ListBoxFindNextMatch(LPHEADLIST lphl, WORD wChar)
728 {
729   LPLISTSTRUCT lpls;
730   UINT         count,first;
731
732   if ((char)wChar < ' ') return LB_ERR;
733   if (!lphl->HasStrings) return LB_ERR;
734
735   lpls = lphl->lpFirst;
736   
737   for (count = 0; lpls != NULL; lpls = lpls->lpNext, count++) {
738     if (tolower(*lpls->itemText) == tolower((char)wChar)) break;
739   }
740   if (lpls == NULL) return LB_ERR;
741   first = count;
742   for(; lpls != NULL; lpls = lpls->lpNext, count++) {
743     if (*lpls->itemText != (char)wChar) 
744       break;
745     if (count > lphl->ItemFocused)
746       return count;
747   }
748   return first;
749 }
750
751 /***********************************************************************
752  *           LBCreate
753  */
754 static LONG LBCreate(HWND hwnd, WORD wParam, LONG lParam)
755 {
756   LPHEADLIST   lphl;
757   LONG         dwStyle = GetWindowLong(hwnd,GWL_STYLE);
758   RECT rect;
759
760   CreateListBoxStruct(hwnd, ODT_LISTBOX, dwStyle, GetParent(hwnd));
761   lphl = ListBoxGetStorageHeader(hwnd);
762   dprintf_listbox(stddeb,"ListBox created: lphl = %p dwStyle = "NPFMT":"NPFMT"\n", 
763                           lphl, HIWORD(dwStyle), LOWORD(dwStyle));
764
765   GetClientRect(hwnd,&rect);
766   lphl->ColumnsWidth = rect.right - rect.left;
767
768   SetScrollRange(hwnd, SB_VERT, 0, ListMaxFirstVisible(lphl), TRUE);
769   SetScrollRange(hwnd, SB_HORZ, 1, 1, TRUE);
770
771   return 0;
772 }
773
774 /***********************************************************************
775  *           LBDestroy
776  */
777 static LONG LBDestroy(HWND hwnd, WORD wParam, LONG lParam)
778 {
779   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
780
781   ListBoxResetContent(lphl);
782
783   DestroyListBoxStruct(lphl);
784   dprintf_listbox(stddeb,"ListBox destroyed: lphl = %p\n",lphl);
785   return 0;
786 }
787
788 /***********************************************************************
789  *           LBVScroll
790  */
791 static LONG LBVScroll(HWND hwnd, WORD wParam, LONG lParam)
792 {
793   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
794   int  y;
795
796   dprintf_listbox(stddeb,"ListBox WM_VSCROLL w=%04X l=%08lX !\n",
797                   wParam, lParam);
798   y = lphl->FirstVisible;
799
800   switch(wParam) {
801   case SB_LINEUP:
802     if (lphl->FirstVisible > 0)
803       lphl->FirstVisible--;
804     break;
805
806   case SB_LINEDOWN:
807     lphl->FirstVisible++;
808     break;
809
810   case SB_PAGEUP:
811     if (lphl->FirstVisible > lphl->ItemsVisible) {
812       lphl->FirstVisible -= lphl->ItemsVisible;
813     } else {
814       lphl->FirstVisible = 0;
815     }
816     break;
817
818   case SB_PAGEDOWN:
819     lphl->FirstVisible += lphl->ItemsVisible;
820     break;
821
822   case SB_THUMBTRACK:
823     lphl->FirstVisible = LOWORD(lParam);
824     break;
825   }
826
827   if (lphl->FirstVisible > ListMaxFirstVisible(lphl))
828     lphl->FirstVisible = ListMaxFirstVisible(lphl);
829
830   if (y != lphl->FirstVisible) {
831     SetScrollPos(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
832     InvalidateRect(hwnd, NULL, TRUE);
833   }
834   return 0;
835 }
836
837 /***********************************************************************
838  *           LBHScroll
839  */
840 static LONG LBHScroll(HWND hwnd, WORD wParam, LONG lParam)
841 {
842   LPHEADLIST lphl;
843   int        y;
844
845   dprintf_listbox(stddeb,"ListBox WM_HSCROLL w=%04X l=%08lX !\n",
846                   wParam, lParam);
847   lphl = ListBoxGetStorageHeader(hwnd);
848   y = lphl->FirstVisible;
849   switch(wParam) {
850   case SB_LINEUP:
851     if (lphl->FirstVisible > lphl->ItemsPerColumn) {
852       lphl->FirstVisible -= lphl->ItemsPerColumn;
853     } else {
854       lphl->FirstVisible = 0;
855     }
856     break;
857   case SB_LINEDOWN:
858     lphl->FirstVisible += lphl->ItemsPerColumn;
859     break;
860   case SB_PAGEUP:
861     if (lphl->ItemsPerColumn != 0) {
862       int lbsub = lphl->ItemsVisible / lphl->ItemsPerColumn * lphl->ItemsPerColumn;
863       if (lphl->FirstVisible > lbsub) {
864         lphl->FirstVisible -= lbsub;
865       } else {
866         lphl->FirstVisible = 0;
867       }
868     }
869     break;
870   case SB_PAGEDOWN:
871     if (lphl->ItemsPerColumn != 0)
872       lphl->FirstVisible += lphl->ItemsVisible /
873         lphl->ItemsPerColumn * lphl->ItemsPerColumn;
874     break;
875   case SB_THUMBTRACK:
876     lphl->FirstVisible = lphl->ItemsPerColumn * LOWORD(lParam);
877     break;
878   } 
879   if (lphl->FirstVisible > ListMaxFirstVisible(lphl))
880     lphl->FirstVisible = ListMaxFirstVisible(lphl);
881
882   if (lphl->ItemsPerColumn != 0) {
883     lphl->FirstVisible = lphl->FirstVisible /
884       lphl->ItemsPerColumn * lphl->ItemsPerColumn + 1;
885     if (y != lphl->FirstVisible) {
886       SetScrollPos(hwnd, SB_HORZ, lphl->FirstVisible / 
887                    lphl->ItemsPerColumn + 1, TRUE);
888       InvalidateRect(hwnd, NULL, TRUE);
889     }
890   }
891   return 0;
892 }
893
894 /***********************************************************************
895  *           LBLButtonDown
896  */
897 static LONG LBLButtonDown(HWND hwnd, WORD wParam, LONG lParam)
898 {
899   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
900   WORD       wRet;
901   int        y;
902   RECT       rectsel;
903   LONG       dwStyle = GetWindowLong(lphl->hSelf,GWL_STYLE);
904   POINT      tmpPOINT;
905   tmpPOINT.x = LOWORD(lParam); tmpPOINT.y = HIWORD(lParam);
906
907   SetFocus(hwnd);
908   SetCapture(hwnd);
909
910   lphl->PrevFocused = lphl->ItemFocused;
911
912   y = ListBoxFindMouse(lphl, LOWORD(lParam), HIWORD(lParam));
913   if (y == -1)
914     return 0;
915
916   if (dwStyle & LBS_MULTIPLESEL) {
917     lphl->ItemFocused = y;
918     wRet = ListBoxGetSel(lphl, y);
919     ListBoxSetSel(lphl, y, !wRet);
920
921     InvalidateRect(hwnd, NULL, TRUE);
922   } else {
923     ListBoxSetCurSel(lphl, y);
924
925   ListBoxGetItemRect(lphl, y, &rectsel);
926     InvalidateRect(hwnd, &rectsel, TRUE);
927     if(lphl->PrevFocused) {
928         ListBoxGetItemRect(lphl, lphl->PrevFocused, &rectsel);
929         InvalidateRect(hwnd, &rectsel, TRUE);
930     } 
931   }
932
933   if (dwStyle & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL))
934     ListBoxSendNotification(lphl, LBN_SELCHANGE);
935
936   if (dwStyle & LBS_NOTIFY)
937     SendMessage(lphl->hParent, WM_LBTRACKPOINT, y, lParam);
938 #ifndef WINELIB
939   if (GetWindowLong(lphl->hSelf,GWL_EXSTYLE) & WS_EX_DRAGDETECT)
940      if( DragDetect(lphl->hSelf,tmpPOINT) )
941          SendMessage(lphl->hParent, WM_BEGINDRAG,0,0L);
942 #endif
943   return 0;
944 }
945
946 /***********************************************************************
947  *           LBLButtonUp
948  */
949 static LONG LBLButtonUp(HWND hwnd, WORD wParam, LONG lParam)
950 {
951   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
952
953   if (GetCapture() == hwnd) ReleaseCapture();
954
955   if (lphl->PrevFocused != lphl->ItemFocused)
956     ListBoxSendNotification(lphl, LBN_SELCHANGE);
957
958   return 0;
959 }
960
961 /***********************************************************************
962  *           LBRButtonUp
963  */
964 static LONG LBRButtonUp(HWND hwnd, WORD wParam, LONG lParam)
965 {
966   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
967
968 #ifdef WINELIB32
969   SendMessage(lphl->hParent, WM_COMMAND, 
970               MAKEWPARAM(GetWindowWord(hwnd,GWW_ID),LBN_DBLCLK),
971               (LPARAM)hwnd);
972 #else
973   SendMessage(lphl->hParent, WM_COMMAND, GetWindowWord(hwnd,GWW_ID),
974                 MAKELONG(hwnd, LBN_DBLCLK));
975 #endif
976
977   return 0;
978 }
979
980 /***********************************************************************
981  *           LBMouseMove
982  */
983 static LONG LBMouseMove(HWND hwnd, WORD wParam, LONG lParam)
984 {
985   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
986   int  y;
987   WORD wRet;
988   LONG  dwStyle = GetWindowLong(lphl->hSelf,GWL_STYLE);
989   RECT rect, rectsel;   /* XXX Broken */
990
991   dprintf_listbox(stddeb,"LBMouseMove %d %d\n",SLOWORD(lParam),SHIWORD(lParam));
992   if ((wParam & MK_LBUTTON) != 0) {
993     y = SHIWORD(lParam);
994     if (y < 0) {
995       if (lphl->FirstVisible > 0) {
996         lphl->FirstVisible--;
997         SetScrollPos(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
998         InvalidateRect(hwnd, NULL, TRUE);
999         return 0;
1000       }
1001     }
1002     GetClientRect(hwnd, &rect);
1003     if (y >= rect.bottom) {
1004       if (lphl->FirstVisible < ListMaxFirstVisible(lphl)) {
1005         lphl->FirstVisible++;
1006         SetScrollPos(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
1007         InvalidateRect(hwnd, NULL, TRUE);
1008         return 0;
1009       }
1010     }
1011     if ((y > 0) && (y < (rect.bottom - 4))) {
1012       if ((y < rectsel.top) || (y > rectsel.bottom)) {
1013         wRet = ListBoxFindMouse(lphl, LOWORD(lParam), HIWORD(lParam));
1014         if (wRet == lphl->ItemFocused)  {
1015           return 0;
1016         }
1017         if (dwStyle & LBS_MULTIPLESEL) {
1018           lphl->ItemFocused = wRet;
1019           ListBoxSendNotification(lphl, LBN_SELCHANGE);
1020         } else {
1021           ListBoxSetCurSel(lphl, wRet);
1022           if(dwStyle & LBS_EXTENDEDSEL)
1023              ListBoxSendNotification(lphl, LBN_SELCHANGE);
1024         }
1025         ListBoxGetItemRect(lphl, wRet, &rectsel);
1026         InvalidateRect(hwnd, &rectsel, TRUE);
1027       }
1028     }
1029   }
1030
1031   return 0;
1032 }
1033
1034 /***********************************************************************
1035  *           LBKeyDown
1036  *
1037  * Doesn't yet handle properly VK_SHIFT with LB_EXTENDEDSEL
1038  */
1039 static LONG LBKeyDown(HWND hwnd, WORD wParam, LONG lParam)
1040 {
1041   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1042   LONG       dwStyle = GetWindowLong(lphl->hSelf,GWL_STYLE);
1043   WORD       newFocused = 0xFFFF;
1044   RECT       rect;
1045
1046   ListBoxGetItemRect(lphl,lphl->ItemFocused,&rect);
1047   switch(wParam) 
1048     {
1049         /* ugly kludge that belongs in TranslateMessage */
1050
1051         case VK_HOME:
1052         case VK_END:
1053         case VK_LEFT:
1054         case VK_RIGHT:
1055         case VK_UP:
1056         case VK_DOWN:
1057         case VK_PRIOR:
1058         case VK_NEXT:
1059              if ( dwStyle & LBS_WANTKEYBOARDINPUT )
1060                 {
1061                   newFocused = (WORD)(INT)SendMessage(lphl->hParent,WM_VKEYTOITEM,
1062                                                       wParam,MAKELPARAM(lphl->ItemFocused,hwnd));
1063                   if ( newFocused == 0xFFFE ) return 0L;
1064   }
1065              if ( newFocused == 0xFFFF ) 
1066                 {
1067                   newFocused = lphl->ItemFocused;
1068
1069                   /* nested switch */
1070                   switch(wParam)
1071                     {
1072   case VK_HOME:
1073     newFocused = 0;
1074     break;
1075   case VK_END:
1076     newFocused = lphl->ItemsCount - 1;
1077     break;
1078   case VK_LEFT:
1079                           if (dwStyle & LBS_MULTICOLUMN) {
1080       if (newFocused >= lphl->ItemsPerColumn) {
1081         newFocused -= lphl->ItemsPerColumn;
1082       } else {
1083         newFocused = 0;
1084       }
1085     }
1086     break;
1087   case VK_UP:
1088     if (newFocused > 0) newFocused--;
1089     break;
1090   case VK_RIGHT:
1091                             if (dwStyle & LBS_MULTICOLUMN) 
1092       newFocused += lphl->ItemsPerColumn;
1093     break;
1094   case VK_DOWN:
1095     newFocused++;
1096     break;
1097   case VK_PRIOR:
1098                             if (newFocused > lphl->ItemsVisible)
1099       newFocused -= lphl->ItemsVisible;
1100                             else  newFocused = 0;
1101     break;
1102   case VK_NEXT:
1103     newFocused += lphl->ItemsVisible;
1104     break;
1105   default:
1106     return 0;
1107   }
1108                   /* end of nested switch */
1109                 }
1110              break;   
1111         case VK_SPACE:
1112              if (dwStyle & LBS_MULTIPLESEL)
1113                 {
1114                  WORD wRet = ListBoxGetSel(lphl, lphl->ItemFocused);
1115                  ListBoxSetSel(lphl, lphl->ItemFocused, !wRet);
1116                  }
1117              return 0;
1118
1119         /* chars are handled in LBChar */
1120         default:
1121              return 0;
1122     }
1123
1124   /* at this point newFocused is set up */
1125
1126   if (newFocused >= lphl->ItemsCount)
1127     newFocused = lphl->ItemsCount - 1;
1128   
1129   if (!(dwStyle & LBS_MULTIPLESEL)) 
1130      {
1131     ListBoxSetCurSel(lphl, newFocused);
1132         ListBoxSendNotification(lphl, LBN_SELCHANGE);
1133   }
1134
1135   lphl->ItemFocused = newFocused;
1136
1137   if( ListBoxScrollToFocus(lphl) || (dwStyle & 
1138                           (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) )
1139   InvalidateRect(hwnd, NULL, TRUE);
1140   else
1141     {
1142         InvalidateRect(hwnd, &rect, TRUE);
1143         if( newFocused < 0x8000 )
1144           {
1145            ListBoxGetItemRect(lphl, newFocused, &rect);
1146            InvalidateRect(hwnd, &rect, TRUE);
1147           }
1148     }
1149
1150   SetScrollPos(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
1151
1152   return 0;
1153 }
1154
1155 /***********************************************************************
1156  *           LBChar
1157  */
1158 static LONG LBChar(HWND hwnd, WORD wParam, LONG lParam)
1159 {
1160   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1161   LONG       dwStyle = GetWindowLong(lphl->hSelf,GWL_STYLE);
1162   WORD       newFocused = 0xFFFF;
1163
1164   if ( (dwStyle & LBS_WANTKEYBOARDINPUT) && !(lphl->HasStrings))
1165        {
1166         newFocused = (WORD)(INT)SendMessage(lphl->hParent,WM_CHARTOITEM,
1167                                             wParam,MAKELPARAM(lphl->ItemFocused,hwnd));
1168         if ( newFocused == 0xFFFE ) return 0L;
1169        }
1170
1171   if (newFocused == 0xFFFF ) 
1172   newFocused = ListBoxFindNextMatch(lphl, wParam);
1173
1174   if (newFocused == (WORD)LB_ERR) return 0;
1175
1176   if (newFocused >= lphl->ItemsCount)
1177     newFocused = lphl->ItemsCount - 1;
1178
1179   if (!(dwStyle & LBS_MULTIPLESEL)) 
1180      {
1181     ListBoxSetCurSel(lphl, newFocused);
1182         ListBoxSendNotification(lphl, LBN_SELCHANGE);
1183   }
1184
1185   lphl->ItemFocused = newFocused;
1186   ListBoxScrollToFocus(lphl);
1187   SetScrollPos(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
1188
1189   InvalidateRect(hwnd, NULL, TRUE);
1190
1191   return 0;
1192 }
1193
1194 /***********************************************************************
1195  *           LBSetRedraw
1196  */
1197 static LONG LBSetRedraw(HWND hwnd, WORD wParam, LONG lParam)
1198 {
1199   LPHEADLIST  lphl = ListBoxGetStorageHeader(hwnd);
1200
1201   dprintf_listbox(stddeb,"ListBox WM_SETREDRAW hWnd="NPFMT" w=%04X !\n",
1202                   hwnd, wParam);
1203   lphl->bRedrawFlag = wParam;
1204
1205   return 0;
1206 }
1207
1208 /***********************************************************************
1209  *           LBSetFont
1210  */
1211 static LONG LBSetFont(HWND hwnd, WPARAM wParam, LPARAM lParam)
1212 {
1213   LPHEADLIST  lphl = ListBoxGetStorageHeader(hwnd);
1214
1215   if (wParam == 0)
1216     lphl->hFont = GetStockObject(SYSTEM_FONT);
1217   else
1218     lphl->hFont = (HFONT) wParam;
1219
1220   return 0;
1221 }
1222
1223 /***********************************************************************
1224  *           LBPaint
1225  */
1226 static LONG LBPaint(HWND hwnd, WORD wParam, LONG lParam)
1227 {
1228   LPHEADLIST   lphl = ListBoxGetStorageHeader(hwnd);
1229   LONG         dwStyle = GetWindowLong(lphl->hSelf,GWL_STYLE);
1230   LPLISTSTRUCT lpls;
1231   PAINTSTRUCT  ps;
1232   HBRUSH       hBrush;
1233   HFONT        hOldFont;
1234   HDC   hdc;
1235   RECT  rect;
1236   int   i, top, height, maxwidth, ipc;
1237
1238   top = 0;
1239   hdc = BeginPaint( hwnd, &ps );
1240
1241   if (!IsWindowVisible(hwnd) || !lphl->bRedrawFlag) {
1242     EndPaint(hwnd, &ps);
1243     return 0;
1244   }
1245
1246   hOldFont = SelectObject(hdc, lphl->hFont);
1247
1248 #ifdef WINELIB32
1249   hBrush = (HBRUSH) SendMessage(lphl->hParent, WM_CTLCOLORLISTBOX, (WPARAM)hdc,
1250                                 (LPARAM)hwnd);
1251 #else
1252   hBrush = SendMessage(lphl->hParent, WM_CTLCOLOR, hdc,
1253                        MAKELONG(hwnd, CTLCOLOR_LISTBOX));
1254 #endif
1255
1256   if (hBrush == 0) hBrush = GetStockObject(WHITE_BRUSH);
1257
1258   GetClientRect(hwnd, &rect);
1259   FillRect(hdc, &rect, hBrush);
1260
1261   maxwidth = rect.right;
1262   if (dwStyle & LBS_MULTICOLUMN) {
1263     rect.right = lphl->ColumnsWidth;
1264   }
1265   lpls = lphl->lpFirst;
1266
1267   lphl->ItemsVisible = 0;
1268   lphl->ItemsPerColumn = ipc = 0;
1269
1270   for(i = 0; i < lphl->ItemsCount; i++) {
1271     if (lpls == NULL) break;
1272
1273     if (i >= lphl->FirstVisible) {
1274       height = lpls->mis.itemHeight;
1275
1276       if (top > rect.bottom) {
1277         if (dwStyle & LBS_MULTICOLUMN) {
1278           lphl->ItemsPerColumn = MAX(lphl->ItemsPerColumn, ipc);
1279           ipc = 0;
1280           top = 0;
1281           rect.left += lphl->ColumnsWidth;
1282           rect.right += lphl->ColumnsWidth;
1283           if (rect.left > maxwidth) break;
1284         } else {
1285           break;
1286         }
1287       }
1288
1289       lpls->itemRect.top    = top;
1290       lpls->itemRect.bottom = top + height;
1291       lpls->itemRect.left   = rect.left;
1292       lpls->itemRect.right  = rect.right;
1293
1294       dprintf_listbox(stddeb,"drawing item: %ld %d %ld %d %d\n",(LONG)rect.left,top,(LONG)rect.right,top+height,lpls->itemState);
1295       if (lphl->OwnerDrawn) {
1296         ListBoxDrawItem (hwnd, lphl, hdc, lpls, &lpls->itemRect, ODA_DRAWENTIRE, 0);
1297         if (lpls->itemState)
1298           ListBoxDrawItem (hwnd, lphl, hdc, lpls, &lpls->itemRect, ODA_SELECT, ODS_SELECTED);
1299       } else {
1300         ListBoxDrawItem (hwnd, lphl, hdc, lpls, &lpls->itemRect, ODA_DRAWENTIRE, 
1301                          lpls->itemState);
1302       }
1303       if ((lphl->ItemFocused == i) && GetFocus() == hwnd)
1304         ListBoxDrawItem (hwnd,lphl, hdc, lpls, &lpls->itemRect, ODA_FOCUS, ODS_FOCUS);
1305
1306       top += height;
1307       lphl->ItemsVisible++;
1308       ipc++;
1309     }
1310
1311     lpls = lpls->lpNext;
1312   }
1313   ListBoxUpdateWindow(hwnd,lphl,FALSE);
1314   SelectObject(hdc,hOldFont);
1315   EndPaint( hwnd, &ps );
1316   return 0;
1317 }
1318
1319 /***********************************************************************
1320  *           LBSetFocus
1321  */
1322 static LONG LBSetFocus(HWND hwnd, WORD wParam, LONG lParam)
1323 {
1324   dprintf_listbox(stddeb,"ListBox WM_SETFOCUS !\n");
1325
1326   return 0;
1327 }
1328
1329 /***********************************************************************
1330  *           LBKillFocus
1331  */
1332 static LONG LBKillFocus(HWND hwnd, WORD wParam, LONG lParam)
1333 {
1334   dprintf_listbox(stddeb,"ListBox WM_KILLFOCUS !\n");
1335
1336   InvalidateRect(hwnd, NULL, TRUE);
1337   
1338   return 0;
1339 }
1340
1341 /***********************************************************************
1342  *           LBResetContent
1343  */
1344 static LONG LBResetContent(HWND hwnd, WORD wParam, LONG lParam)
1345 {
1346   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1347
1348   dprintf_listbox(stddeb,"ListBox LB_RESETCONTENT !\n");
1349   ListBoxResetContent(lphl);
1350   ListBoxUpdateWindow(hwnd, lphl, TRUE);
1351   return 0;
1352 }
1353
1354 /***********************************************************************
1355  *           LBDir
1356  */
1357 static LONG LBDir(HWND hwnd, WORD wParam, LONG lParam)
1358 {
1359   WORD   wRet;
1360   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1361   dprintf_listbox(stddeb,"ListBox LB_DIR !\n");
1362
1363   wRet = ListBoxDirectory(lphl, wParam, (LPSTR)PTR_SEG_TO_LIN(lParam));
1364   ListBoxUpdateWindow(hwnd, lphl, TRUE);
1365   return wRet;
1366 }
1367
1368 /***********************************************************************
1369  *           LBAddString
1370  */
1371 static LONG LBAddString(HWND hwnd, WORD wParam, LONG lParam)
1372 {
1373   WORD  wRet;
1374   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1375
1376   if (lphl->HasStrings)
1377     wRet = ListBoxAddString(lphl, (LPSTR)PTR_SEG_TO_LIN(lParam));
1378   else
1379     wRet = ListBoxAddString(lphl, (LPSTR)lParam);
1380
1381   ListBoxUpdateWindow(hwnd,lphl,TRUE);
1382   return wRet;
1383 }
1384
1385 /***********************************************************************
1386  *           LBGetText
1387  */
1388 static LONG LBGetText(HWND hwnd, WORD wParam, LONG lParam)
1389 {
1390   LONG   wRet;
1391   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1392
1393   dprintf_listbox(stddeb, "LB_GETTEXT  wParam=%d\n",wParam);
1394   wRet = ListBoxGetText(lphl, wParam, (LPSTR)PTR_SEG_TO_LIN(lParam));
1395
1396   return wRet;
1397 }
1398
1399 /***********************************************************************
1400  *           LBInsertString
1401  */
1402 static LONG LBInsertString(HWND hwnd, WORD wParam, LONG lParam)
1403 {
1404   WORD  wRet;
1405   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1406
1407   if (lphl->HasStrings)
1408     wRet = ListBoxInsertString(lphl, wParam, (LPSTR)PTR_SEG_TO_LIN(lParam));
1409   else
1410     wRet = ListBoxInsertString(lphl, wParam, (LPSTR)lParam);
1411
1412   ListBoxUpdateWindow(hwnd,lphl,TRUE);
1413   return wRet;
1414 }
1415
1416 /***********************************************************************
1417  *           LBDeleteString
1418  */
1419 static LONG LBDeleteString(HWND hwnd, WORD wParam, LONG lParam)
1420 {
1421   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1422   LONG lRet = ListBoxDeleteString(lphl,wParam);
1423   
1424   ListBoxUpdateWindow(hwnd,lphl,TRUE);
1425   return lRet;
1426 }
1427
1428 /***********************************************************************
1429  *           LBFindString
1430  */
1431 static LONG LBFindString(HWND hwnd, WORD wParam, LONG lParam)
1432 {
1433   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1434   return ListBoxFindString(lphl, wParam, lParam);
1435 }
1436
1437 /***********************************************************************
1438  *           LBGetCaretIndex
1439  */
1440 static LONG LBGetCaretIndex(HWND hwnd, WORD wParam, LONG lParam)
1441 {
1442   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1443   return lphl->ItemFocused;
1444 }
1445
1446 /***********************************************************************
1447  *           LBGetCount
1448  */
1449 static LONG LBGetCount(HWND hwnd, WORD wParam, LONG lParam)
1450 {
1451   LPHEADLIST  lphl;
1452
1453   lphl = ListBoxGetStorageHeader(hwnd);
1454   return lphl->ItemsCount;
1455 }
1456
1457 /***********************************************************************
1458  *           LBGetCurSel
1459  */
1460 static LONG LBGetCurSel(HWND hwnd, WORD wParam, LONG lParam)
1461 {
1462   LPHEADLIST  lphl;
1463
1464   lphl = ListBoxGetStorageHeader(hwnd);
1465   dprintf_listbox(stddeb,"ListBox LB_GETCURSEL %u !\n", 
1466                   lphl->ItemFocused);
1467   return lphl->ItemFocused;
1468 }
1469
1470 /***********************************************************************
1471  *           LBGetHorizontalExtent
1472  */
1473 static LONG LBGetHorizontalExtent(HWND hwnd, WORD wParam, LONG lParam)
1474 {    
1475   return 0;
1476 }
1477
1478 /***********************************************************************
1479  *           LBGetItemHeight
1480  */
1481 static LONG LBGetItemHeight(HWND hwnd, WORD wParam, LONG lParam)
1482 {
1483   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1484   LPLISTSTRUCT lpls = ListBoxGetItem (lphl, wParam);
1485   
1486   if (lpls == NULL) return LB_ERR;
1487   return lpls->mis.itemHeight;
1488 }
1489
1490 /***********************************************************************
1491  *           LBGetItemRect
1492  */
1493 static LONG LBGetItemRect(HWND hwnd, WORD wParam, LONG lParam)
1494 {
1495   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1496   return ListBoxGetItemRect(lphl, wParam, PTR_SEG_TO_LIN(lParam));
1497 }
1498
1499 /***********************************************************************
1500  *           LBGetSel
1501  */
1502 static LONG LBGetSel(HWND hwnd, WORD wParam, LONG lParam)
1503 {
1504   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1505   return (ListBoxGetSel(lphl, wParam) )? 1 : 0;
1506 }
1507
1508 /***********************************************************************
1509  *           LBGetSelCount
1510  */
1511 static LONG LBGetSelCount(HWND hwnd, WORD wParam, LONG lParam)
1512 {
1513   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1514   LPLISTSTRUCT lpls;
1515   int          cnt = 0;
1516   int          items = 0;
1517
1518   if (!(GetWindowLong(lphl->hSelf,GWL_STYLE) & 
1519        (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)  )) 
1520          return LB_ERR;
1521
1522   for( lpls = lphl->lpFirst;
1523        lpls;
1524        lpls = lpls->lpNext )
1525         {
1526            items++;
1527            if (lpls->itemState ) 
1528                cnt++;
1529   }
1530
1531   return cnt;
1532 }
1533
1534 /***********************************************************************
1535  *           LBGetSelItems
1536  */
1537 static LONG LBGetSelItems(HWND hwnd, WORD wParam, LONG lParam)
1538 {
1539   LPHEADLIST  lphl = ListBoxGetStorageHeader(hwnd);
1540   LPLISTSTRUCT lpls;
1541   int cnt, idx;
1542   int *lpItems = PTR_SEG_TO_LIN(lParam);
1543
1544   if (!(GetWindowLong(lphl->hSelf,GWL_STYLE) & 
1545        (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)  )) 
1546         return LB_ERR;
1547
1548   if (wParam == 0) return 0;
1549
1550   lpls = lphl->lpFirst;
1551   cnt = 0; idx = 0;
1552
1553   while (lpls != NULL) {
1554     if (lpls->itemState > 0) lpItems[cnt++] = idx;
1555
1556     if (cnt == wParam) break;
1557     idx++;
1558     lpls = lpls->lpNext;
1559   }
1560
1561   return cnt;
1562 }
1563
1564 /***********************************************************************
1565  *           LBGetTextLen
1566  */
1567 static LONG LBGetTextLen(HWND hwnd, WORD wParam, LONG lParam)
1568 {
1569   LPHEADLIST   lphl = ListBoxGetStorageHeader(hwnd);
1570   LPLISTSTRUCT lpls = ListBoxGetItem(lphl,wParam);
1571
1572   if (lpls == NULL || !lphl->HasStrings) return LB_ERR;
1573   return strlen(lpls->itemText);
1574 }
1575
1576 /***********************************************************************
1577  *           LBGetDlgCode
1578  */
1579 static LONG LBGetDlgCode(HWND hwnd, WORD wParam, LONG lParam)
1580 {
1581   return DLGC_WANTARROWS | DLGC_WANTCHARS;
1582 }
1583
1584 /***********************************************************************
1585  *           LBGetTopIndex
1586  */
1587 static LONG LBGetTopIndex(HWND hwnd, WORD wParam, LONG lParam)
1588 {
1589   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1590
1591   return lphl->FirstVisible;
1592 }
1593
1594
1595 /***********************************************************************
1596  *           LBSelectString
1597  */
1598 static LONG LBSelectString(HWND hwnd, WORD wParam, LONG lParam)
1599 {
1600   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1601   WORD  wRet;
1602
1603   wRet = ListBoxFindString(lphl, wParam, lParam);
1604
1605   /* XXX add functionality here */
1606
1607   return 0;
1608 }
1609
1610 /***********************************************************************
1611  *           LBSelItemRange
1612  */
1613 static LONG LBSelItemRange(HWND hwnd, WORD wParam, LONG lParam)
1614 {
1615   LPHEADLIST   lphl = ListBoxGetStorageHeader(hwnd);
1616   LPLISTSTRUCT lpls;
1617   WORD         cnt;
1618   WORD         first = LOWORD(lParam);
1619   WORD         last = HIWORD(lParam);
1620   BOOL         select = wParam;
1621
1622   if (!(GetWindowLong(lphl->hSelf,GWL_STYLE) & 
1623        (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)  )) 
1624         return LB_ERR;
1625
1626   if (first >= lphl->ItemsCount ||
1627       last >= lphl->ItemsCount) return LB_ERR;
1628
1629   lpls = lphl->lpFirst;
1630   cnt = 0;
1631
1632   while (lpls != NULL) {
1633     if (cnt++ >= first)
1634       lpls->itemState = select ? ODS_SELECTED : 0;
1635
1636     if (cnt > last)
1637       break;
1638
1639     lpls = lpls->lpNext;
1640   }
1641
1642   return 0;
1643 }
1644
1645 /***********************************************************************
1646  *           LBSetCaretIndex
1647  */
1648 static LONG LBSetCaretIndex(HWND hwnd, WORD wParam, LONG lParam)
1649 {
1650   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1651
1652   if (!(GetWindowLong(lphl->hSelf,GWL_STYLE) & LBS_MULTIPLESEL)) return 0;
1653   if (wParam >= lphl->ItemsCount) return LB_ERR;
1654
1655   lphl->ItemFocused = wParam;
1656   ListBoxScrollToFocus (lphl);
1657
1658   SetScrollPos(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
1659   InvalidateRect(hwnd, NULL, TRUE);
1660   return 0;
1661 }
1662
1663 /***********************************************************************
1664  *           LBSetColumnWidth
1665  */
1666 static LONG LBSetColumnWidth(HWND hwnd, WORD wParam, LONG lParam)
1667 {
1668   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1669   lphl->ColumnsWidth = wParam;
1670   InvalidateRect(hwnd,NULL,TRUE);
1671   return 0;
1672 }
1673
1674 /***********************************************************************
1675  *           LBSetHorizontalExtent
1676  */
1677 static LONG LBSetHorizontalExtent(HWND hwnd, WORD wParam, LONG lParam)
1678 {
1679   return 0;
1680 }
1681
1682 /***********************************************************************
1683  *           LBGetItemData
1684  */
1685 static LONG LBGetItemData(HWND hwnd, WORD wParam, LONG lParam)
1686 {
1687   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1688   dprintf_listbox(stddeb, "LB_GETITEMDATA wParam=%x\n", wParam);
1689   return ListBoxGetItemData(lphl, wParam);
1690 }
1691
1692 /***********************************************************************
1693  *           LBSetItemData
1694  */
1695 static LONG LBSetItemData(HWND hwnd, WORD wParam, LONG lParam)
1696 {
1697   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1698   dprintf_listbox(stddeb, "LB_SETITEMDATA  wParam=%x  lParam=%lx\n", wParam, lParam);
1699   return ListBoxSetItemData(lphl, wParam, lParam);
1700 }
1701
1702 /***********************************************************************
1703  *           LBSetTabStops
1704  */
1705 static LONG LBSetTabStops(HWND hwnd, WORD wParam, LONG lParam)
1706 {
1707   LPHEADLIST  lphl;
1708
1709   lphl = ListBoxGetStorageHeader(hwnd);
1710
1711   if (lphl->TabStops != NULL) {
1712     lphl->iNumStops = 0;
1713     free (lphl->TabStops);
1714   }
1715
1716   lphl->TabStops = malloc (wParam * sizeof (short));
1717   if (lphl->TabStops) {
1718     lphl->iNumStops = wParam;
1719     memcpy (lphl->TabStops, PTR_SEG_TO_LIN(lParam), wParam * sizeof (short));
1720     return TRUE;
1721   }
1722
1723   return FALSE;
1724 }
1725
1726 /***********************************************************************
1727  *           LBSetCurSel
1728  */
1729 static LONG LBSetCurSel(HWND hwnd, WORD wParam, LONG lParam)
1730 {
1731   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1732   WORD  wRet;
1733
1734   dprintf_listbox(stddeb,"ListBox LB_SETCURSEL wParam=%x !\n", 
1735                   wParam);
1736
1737   wRet = ListBoxSetCurSel(lphl, wParam);
1738
1739   SetScrollPos(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
1740   InvalidateRect(hwnd, NULL, TRUE);
1741
1742   return wRet;
1743 }
1744
1745 /***********************************************************************
1746  *           LBSetSel
1747  */
1748 static LONG LBSetSel(HWND hwnd, WORD wParam, LONG lParam)
1749 {
1750   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1751   WORD wRet;
1752
1753   dprintf_listbox(stddeb,"ListBox LB_SETSEL wParam=%x lParam=%lX !\n", wParam, lParam);
1754
1755   wRet = ListBoxSetSel(lphl, LOWORD(lParam), wParam);
1756   InvalidateRect(hwnd, NULL, TRUE);
1757
1758   return wRet;
1759 }
1760
1761 /***********************************************************************
1762  *           LBSetTopIndex
1763  */
1764 static LONG LBSetTopIndex(HWND hwnd, WORD wParam, LONG lParam)
1765 {
1766   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1767
1768   dprintf_listbox(stddeb,"ListBox LB_SETTOPINDEX wParam=%x !\n",
1769                   wParam);
1770   lphl->FirstVisible = wParam;
1771   SetScrollPos(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
1772
1773   InvalidateRect(hwnd, NULL, TRUE);
1774
1775   return 0;
1776 }
1777
1778 /***********************************************************************
1779  *           LBSetItemHeight
1780  */
1781 static LONG LBSetItemHeight(HWND hwnd, WORD wParam, LONG lParam)
1782 {
1783   LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1784   WORD wRet;
1785   
1786   dprintf_listbox(stddeb,"ListBox LB_SETITEMHEIGHT wParam=%x lParam=%lX !\n", wParam, lParam);
1787   wRet = ListBoxSetItemHeight(lphl, wParam, lParam);
1788   InvalidateRect(hwnd,NULL,TRUE);
1789   return wRet;
1790 }
1791
1792 /***********************************************************************
1793  *           LBPassToParent
1794  */
1795 static LRESULT LBPassToParent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
1796 {
1797   WND* ptrWnd = WIN_FindWndPtr(hwnd);  
1798
1799   if( ptrWnd )
1800         if( /* !(ptrWnd->dwExStyle & WS_EX_NOPARENTNOTIFY) && */ 
1801                  ptrWnd->hwndParent ) 
1802                  return SendMessage(ptrWnd->hwndParent,message,wParam,lParam);
1803   return 0;
1804 }
1805
1806 /***********************************************************************
1807  *           ListBoxWndProc 
1808  */
1809 LRESULT ListBoxWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
1810
1811     switch (message) {
1812      case WM_CREATE: return LBCreate(hwnd, wParam, lParam);
1813      case WM_DESTROY: return LBDestroy(hwnd, wParam, lParam);
1814      case WM_GETDLGCODE: return LBGetDlgCode(hwnd, wParam, lParam);
1815      case WM_VSCROLL: return LBVScroll(hwnd, wParam, lParam);
1816      case WM_HSCROLL: return LBHScroll(hwnd, wParam, lParam);
1817      case WM_LBUTTONDOWN: return LBLButtonDown(hwnd, wParam, lParam);
1818      case WM_LBUTTONUP: return LBLButtonUp(hwnd, wParam, lParam);
1819      case WM_RBUTTONUP: return LBRButtonUp(hwnd, wParam, lParam);
1820      case WM_LBUTTONDBLCLK: return LBRButtonUp(hwnd, wParam, lParam);
1821      case WM_MOUSEMOVE: return LBMouseMove(hwnd, wParam, lParam);
1822      case WM_KEYDOWN: return LBKeyDown(hwnd, wParam, lParam);
1823      case WM_CHAR: return LBChar(hwnd, wParam, lParam);
1824      case WM_SETFONT: return LBSetFont(hwnd, wParam, lParam);
1825      case WM_SETREDRAW: return LBSetRedraw(hwnd, wParam, lParam);
1826      case WM_PAINT: return LBPaint(hwnd, wParam, lParam);
1827      case WM_SETFOCUS: return LBSetFocus(hwnd, wParam, lParam);
1828      case WM_KILLFOCUS: return LBKillFocus(hwnd, wParam, lParam);
1829      case LB_RESETCONTENT: return LBResetContent(hwnd, wParam, lParam);
1830      case LB_DIR: return LBDir(hwnd, wParam, lParam);
1831      case LB_ADDSTRING: return LBAddString(hwnd, wParam, lParam);
1832      case LB_INSERTSTRING: return LBInsertString(hwnd, wParam, lParam);
1833      case LB_DELETESTRING: return LBDeleteString(hwnd, wParam, lParam);
1834      case LB_FINDSTRING: return LBFindString(hwnd, wParam, lParam);
1835      case LB_GETCARETINDEX: return LBGetCaretIndex(hwnd, wParam, lParam);
1836      case LB_GETCOUNT: return LBGetCount(hwnd, wParam, lParam);
1837      case LB_GETCURSEL: return LBGetCurSel(hwnd, wParam, lParam);
1838      case LB_GETHORIZONTALEXTENT: return LBGetHorizontalExtent(hwnd, wParam, lParam);
1839      case LB_GETITEMDATA: return LBGetItemData(hwnd, wParam, lParam);
1840      case LB_GETITEMHEIGHT: return LBGetItemHeight(hwnd, wParam, lParam);
1841      case LB_GETITEMRECT: return LBGetItemRect(hwnd, wParam, lParam);
1842      case LB_GETSEL: return LBGetSel(hwnd, wParam, lParam);
1843      case LB_GETSELCOUNT: return LBGetSelCount(hwnd, wParam, lParam);
1844      case LB_GETSELITEMS: return LBGetSelItems(hwnd, wParam, lParam);
1845      case LB_GETTEXT: return LBGetText(hwnd, wParam, lParam);
1846      case LB_GETTEXTLEN: return LBGetTextLen(hwnd, wParam, lParam);
1847      case LB_GETTOPINDEX: return LBGetTopIndex(hwnd, wParam, lParam);
1848      case LB_SELECTSTRING: return LBSelectString(hwnd, wParam, lParam);
1849      case LB_SELITEMRANGE: return LBSelItemRange(hwnd, wParam, lParam);
1850      case LB_SETCARETINDEX: return LBSetCaretIndex(hwnd, wParam, lParam);
1851      case LB_SETCOLUMNWIDTH: return LBSetColumnWidth(hwnd, wParam, lParam);
1852      case LB_SETHORIZONTALEXTENT: return LBSetHorizontalExtent(hwnd, wParam, lParam);
1853      case LB_SETITEMDATA: return LBSetItemData(hwnd, wParam, lParam);
1854      case LB_SETTABSTOPS: return LBSetTabStops(hwnd, wParam, lParam);
1855      case LB_SETCURSEL: return LBSetCurSel(hwnd, wParam, lParam);
1856      case LB_SETSEL: return LBSetSel(hwnd, wParam, lParam);
1857      case LB_SETTOPINDEX: return LBSetTopIndex(hwnd, wParam, lParam);
1858      case LB_SETITEMHEIGHT: return LBSetItemHeight(hwnd, wParam, lParam);
1859
1860      case WM_DROPFILES: return LBPassToParent(hwnd, message, wParam, lParam);
1861
1862         case WM_DROPOBJECT:
1863         case WM_QUERYDROPOBJECT:
1864         case WM_DRAGSELECT:
1865         case WM_DRAGMOVE:
1866                 {
1867                  LPDRAGINFO lpDragInfo = (LPDRAGINFO) PTR_SEG_TO_LIN((SEGPTR)lParam);
1868                  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1869
1870                  /* more undocumented Microsoft crap - drag&drop depends on it - AK */
1871
1872                  lpDragInfo->l = ListBoxFindMouse(lphl,lpDragInfo->pt.x,
1873                                                        lpDragInfo->pt.y);
1874                 
1875                  return LBPassToParent(hwnd, message, wParam, lParam);
1876                 }
1877     }
1878     
1879     return DefWindowProc(hwnd, message, wParam, lParam);
1880 }
1881
1882 /************************************************************************
1883  *                      DlgDirSelect                    [USER.99]
1884  */
1885 BOOL DlgDirSelect(HWND hDlg, LPSTR lpStr, int nIDLBox)
1886 {
1887   HWND hwnd;
1888   LPHEADLIST lphl;
1889   char s[130];
1890
1891   dprintf_listbox( stddeb, "DlgDirSelect("NPFMT", '%s', %d) \n", hDlg, lpStr, 
1892                   nIDLBox );
1893
1894   hwnd = GetDlgItem(hDlg, nIDLBox);
1895   lphl = ListBoxGetStorageHeader(hwnd);
1896   if(lphl->ItemFocused == -1) {
1897     dprintf_listbox(stddeb, "Nothing selected!\n");
1898     return FALSE;
1899   }
1900   ListBoxGetText(lphl, lphl->ItemFocused, s);
1901   dprintf_listbox(stddeb, "Selection is %s\n", s);
1902   if( s[0] == '[' ) {
1903     if( s[1] == '-' ) {
1904       strncpy( lpStr, s+2, strlen(s)-4 );    /* device name */
1905       lpStr[ strlen(s)-4 ] = 0;
1906       strcat( lpStr, ":" );
1907     }
1908     else {
1909       strncpy( lpStr, s+1, strlen(s)-2 );    /* directory name */
1910       lpStr[ strlen(s)-2 ] = 0;
1911       strcat( lpStr, "\\" );
1912     }
1913     dprintf_listbox( stddeb, "Returning %s\n", lpStr );
1914     return TRUE;
1915   } else {
1916     strcpy( lpStr, s );                     /* file name */
1917     dprintf_listbox( stddeb, "Returning %s\n", lpStr );
1918     return FALSE;
1919   }
1920 }
1921
1922
1923 /************************************************************************
1924  *                         DlgDirList                   [USER.100]
1925  */
1926 int DlgDirList(HWND hDlg, LPSTR lpPathSpec, 
1927                int nIDLBox, int nIDStat, WORD wType)
1928 {
1929   HWND  hWnd;
1930   int ret;
1931   
1932   dprintf_listbox(stddeb,"DlgDirList("NPFMT", '%s', %d, %d, %04X) \n",
1933                   hDlg, lpPathSpec, nIDLBox, nIDStat, wType);
1934   if (nIDLBox)  {
1935     LPHEADLIST lphl;
1936     hWnd = GetDlgItem(hDlg, nIDLBox);
1937     lphl = ListBoxGetStorageHeader(hWnd);
1938     ListBoxResetContent(lphl);
1939     ret = ListBoxDirectory(lphl, wType, lpPathSpec);
1940     ListBoxUpdateWindow(hWnd, lphl, TRUE);
1941   } else {
1942     ret = 0;
1943   }
1944   if (nIDStat) {
1945       int drive;
1946       HANDLE hTemp;
1947       char *temp;
1948       drive = DOS_GetDefaultDrive();
1949       hTemp = USER_HEAP_ALLOC( 256 );
1950       temp = (char *) USER_HEAP_LIN_ADDR( hTemp );
1951       strcpy( temp+3, DOS_GetCurrentDir(drive) );
1952       if( temp[3] == '\\' ) {
1953         temp[1] = 'A'+drive;
1954         temp[2] = ':';
1955         SendDlgItemMessage( hDlg, nIDStat, WM_SETTEXT, 0,
1956                             (LPARAM)(USER_HEAP_SEG_ADDR(hTemp) + 1) );
1957       } else {
1958         temp[0] = 'A'+drive;
1959         temp[1] = ':';
1960         temp[2] = '\\';
1961         SendDlgItemMessage( hDlg, nIDStat, WM_SETTEXT, 0,
1962                             (LPARAM)USER_HEAP_SEG_ADDR(hTemp) );
1963       }
1964       USER_HEAP_FREE( hTemp );
1965   } 
1966   return ret;
1967 }