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