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