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