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