Modify the look of bitmap menu items to match Win95 and Win98
[wine] / controls / listbox.c
1 /*
2  * Listbox controls
3  *
4  * Copyright 1996 Alexandre Julliard
5  */
6
7 #include <string.h>
8 #include <stdlib.h>
9 #include <stdio.h>
10 #include "windef.h"
11 #include "wingdi.h"
12 #include "wine/winuser16.h"
13 #include "wine/winbase16.h"
14 #include "winuser.h"
15 #include "winerror.h"
16 #include "heap.h"
17 #include "spy.h"
18 #include "win.h"
19 #include "controls.h"
20 #include "debugtools.h"
21 #include "tweak.h"
22
23 DEFAULT_DEBUG_CHANNEL(listbox);
24 DECLARE_DEBUG_CHANNEL(combo);
25
26 /* Unimplemented yet:
27  * - LBS_USETABSTOPS
28  * - Unicode
29  * - Locale handling
30  *
31  * Probably needs improvement:
32  * - LBS_NOSEL
33  */
34
35 /* Items array granularity */
36 #define LB_ARRAY_GRANULARITY 16
37
38 /* Scrolling timeout in ms */
39 #define LB_SCROLL_TIMEOUT 50
40
41 /* Listbox system timer id */
42 #define LB_TIMER_ID  2
43
44 /* flag listbox changed while setredraw false - internal style */
45 #define LBS_DISPLAYCHANGED 0x80000000 
46
47 /* Item structure */
48 typedef struct
49 {
50     LPSTR     str;       /* Item text */
51     BOOL    selected;  /* Is item selected? */
52     UINT    height;    /* Item height (only for OWNERDRAWVARIABLE) */
53     DWORD     data;      /* User data */
54 } LB_ITEMDATA;
55
56 /* Listbox structure */
57 typedef struct
58 {
59     HANDLE      heap;           /* Heap for this listbox */
60     HWND        owner;          /* Owner window to send notifications to */
61     UINT        style;          /* Window style */
62     INT         width;          /* Window width */
63     INT         height;         /* Window height */
64     LB_ITEMDATA  *items;          /* Array of items */
65     INT         nb_items;       /* Number of items */
66     INT         top_item;       /* Top visible item */
67     INT         selected_item;  /* Selected item */
68     INT         focus_item;     /* Item that has the focus */
69     INT         anchor_item;    /* Anchor item for extended selection */
70     INT         item_height;    /* Default item height */
71     INT         page_size;      /* Items per listbox page */
72     INT         column_width;   /* Column width for multi-column listboxes */
73     INT         horz_extent;    /* Horizontal extent (0 if no hscroll) */
74     INT         horz_pos;       /* Horizontal position */
75     INT         nb_tabs;        /* Number of tabs in array */
76     INT        *tabs;           /* Array of tabs */
77     BOOL        caret_on;       /* Is caret on? */
78     BOOL        captured;       /* Is mouse captured? */
79     BOOL        in_focus;
80     HFONT       font;           /* Current font */
81     LCID          locale;         /* Current locale for string comparisons */
82     LPHEADCOMBO   lphc;           /* ComboLBox */
83 } LB_DESCR;
84
85
86 #define IS_OWNERDRAW(descr) \
87     ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
88
89 #define HAS_STRINGS(descr) \
90     (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
91
92
93 #define IS_MULTISELECT(descr) \
94     ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
95
96 #define SEND_NOTIFICATION(wnd,descr,code) \
97     (SendMessageA( (descr)->owner, WM_COMMAND, \
98      MAKEWPARAM((wnd)->wIDmenu, (code)), (wnd)->hwndSelf ))
99
100 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
101
102 /* Current timer status */
103 typedef enum
104 {
105     LB_TIMER_NONE,
106     LB_TIMER_UP,
107     LB_TIMER_LEFT,
108     LB_TIMER_DOWN,
109     LB_TIMER_RIGHT
110 } TIMER_DIRECTION;
111
112 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
113
114 static LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
115 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
116
117 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
118                                     RECT *rect );
119
120 /*********************************************************************
121  * listbox class descriptor
122  */
123 const struct builtin_class_descr LISTBOX_builtin_class =
124 {
125     "ListBox",            /* name */
126     CS_GLOBALCLASS | CS_DBLCLKS /*| CS_PARENTDC*/,  /* style */
127     ListBoxWndProcA,      /* procA */
128     NULL,                 /* procW (FIXME) */
129     sizeof(LB_DESCR *),   /* extra */
130     IDC_ARROWA,           /* cursor */
131     0                     /* brush */
132 };
133
134
135 /*********************************************************************
136  * combolbox class descriptor
137  */
138 const struct builtin_class_descr COMBOLBOX_builtin_class =
139 {
140     "ComboLBox",          /* name */
141     CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS,  /* style */
142     ComboLBWndProcA,      /* procA */
143     NULL,                 /* procW (FIXME) */
144     sizeof(LB_DESCR *),   /* extra */
145     IDC_ARROWA,           /* cursor */
146     0                     /* brush */
147 };
148
149
150 /***********************************************************************
151  *           LISTBOX_Dump
152  */
153 void LISTBOX_Dump( WND *wnd )
154 {
155     INT i;
156     LB_ITEMDATA *item;
157     LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
158
159     TRACE( "Listbox:\n" );
160     TRACE( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
161                      wnd->hwndSelf, (UINT)descr, descr->heap, descr->nb_items,
162                      descr->top_item );
163     for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
164     {
165         TRACE( "%4d: %-40s %d %08lx %3d\n",
166                          i, item->str, item->selected, item->data, item->height );
167     }
168 }
169
170
171 /***********************************************************************
172  *           LISTBOX_GetCurrentPageSize
173  *
174  * Return the current page size
175  */
176 static INT LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
177 {
178     INT i, height;
179     if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
180     for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
181     {
182         if ((height += descr->items[i].height) > descr->height) break;
183     }
184     if (i == descr->top_item) return 1;
185     else return i - descr->top_item;
186 }
187
188
189 /***********************************************************************
190  *           LISTBOX_GetMaxTopIndex
191  *
192  * Return the maximum possible index for the top of the listbox.
193  */
194 static INT LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
195 {
196     INT max, page;
197
198     if (descr->style & LBS_OWNERDRAWVARIABLE)
199     {
200         page = descr->height;
201         for (max = descr->nb_items - 1; max >= 0; max--)
202             if ((page -= descr->items[max].height) < 0) break;
203         if (max < descr->nb_items - 1) max++;
204     }
205     else if (descr->style & LBS_MULTICOLUMN)
206     {
207         if ((page = descr->width / descr->column_width) < 1) page = 1;
208         max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
209         max = (max - page) * descr->page_size;
210     }
211     else
212     {
213         max = descr->nb_items - descr->page_size;
214     }
215     if (max < 0) max = 0;
216     return max;
217 }
218
219
220 /***********************************************************************
221  *           LISTBOX_UpdateScroll
222  *
223  * Update the scrollbars. Should be called whenever the content
224  * of the listbox changes.
225  */
226 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
227 {
228     SCROLLINFO info;
229
230     /* Check the listbox scroll bar flags individually before we call
231        SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
232        no WS_VSCROLL, we end up with an uninitialized, visible horizontal
233        scroll bar when we do not need one.
234     if (!(descr->style & WS_VSCROLL)) return;
235     */  
236
237     /*   It is important that we check descr->style, and not wnd->dwStyle, 
238        for WS_VSCROLL, as the former is exactly the one passed in 
239        argument to CreateWindow.  
240          In Windows (and from now on in Wine :) a listbox created 
241        with such a style (no WS_SCROLL) does not update 
242        the scrollbar with listbox-related data, thus letting 
243        the programmer use it for his/her own purposes. */
244
245     if (descr->style & LBS_NOREDRAW) return;
246     info.cbSize = sizeof(info);
247
248     if (descr->style & LBS_MULTICOLUMN)
249     {
250         info.nMin  = 0;
251         info.nMax  = (descr->nb_items - 1) / descr->page_size;
252         info.nPos  = descr->top_item / descr->page_size;
253         info.nPage = descr->width / descr->column_width;
254         if (info.nPage < 1) info.nPage = 1;
255         info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
256         if (descr->style & LBS_DISABLENOSCROLL)
257             info.fMask |= SIF_DISABLENOSCROLL;
258         if (descr->style & WS_HSCROLL)
259             SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
260         info.nMax = 0;
261         info.fMask = SIF_RANGE;
262         if (descr->style & WS_VSCROLL)
263             SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
264     }
265     else
266     {
267         info.nMin  = 0;
268         info.nMax  = descr->nb_items - 1;
269         info.nPos  = descr->top_item;
270         info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
271         info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
272         if (descr->style & LBS_DISABLENOSCROLL)
273             info.fMask |= SIF_DISABLENOSCROLL;
274         if (descr->style & WS_VSCROLL)
275             SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
276
277         if (descr->horz_extent)
278         {
279             info.nMin  = 0;
280             info.nMax  = descr->horz_extent - 1;
281             info.nPos  = descr->horz_pos;
282             info.nPage = descr->width;
283             info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
284             if (descr->style & LBS_DISABLENOSCROLL)
285                 info.fMask |= SIF_DISABLENOSCROLL;
286             if (descr->style & WS_HSCROLL)
287                 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
288             
289         }
290     }
291 }
292
293
294 /***********************************************************************
295  *           LISTBOX_SetTopItem
296  *
297  * Set the top item of the listbox, scrolling up or down if necessary.
298  */
299 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT index,
300                                    BOOL scroll )
301 {
302     INT max = LISTBOX_GetMaxTopIndex( wnd, descr );
303     if (index > max) index = max;
304     if (index < 0) index = 0;
305     if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
306     if (descr->top_item == index) return LB_OKAY;
307     if (descr->style & LBS_MULTICOLUMN)
308     {
309         INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
310         if (scroll && (abs(diff) < descr->width))
311             ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL, 
312                               SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
313
314         else
315             scroll = FALSE;
316     }
317     else if (scroll)
318     {
319         INT diff;
320         if (descr->style & LBS_OWNERDRAWVARIABLE)
321         {
322             INT i;
323             diff = 0;
324             if (index > descr->top_item)
325             {
326                 for (i = index - 1; i >= descr->top_item; i--)
327                     diff -= descr->items[i].height;
328             }
329             else
330             {
331                 for (i = index; i < descr->top_item; i++)
332                     diff += descr->items[i].height;
333             }
334         }
335         else 
336             diff = (descr->top_item - index) * descr->item_height;
337
338         if (abs(diff) < descr->height)
339             ScrollWindowEx( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
340                               SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
341         else
342             scroll = FALSE;
343     }
344     if (!scroll) InvalidateRect( wnd->hwndSelf, NULL, TRUE );
345     descr->top_item = index;
346     LISTBOX_UpdateScroll( wnd, descr );
347     return LB_OKAY;
348 }
349
350
351 /***********************************************************************
352  *           LISTBOX_UpdatePage
353  *
354  * Update the page size. Should be called when the size of
355  * the client area or the item height changes.
356  */
357 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
358 {
359     INT page_size;
360
361     if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1) 
362                        page_size = 1;
363     if (page_size == descr->page_size) return;
364     descr->page_size = page_size;
365     if (descr->style & LBS_MULTICOLUMN)
366         InvalidateRect( wnd->hwndSelf, NULL, TRUE );
367     LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
368 }
369
370
371 /***********************************************************************
372  *           LISTBOX_UpdateSize
373  *
374  * Update the size of the listbox. Should be called when the size of
375  * the client area changes.
376  */
377 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
378 {
379     RECT rect;
380
381     GetClientRect( wnd->hwndSelf, &rect );
382     descr->width  = rect.right - rect.left;
383     descr->height = rect.bottom - rect.top;
384     if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
385     {
386         UINT remaining;
387
388         if(descr->item_height != 0)
389             remaining = descr->height % descr->item_height;
390         else
391             remaining = 0;
392         if ((descr->height > descr->item_height) && remaining)
393         {
394             if (!(wnd->flags & WIN_ISWIN32))
395             { /* give a margin for error to 16 bits programs - if we need
396                  less than the height of the nonclient area, round to the
397                  *next* number of items */ 
398                 int ncheight = wnd->rectWindow.bottom - wnd->rectWindow.top - descr->height;
399                 if ((descr->item_height - remaining) <= ncheight)
400                     remaining = remaining - descr->item_height;
401             }
402             TRACE("[%04x]: changing height %d -> %d\n",
403                          wnd->hwndSelf, descr->height,
404                          descr->height - remaining );
405             SetWindowPos( wnd->hwndSelf, 0, 0, 0,
406                             wnd->rectWindow.right - wnd->rectWindow.left,
407                             wnd->rectWindow.bottom - wnd->rectWindow.top - remaining,
408                             SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
409             return;
410         }
411     }
412     TRACE("[%04x]: new size = %d,%d\n",
413                  wnd->hwndSelf, descr->width, descr->height );
414     LISTBOX_UpdatePage( wnd, descr );
415     LISTBOX_UpdateScroll( wnd, descr );
416
417     /* Invalidate the focused item so it will be repainted correctly */
418     if (1==LISTBOX_GetItemRect( wnd, descr, descr->focus_item, &rect ))
419     {
420         InvalidateRect( wnd->hwndSelf, &rect, FALSE );
421     }
422 }
423
424
425 /***********************************************************************
426  *           LISTBOX_GetItemRect
427  *
428  * Get the rectangle enclosing an item, in listbox client coordinates.
429  * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
430  */
431 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
432                                     RECT *rect )
433 {
434     /* Index <= 0 is legal even on empty listboxes */
435     if (index && (index >= descr->nb_items)) return -1;
436     SetRect( rect, 0, 0, descr->width, descr->height );
437     if (descr->style & LBS_MULTICOLUMN)
438     {
439         INT col = (index / descr->page_size) -
440                         (descr->top_item / descr->page_size);
441         rect->left += col * descr->column_width;
442         rect->right = rect->left + descr->column_width;
443         rect->top += (index % descr->page_size) * descr->item_height;
444         rect->bottom = rect->top + descr->item_height;
445     }
446     else if (descr->style & LBS_OWNERDRAWVARIABLE)
447     {
448         INT i;
449         rect->right += descr->horz_pos;
450         if ((index >= 0) && (index < descr->nb_items))
451         {
452             if (index < descr->top_item)
453             {
454                 for (i = descr->top_item-1; i >= index; i--)
455                     rect->top -= descr->items[i].height;
456             }
457             else
458             {
459                 for (i = descr->top_item; i < index; i++)
460                     rect->top += descr->items[i].height;
461             }
462             rect->bottom = rect->top + descr->items[index].height;
463
464         }
465     }
466     else
467     {
468         rect->top += (index - descr->top_item) * descr->item_height;
469         rect->bottom = rect->top + descr->item_height;
470         rect->right += descr->horz_pos;
471     }
472
473     return ((rect->left < descr->width) && (rect->right > 0) &&
474             (rect->top < descr->height) && (rect->bottom > 0));
475 }
476
477
478 /***********************************************************************
479  *           LISTBOX_GetItemFromPoint
480  *
481  * Return the item nearest from point (x,y) (in client coordinates).
482  */
483 static INT LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
484                                        INT x, INT y )
485 {
486     INT index = descr->top_item;
487
488     if (!descr->nb_items) return -1;  /* No items */
489     if (descr->style & LBS_OWNERDRAWVARIABLE)
490     {
491         INT pos = 0;
492         if (y >= 0)
493         {
494             while (index < descr->nb_items)
495             {
496                 if ((pos += descr->items[index].height) > y) break;
497                 index++;
498             }
499         }
500         else
501         {
502             while (index > 0)
503             {
504                 index--;
505                 if ((pos -= descr->items[index].height) <= y) break;
506             }
507         }
508     }
509     else if (descr->style & LBS_MULTICOLUMN)
510     {
511         if (y >= descr->item_height * descr->page_size) return -1;
512         if (y >= 0) index += y / descr->item_height;
513         if (x >= 0) index += (x / descr->column_width) * descr->page_size;
514         else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
515     }
516     else
517     {
518         index += (y / descr->item_height);
519     }
520     if (index < 0) return 0;
521     if (index >= descr->nb_items) return -1;
522     return index;
523 }
524
525
526 /***********************************************************************
527  *           LISTBOX_PaintItem
528  *
529  * Paint an item.
530  */
531 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
532                                const RECT *rect, INT index, UINT action, BOOL ignoreFocus )
533 {
534     LB_ITEMDATA *item = NULL;
535     if (index < descr->nb_items) item = &descr->items[index];
536
537     if (IS_OWNERDRAW(descr))
538     {
539         DRAWITEMSTRUCT dis;
540         RECT r;
541         HRGN hrgn;
542
543         if (!item)
544         {
545             if (action == ODA_FOCUS) 
546                 DrawFocusRect( hdc, rect );
547             else
548                 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
549             return;
550         }
551
552         /* some programs mess with the clipping region when
553         drawing the item, *and* restore the previous region
554         after they are done, so a region has better to exist
555         else everything ends clipped */
556         GetClientRect(wnd->hwndSelf, &r);
557         hrgn = CreateRectRgnIndirect(&r);
558         SelectClipRgn( hdc, hrgn);
559         DeleteObject( hrgn );
560
561         dis.CtlType      = ODT_LISTBOX;
562         dis.CtlID        = wnd->wIDmenu;
563         dis.hwndItem     = wnd->hwndSelf;
564         dis.itemAction   = action;
565         dis.hDC          = hdc;
566         dis.itemID       = index;
567         dis.itemState    = 0;
568         if (item && item->selected) dis.itemState |= ODS_SELECTED;
569         if (!ignoreFocus && (descr->focus_item == index) &&
570             (descr->caret_on) &&
571             (descr->in_focus)) dis.itemState |= ODS_FOCUS;
572         if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
573         dis.itemData     = item ? item->data : 0;
574         dis.rcItem       = *rect;
575         TRACE("[%04x]: drawitem %d (%s) action=%02x "
576                      "state=%02x rect=%d,%d-%d,%d\n",
577                      wnd->hwndSelf, index, item ? item->str : "", action,
578                      dis.itemState, rect->left, rect->top,
579                      rect->right, rect->bottom );
580         SendMessageA(descr->owner, WM_DRAWITEM, wnd->wIDmenu, (LPARAM)&dis);
581     }
582     else
583     {
584         COLORREF oldText = 0, oldBk = 0;
585
586         if (action == ODA_FOCUS)
587         {
588             DrawFocusRect( hdc, rect );
589             return;
590         }
591         if (item && item->selected)
592         {
593             oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
594             oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
595         }
596
597         TRACE("[%04x]: painting %d (%s) action=%02x "
598                      "rect=%d,%d-%d,%d\n",
599                      wnd->hwndSelf, index, item ? item->str : "", action,
600                      rect->left, rect->top, rect->right, rect->bottom );
601         if (!item)
602             ExtTextOutA( hdc, rect->left + 1, rect->top,
603                            ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
604         else if (!(descr->style & LBS_USETABSTOPS)) 
605             ExtTextOutA( hdc, rect->left + 1, rect->top,
606                            ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
607                            strlen(item->str), NULL );
608         else
609         {
610             /* Output empty string to paint background in the full width. */
611             ExtTextOutA( hdc, rect->left + 1, rect->top,
612                            ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
613             TabbedTextOutA( hdc, rect->left + 1 , rect->top,
614                               item->str, strlen(item->str), 
615                               descr->nb_tabs, descr->tabs, 0);
616         }
617         if (item && item->selected)
618         {
619             SetBkColor( hdc, oldBk );
620             SetTextColor( hdc, oldText );
621         }
622         if (!ignoreFocus && (descr->focus_item == index) &&
623             (descr->caret_on) &&
624             (descr->in_focus)) DrawFocusRect( hdc, rect );
625     }
626 }
627
628
629 /***********************************************************************
630  *           LISTBOX_SetRedraw
631  *
632  * Change the redraw flag.
633  */
634 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
635 {
636     if (on)
637     {
638         if (!(descr->style & LBS_NOREDRAW)) return;
639         descr->style &= ~LBS_NOREDRAW;
640         if (descr->style & LBS_DISPLAYCHANGED)
641         {     /* page was changed while setredraw false, refresh automatically */
642             InvalidateRect(wnd->hwndSelf, NULL, TRUE);
643             if ((descr->top_item + descr->page_size) > descr->nb_items)
644             {      /* reset top of page if less than number of items/page */ 
645                 descr->top_item = descr->nb_items - descr->page_size;
646                 if (descr->top_item < 0) descr->top_item = 0;
647             }
648             descr->style &= ~LBS_DISPLAYCHANGED;
649         }
650         LISTBOX_UpdateScroll( wnd, descr );
651     }
652     else descr->style |= LBS_NOREDRAW;
653 }
654
655
656 /***********************************************************************
657  *           LISTBOX_RepaintItem
658  *
659  * Repaint a single item synchronously.
660  */
661 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
662                                  UINT action )
663 {
664     HDC hdc;
665     RECT rect;
666     HFONT oldFont = 0;
667     HBRUSH hbrush, oldBrush = 0;
668
669     /* Do not repaint the item if the item is not visible */
670     if (!IsWindowVisible(wnd->hwndSelf)) return;
671     if (descr->style & LBS_NOREDRAW)
672        {
673        descr->style |= LBS_DISPLAYCHANGED;
674        return;
675        }
676     if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
677     if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
678     if (descr->font) oldFont = SelectObject( hdc, descr->font );
679     hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
680                              hdc, (LPARAM)wnd->hwndSelf );
681     if (hbrush) oldBrush = SelectObject( hdc, hbrush );
682     if (wnd->dwStyle & WS_DISABLED)
683         SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
684     SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
685     LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action, FALSE );
686     if (oldFont) SelectObject( hdc, oldFont );
687     if (oldBrush) SelectObject( hdc, oldBrush );
688     ReleaseDC( wnd->hwndSelf, hdc );
689 }
690
691
692 /***********************************************************************
693  *           LISTBOX_InitStorage
694  */
695 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items,
696                                     DWORD bytes )
697 {
698     LB_ITEMDATA *item;
699
700     nb_items += LB_ARRAY_GRANULARITY - 1;
701     nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
702     if (descr->items)
703         nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
704     if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
705                               nb_items * sizeof(LB_ITEMDATA) )))
706     {
707         SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
708         return LB_ERRSPACE;
709     }
710     descr->items = item;
711     return LB_OKAY;
712 }
713
714
715 /***********************************************************************
716  *           LISTBOX_SetTabStops
717  */
718 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
719                                    LPINT tabs, BOOL short_ints )
720 {
721     if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
722     if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
723     if (!(descr->nb_tabs = count))
724     {
725         descr->tabs = NULL;
726         return TRUE;
727     }
728     /* FIXME: count = 1 */
729     if (!(descr->tabs = (INT *)HeapAlloc( descr->heap, 0,
730                                             descr->nb_tabs * sizeof(INT) )))
731         return FALSE;
732     if (short_ints)
733     {
734         INT i;
735         LPINT16 p = (LPINT16)tabs;
736
737         TRACE("[%04x]: settabstops ", wnd->hwndSelf );
738         for (i = 0; i < descr->nb_tabs; i++) {
739             descr->tabs[i] = *p++<<1; /* FIXME */
740             if (TRACE_ON(listbox)) DPRINTF("%hd ", descr->tabs[i]);
741         }
742         if (TRACE_ON(listbox)) DPRINTF("\n");
743     }
744     else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
745     /* FIXME: repaint the window? */
746     return TRUE;
747 }
748
749
750 /***********************************************************************
751  *           LISTBOX_GetText
752  */
753 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT index,
754                                 LPSTR buffer )
755 {
756     if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
757     if (HAS_STRINGS(descr))
758     {
759         if (!buffer)
760                 return strlen(descr->items[index].str);
761         strcpy( buffer, descr->items[index].str );
762         return strlen(buffer);
763     } else {
764         if (buffer)
765                 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
766         return sizeof(DWORD);
767     }
768 }
769
770
771 /***********************************************************************
772  *           LISTBOX_FindStringPos
773  *
774  * Find the nearest string located before a given string in sort order.
775  * If 'exact' is TRUE, return an error if we don't get an exact match.
776  */
777 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
778                                     BOOL exact )
779 {
780     INT index, min, max, res = -1;
781
782     if (!(descr->style & LBS_SORT)) return -1;  /* Add it at the end */
783     min = 0;
784     max = descr->nb_items;
785     while (min != max)
786     {
787         index = (min + max) / 2;
788         if (HAS_STRINGS(descr))
789             res = lstrcmpiA( descr->items[index].str, str );
790         else
791         {
792             COMPAREITEMSTRUCT cis;
793
794             cis.CtlType    = ODT_LISTBOX;
795             cis.CtlID      = wnd->wIDmenu;
796             cis.hwndItem   = wnd->hwndSelf;
797             cis.itemID1    = index;
798             cis.itemData1  = descr->items[index].data;
799             cis.itemID2    = -1;
800             cis.itemData2  = (DWORD)str;
801             cis.dwLocaleId = descr->locale;
802             res = SendMessageA( descr->owner, WM_COMPAREITEM,
803                                   wnd->wIDmenu, (LPARAM)&cis );
804         }
805         if (!res) return index;
806         if (res > 0) max = index;
807         else min = index + 1;
808     }
809     return exact ? -1 : max;
810 }
811
812
813 /***********************************************************************
814  *           LISTBOX_FindFileStrPos
815  *
816  * Find the nearest string located before a given string in directory
817  * sort order (i.e. first files, then directories, then drives).
818  */
819 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
820 {
821     INT min, max, res = -1;
822
823     if (!HAS_STRINGS(descr))
824         return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
825     min = 0;
826     max = descr->nb_items;
827     while (min != max)
828     {
829         INT index = (min + max) / 2;
830         const char *p = descr->items[index].str;
831         if (*p == '[')  /* drive or directory */
832         {
833             if (*str != '[') res = -1;
834             else if (p[1] == '-')  /* drive */
835             {
836                 if (str[1] == '-') res = str[2] - p[2];
837                 else res = -1;
838             }
839             else  /* directory */
840             {
841                 if (str[1] == '-') res = 1;
842                 else res = lstrcmpiA( str, p );
843             }
844         }
845         else  /* filename */
846         {
847             if (*str == '[') res = 1;
848             else res = lstrcmpiA( str, p );
849         }
850         if (!res) return index;
851         if (res < 0) max = index;
852         else min = index + 1;
853     }
854     return max;
855 }
856
857
858 /***********************************************************************
859  *           LISTBOX_FindString
860  *
861  * Find the item beginning with a given string.
862  */
863 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
864                                  LPCSTR str, BOOL exact )
865 {
866     INT i;
867     LB_ITEMDATA *item;
868
869     if (start >= descr->nb_items) start = -1;
870     item = descr->items + start + 1;
871     if (HAS_STRINGS(descr))
872     {
873         if (!str || ! str[0] ) return LB_ERR;
874         if (exact)
875         {
876             for (i = start + 1; i < descr->nb_items; i++, item++)
877                 if (!lstrcmpiA( str, item->str )) return i;
878             for (i = 0, item = descr->items; i <= start; i++, item++)
879                 if (!lstrcmpiA( str, item->str )) return i;
880         }
881         else
882         {
883  /* Special case for drives and directories: ignore prefix */
884 #define CHECK_DRIVE(item) \
885     if ((item)->str[0] == '[') \
886     { \
887         if (!strncasecmp( str, (item)->str+1, len )) return i; \
888         if (((item)->str[1] == '-') && !strncasecmp(str,(item)->str+2,len)) \
889         return i; \
890     }
891
892             INT len = strlen(str);
893             for (i = start + 1; i < descr->nb_items; i++, item++)
894             {
895                if (!strncasecmp( str, item->str, len )) return i;
896                CHECK_DRIVE(item);
897             }
898             for (i = 0, item = descr->items; i <= start; i++, item++)
899             {
900                if (!strncasecmp( str, item->str, len )) return i;
901                CHECK_DRIVE(item);
902             }
903 #undef CHECK_DRIVE
904         }
905     }
906     else
907     {
908         if (exact && (descr->style & LBS_SORT))
909             /* If sorted, use a WM_COMPAREITEM binary search */
910             return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
911
912         /* Otherwise use a linear search */
913         for (i = start + 1; i < descr->nb_items; i++, item++)
914             if (item->data == (DWORD)str) return i;
915         for (i = 0, item = descr->items; i <= start; i++, item++)
916             if (item->data == (DWORD)str) return i;
917     }
918     return LB_ERR;
919 }
920
921
922 /***********************************************************************
923  *           LISTBOX_GetSelCount
924  */
925 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
926 {
927     INT i, count;
928     LB_ITEMDATA *item = descr->items;
929
930     if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
931     for (i = count = 0; i < descr->nb_items; i++, item++)
932         if (item->selected) count++;
933     return count;
934 }
935
936
937 /***********************************************************************
938  *           LISTBOX_GetSelItems16
939  */
940 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
941                                       LPINT16 array )
942 {
943     INT i, count;
944     LB_ITEMDATA *item = descr->items;
945
946     if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
947     for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
948         if (item->selected) array[count++] = (INT16)i;
949     return count;
950 }
951
952
953 /***********************************************************************
954  *           LISTBOX_GetSelItems32
955  */
956 static LRESULT LISTBOX_GetSelItems( WND *wnd, LB_DESCR *descr, INT max,
957                                       LPINT array )
958 {
959     INT i, count;
960     LB_ITEMDATA *item = descr->items;
961
962     if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
963     for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
964         if (item->selected) array[count++] = i;
965     return count;
966 }
967
968
969 /***********************************************************************
970  *           LISTBOX_Paint
971  */
972 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
973 {
974     INT i, col_pos = descr->page_size - 1;
975     RECT rect;
976     RECT focusRect = {-1, -1, -1, -1};
977     HFONT oldFont = 0;
978     HBRUSH hbrush, oldBrush = 0;
979
980     if (descr->style & LBS_NOREDRAW) return 0;
981
982     SetRect( &rect, 0, 0, descr->width, descr->height );
983     if (descr->style & LBS_MULTICOLUMN)
984         rect.right = rect.left + descr->column_width;
985     else if (descr->horz_pos)
986     {
987         SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
988         rect.right += descr->horz_pos;
989     }
990
991     if (descr->font) oldFont = SelectObject( hdc, descr->font );
992     hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
993                              hdc, (LPARAM)wnd->hwndSelf );
994     if (hbrush) oldBrush = SelectObject( hdc, hbrush );
995     if (wnd->dwStyle & WS_DISABLED)
996         SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
997
998     if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
999         (descr->in_focus))
1000     {
1001         /* Special case for empty listbox: paint focus rect */
1002         rect.bottom = rect.top + descr->item_height;
1003         LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
1004                            ODA_FOCUS, FALSE );
1005         rect.top = rect.bottom;
1006     }
1007
1008     /* Paint all the item, regarding the selection
1009        Focus state will be painted after  */
1010
1011     for (i = descr->top_item; i < descr->nb_items; i++)
1012     {
1013         if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1014             rect.bottom = rect.top + descr->item_height;
1015         else
1016             rect.bottom = rect.top + descr->items[i].height;
1017
1018         if (i == descr->focus_item)
1019         {
1020             /* keep the focus rect, to paint the focus item after */
1021             focusRect.left = rect.left;
1022             focusRect.right = rect.right;
1023             focusRect.top = rect.top;
1024             focusRect.bottom = rect.bottom;
1025         }
1026         LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1027         rect.top = rect.bottom;
1028
1029         if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1030         {
1031             if (!IS_OWNERDRAW(descr))
1032             {
1033                 /* Clear the bottom of the column */
1034                 if (rect.top < descr->height)
1035                 {
1036                     rect.bottom = descr->height;
1037                     ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1038                                    &rect, NULL, 0, NULL );
1039                 }
1040             }
1041
1042             /* Go to the next column */
1043             rect.left += descr->column_width;
1044             rect.right += descr->column_width;
1045             rect.top = 0;
1046             col_pos = descr->page_size - 1;
1047         }
1048         else
1049         {
1050             col_pos--;
1051             if (rect.top >= descr->height) break;
1052         }
1053     }
1054
1055     /* Paint the focus item now */
1056     if (focusRect.top != focusRect.bottom && descr->caret_on)
1057         LISTBOX_PaintItem( wnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1058
1059     if (!IS_OWNERDRAW(descr))
1060     {
1061         /* Clear the remainder of the client area */
1062         if (rect.top < descr->height)
1063         {
1064             rect.bottom = descr->height;
1065             ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1066                            &rect, NULL, 0, NULL );
1067         }
1068         if (rect.right < descr->width)
1069         {
1070             rect.left   = rect.right;
1071             rect.right  = descr->width;
1072             rect.top    = 0;
1073             rect.bottom = descr->height;
1074             ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1075                            &rect, NULL, 0, NULL );
1076         }
1077     }
1078     if (oldFont) SelectObject( hdc, oldFont );
1079     if (oldBrush) SelectObject( hdc, oldBrush );
1080     return 0;
1081 }
1082
1083
1084 /***********************************************************************
1085  *           LISTBOX_InvalidateItems
1086  *
1087  * Invalidate all items from a given item. If the specified item is not
1088  * visible, nothing happens.
1089  */
1090 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
1091 {
1092     RECT rect;
1093
1094     if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
1095     {
1096         if (descr->style & LBS_NOREDRAW)
1097         {
1098             descr->style |= LBS_DISPLAYCHANGED;
1099             return;
1100         }
1101         rect.bottom = descr->height;
1102         InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1103         if (descr->style & LBS_MULTICOLUMN)
1104         {
1105             /* Repaint the other columns */
1106             rect.left  = rect.right;
1107             rect.right = descr->width;
1108             rect.top   = 0;
1109             InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1110         }
1111     }
1112 }
1113
1114
1115 /***********************************************************************
1116  *           LISTBOX_GetItemHeight
1117  */
1118 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
1119 {
1120     if (descr->style & LBS_OWNERDRAWVARIABLE)
1121     {
1122         if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1123         return descr->items[index].height;
1124     }
1125     else return descr->item_height;
1126 }
1127
1128
1129 /***********************************************************************
1130  *           LISTBOX_SetItemHeight
1131  */
1132 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1133                                       UINT height )
1134 {
1135     if (!height) height = 1;
1136
1137     if (descr->style & LBS_OWNERDRAWVARIABLE)
1138     {
1139         if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1140         TRACE("[%04x]: item %d height = %d\n",
1141                      wnd->hwndSelf, index, height );
1142         descr->items[index].height = height;
1143         LISTBOX_UpdateScroll( wnd, descr );
1144         LISTBOX_InvalidateItems( wnd, descr, index );
1145     }
1146     else if (height != descr->item_height)
1147     {
1148         TRACE("[%04x]: new height = %d\n",
1149                      wnd->hwndSelf, height );
1150         descr->item_height = height;
1151         LISTBOX_UpdatePage( wnd, descr );
1152         LISTBOX_UpdateScroll( wnd, descr );
1153         InvalidateRect( wnd->hwndSelf, 0, TRUE );
1154     }
1155     return LB_OKAY;
1156 }
1157
1158
1159 /***********************************************************************
1160  *           LISTBOX_SetHorizontalPos
1161  */
1162 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1163 {
1164     INT diff;
1165
1166     if (pos > descr->horz_extent - descr->width)
1167         pos = descr->horz_extent - descr->width;
1168     if (pos < 0) pos = 0;
1169     if (!(diff = descr->horz_pos - pos)) return;
1170     TRACE("[%04x]: new horz pos = %d\n",
1171                  wnd->hwndSelf, pos );
1172     descr->horz_pos = pos;
1173     LISTBOX_UpdateScroll( wnd, descr );
1174     if (abs(diff) < descr->width)
1175         ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1176                           SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1177     else
1178         InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1179 }
1180
1181
1182 /***********************************************************************
1183  *           LISTBOX_SetHorizontalExtent
1184  */
1185 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1186                                             UINT extent )
1187 {
1188     if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1189         return LB_OKAY;
1190     if (extent <= 0) extent = 1;
1191     if (extent == descr->horz_extent) return LB_OKAY;
1192     TRACE("[%04x]: new horz extent = %d\n",
1193                  wnd->hwndSelf, extent );
1194     descr->horz_extent = extent;
1195     if (descr->horz_pos > extent - descr->width)
1196         LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1197     else
1198         LISTBOX_UpdateScroll( wnd, descr );
1199     return LB_OKAY;
1200 }
1201
1202
1203 /***********************************************************************
1204  *           LISTBOX_SetColumnWidth
1205  */
1206 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1207 {
1208     if (width == descr->column_width) return LB_OKAY;
1209     TRACE("[%04x]: new column width = %d\n",
1210                  wnd->hwndSelf, width );
1211     descr->column_width = width;
1212     LISTBOX_UpdatePage( wnd, descr );
1213     return LB_OKAY;
1214 }
1215
1216
1217 /***********************************************************************
1218  *           LISTBOX_SetFont
1219  *
1220  * Returns the item height.
1221  */
1222 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1223 {
1224     HDC hdc;
1225     HFONT oldFont = 0;
1226     TEXTMETRICA tm;
1227
1228     descr->font = font;
1229
1230     if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1231     {
1232         ERR("unable to get DC.\n" );
1233         return 16;
1234     }
1235     if (font) oldFont = SelectObject( hdc, font );
1236     GetTextMetricsA( hdc, &tm );
1237     if (oldFont) SelectObject( hdc, oldFont );
1238     ReleaseDC( wnd->hwndSelf, hdc );
1239     if (!IS_OWNERDRAW(descr))
1240         LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1241     return tm.tmHeight ;
1242 }
1243
1244
1245 /***********************************************************************
1246  *           LISTBOX_MakeItemVisible
1247  *
1248  * Make sure that a given item is partially or fully visible.
1249  */
1250 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1251                                      BOOL fully )
1252 {
1253     INT top;
1254
1255     if (index <= descr->top_item) top = index;
1256     else if (descr->style & LBS_MULTICOLUMN)
1257     {
1258         INT cols = descr->width;
1259         if (!fully) cols += descr->column_width - 1;
1260         if (cols >= descr->column_width) cols /= descr->column_width;
1261         else cols = 1;
1262         if (index < descr->top_item + (descr->page_size * cols)) return;
1263         top = index - descr->page_size * (cols - 1);
1264     }
1265     else if (descr->style & LBS_OWNERDRAWVARIABLE)
1266     {
1267         INT height = fully ? descr->items[index].height : 1;
1268         for (top = index; top > descr->top_item; top--)
1269             if ((height += descr->items[top-1].height) > descr->height) break;
1270     }
1271     else
1272     {
1273         if (index < descr->top_item + descr->page_size) return;
1274         if (!fully && (index == descr->top_item + descr->page_size) &&
1275             (descr->height > (descr->page_size * descr->item_height))) return;
1276         top = index - descr->page_size + 1;
1277     }
1278     LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1279 }
1280
1281 /***********************************************************************
1282  *           LISTBOX_SetCaretIndex
1283  *
1284  * NOTES
1285  *   index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1286  *
1287  */
1288 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1289                                       BOOL fully_visible )
1290 {
1291     INT oldfocus = descr->focus_item;          
1292
1293     if (descr->style & LBS_NOSEL) return LB_ERR;
1294     if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1295     if (index == oldfocus) return LB_OKAY;
1296     descr->focus_item = index;
1297     if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1298         LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1299
1300     LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1301     if (descr->caret_on && (descr->in_focus))
1302         LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1303
1304     return LB_OKAY;
1305 }
1306
1307
1308 /***********************************************************************
1309  *           LISTBOX_SelectItemRange
1310  *
1311  * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1312  */
1313 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1314                                         INT last, BOOL on )
1315 {
1316     INT i;
1317
1318     /* A few sanity checks */
1319
1320     if (descr->style & LBS_NOSEL) return LB_ERR;
1321     if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1322     if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1323     if (last == -1) last = descr->nb_items - 1;
1324     if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1325     if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1326     /* selected_item reflects last selected/unselected item on multiple sel */
1327     descr->selected_item = last;
1328
1329     if (on)  /* Turn selection on */
1330     {
1331         for (i = first; i <= last; i++)
1332         {
1333             if (descr->items[i].selected) continue;
1334             descr->items[i].selected = TRUE;
1335             LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1336         }
1337         LISTBOX_SetCaretIndex( wnd, descr, last, TRUE );
1338     }
1339     else  /* Turn selection off */
1340     {
1341         for (i = first; i <= last; i++)
1342         {
1343             if (!descr->items[i].selected) continue;
1344             descr->items[i].selected = FALSE;
1345             LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1346         }
1347     }
1348     return LB_OKAY;
1349 }
1350
1351 /***********************************************************************
1352  *           LISTBOX_SetSelection
1353  */
1354 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1355                                      BOOL on, BOOL send_notify )
1356 {
1357     TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1358
1359     if (descr->style & LBS_NOSEL) return LB_ERR;
1360     if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1361     if (descr->style & LBS_MULTIPLESEL)
1362     {
1363         if (index == -1)  /* Select all items */
1364             return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1365         else  /* Only one item */
1366             return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1367     }
1368     else
1369     {
1370         INT oldsel = descr->selected_item;
1371         if (index == oldsel) return LB_OKAY;
1372         if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1373         if (index != -1) descr->items[index].selected = TRUE;
1374         descr->selected_item = index;
1375         if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT );
1376         if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1377         if (send_notify && descr->nb_items) SEND_NOTIFICATION( wnd, descr,
1378                                (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1379         else
1380             if( descr->lphc ) /* set selection change flag for parent combo */
1381                 descr->lphc->wState |= CBF_SELCHANGE;
1382     }
1383     return LB_OKAY;
1384 }
1385
1386
1387 /***********************************************************************
1388  *           LISTBOX_MoveCaret
1389  *
1390  * Change the caret position and extend the selection to the new caret.
1391  */
1392 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1393                                BOOL fully_visible )
1394 {
1395     INT oldfocus = descr->focus_item;          
1396
1397     if ((index <  0) || (index >= descr->nb_items)) 
1398         return;
1399
1400     /* Important, repaint needs to be done in this order if
1401        you want to mimic Windows behavior:
1402        1. Remove the focus and paint the item  
1403        2. Remove the selection and paint the item(s)
1404        3. Set the selection and repaint the item(s)
1405        4. Set the focus to 'index' and repaint the item */
1406
1407     /* 1. remove the focus and repaint the item */
1408     descr->focus_item = -1;
1409     if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1410         LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1411
1412     /* 2. then turn off the previous selection */
1413     /* 3. repaint the new selected item */
1414     if (descr->style & LBS_EXTENDEDSEL)
1415     {
1416         if (descr->anchor_item != -1)
1417         {
1418             INT first = min( index, descr->anchor_item );
1419             INT last  = max( index, descr->anchor_item );
1420             if (first > 0)
1421                 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1422             LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1423             LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1424         }
1425     }
1426     else if (!(descr->style & LBS_MULTIPLESEL))
1427     {
1428         /* Set selection to new caret item */
1429         LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1430     }
1431    
1432     /* 4. repaint the new item with the focus */
1433     descr->focus_item = index;
1434     LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1435     if (descr->caret_on && (descr->in_focus))
1436         LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1437 }
1438
1439
1440 /***********************************************************************
1441  *           LISTBOX_InsertItem
1442  */
1443 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1444                                    LPSTR str, DWORD data )
1445 {
1446     LB_ITEMDATA *item;
1447     INT max_items;
1448     INT oldfocus = descr->focus_item;
1449
1450     if (index == -1) index = descr->nb_items;
1451     else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1452     if (!descr->items) max_items = 0;
1453     else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1454     if (descr->nb_items == max_items)
1455     {
1456         /* We need to grow the array */
1457         max_items += LB_ARRAY_GRANULARITY;
1458         if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1459                                   max_items * sizeof(LB_ITEMDATA) )))
1460         {
1461             SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1462             return LB_ERRSPACE;
1463         }
1464         descr->items = item;
1465     }
1466
1467     /* Insert the item structure */
1468
1469     item = &descr->items[index];
1470     if (index < descr->nb_items)
1471         RtlMoveMemory( item + 1, item,
1472                        (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1473     item->str      = str;
1474     item->data     = data;
1475     item->height   = 0;
1476     item->selected = FALSE;
1477     descr->nb_items++;
1478
1479     /* Get item height */
1480
1481     if (descr->style & LBS_OWNERDRAWVARIABLE)
1482     {
1483         MEASUREITEMSTRUCT mis;
1484
1485         mis.CtlType    = ODT_LISTBOX;
1486         mis.CtlID      = wnd->wIDmenu;
1487         mis.itemID     = index;
1488         mis.itemData   = descr->items[index].data;
1489         mis.itemHeight = descr->item_height;
1490         SendMessageA( descr->owner, WM_MEASUREITEM, wnd->wIDmenu, (LPARAM)&mis );
1491         item->height = mis.itemHeight ? mis.itemHeight : 1;
1492         TRACE("[%04x]: measure item %d (%s) = %d\n",
1493                      wnd->hwndSelf, index, str ? str : "", item->height );
1494     }
1495
1496     /* Repaint the items */
1497
1498     LISTBOX_UpdateScroll( wnd, descr );
1499     LISTBOX_InvalidateItems( wnd, descr, index );
1500
1501     /* Move selection and focused item */
1502     /* If listbox was empty, set focus to the first item */
1503     if (descr->nb_items == 1)
1504          LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1505     /* single select don't change selection index in win31 */
1506     else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1507     {
1508         descr->selected_item++;
1509         LISTBOX_SetSelection( wnd, descr, descr->selected_item-1, TRUE, FALSE );     
1510     }
1511     else
1512     {
1513         if (index <= descr->selected_item)
1514         {
1515            descr->selected_item++;
1516            descr->focus_item = oldfocus; /* focus not changed */
1517         }
1518     }
1519     return LB_OKAY;
1520 }
1521
1522
1523 /***********************************************************************
1524  *           LISTBOX_InsertString
1525  */
1526 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1527                                      LPCSTR str )
1528 {
1529     LPSTR new_str = NULL;
1530     DWORD data = 0;
1531     LRESULT ret;
1532
1533     if (HAS_STRINGS(descr))
1534     {
1535         if (!str) str="";
1536         if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1537         {
1538             SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1539             return LB_ERRSPACE;
1540         }
1541     }
1542     else data = (DWORD)str;
1543
1544     if (index == -1) index = descr->nb_items;
1545     if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1546     {
1547         if (new_str) HeapFree( descr->heap, 0, new_str );
1548         return ret;
1549     }
1550
1551     TRACE("[%04x]: added item %d '%s'\n",
1552                  wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1553     return index;
1554 }
1555
1556
1557 /***********************************************************************
1558  *           LISTBOX_DeleteItem
1559  *
1560  * Delete the content of an item. 'index' must be a valid index.
1561  */
1562 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1563 {
1564     /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1565      *       while Win95 sends it for all items with user data.
1566      *       It's probably better to send it too often than not
1567      *       often enough, so this is what we do here.
1568      */
1569     if (IS_OWNERDRAW(descr) || descr->items[index].data)
1570     {
1571         DELETEITEMSTRUCT dis;
1572
1573         dis.CtlType  = ODT_LISTBOX;
1574         dis.CtlID    = wnd->wIDmenu;
1575         dis.itemID   = index;
1576         dis.hwndItem = wnd->hwndSelf;
1577         dis.itemData = descr->items[index].data;
1578         SendMessageA( descr->owner, WM_DELETEITEM, wnd->wIDmenu, (LPARAM)&dis );
1579     }
1580     if (HAS_STRINGS(descr) && descr->items[index].str)
1581         HeapFree( descr->heap, 0, descr->items[index].str );
1582 }
1583
1584
1585 /***********************************************************************
1586  *           LISTBOX_RemoveItem
1587  *
1588  * Remove an item from the listbox and delete its content.
1589  */
1590 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1591 {
1592     LB_ITEMDATA *item;
1593     INT max_items;
1594
1595     if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1596     else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1597
1598     /* We need to invalidate the original rect instead of the updated one. */
1599     LISTBOX_InvalidateItems( wnd, descr, index );
1600
1601     LISTBOX_DeleteItem( wnd, descr, index );
1602
1603     /* Remove the item */
1604
1605     item = &descr->items[index];
1606     if (index < descr->nb_items-1)
1607         RtlMoveMemory( item, item + 1,
1608                        (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1609     descr->nb_items--;
1610     if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1611
1612     /* Shrink the item array if possible */
1613
1614     max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1615     if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1616     {
1617         max_items -= LB_ARRAY_GRANULARITY;
1618         item = HeapReAlloc( descr->heap, 0, descr->items,
1619                             max_items * sizeof(LB_ITEMDATA) );
1620         if (item) descr->items = item;
1621     }
1622     /* Repaint the items */
1623
1624     LISTBOX_UpdateScroll( wnd, descr );
1625     /* if we removed the scrollbar, reset the top of the list
1626       (correct for owner-drawn ???) */
1627     if (descr->nb_items == descr->page_size)
1628         LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1629
1630     /* Move selection and focused item */
1631     if (!IS_MULTISELECT(descr))
1632     {
1633         if (index == descr->selected_item)
1634             descr->selected_item = -1;
1635         else if (index < descr->selected_item)
1636     {
1637             descr->selected_item--;
1638             if (ISWIN31) /* win 31 do not change the selected item number */
1639                LISTBOX_SetSelection( wnd, descr, descr->selected_item + 1, TRUE, FALSE);
1640     }
1641     }
1642
1643     if (descr->focus_item >= descr->nb_items)
1644     {
1645           descr->focus_item = descr->nb_items - 1;
1646           if (descr->focus_item < 0) descr->focus_item = 0;
1647     }
1648     return LB_OKAY;
1649 }
1650
1651
1652 /***********************************************************************
1653  *           LISTBOX_ResetContent
1654  */
1655 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1656 {
1657     INT i;
1658
1659     for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1660     if (descr->items) HeapFree( descr->heap, 0, descr->items );
1661     descr->nb_items      = 0;
1662     descr->top_item      = 0;
1663     descr->selected_item = -1;
1664     descr->focus_item    = 0;
1665     descr->anchor_item   = -1;
1666     descr->items         = NULL;
1667 }
1668
1669
1670 /***********************************************************************
1671  *           LISTBOX_SetCount
1672  */
1673 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1674 {
1675     LRESULT ret;
1676
1677     if (HAS_STRINGS(descr)) return LB_ERR;
1678     /* FIXME: this is far from optimal... */
1679     if (count > descr->nb_items)
1680     {
1681         while (count > descr->nb_items)
1682             if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1683                 return ret;
1684     }
1685     else if (count < descr->nb_items)
1686     {
1687         while (count < descr->nb_items)
1688             if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1689                 return ret;
1690     }
1691     return LB_OKAY;
1692 }
1693
1694
1695 /***********************************************************************
1696  *           LISTBOX_Directory
1697  */
1698 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1699                                   LPCSTR filespec, BOOL long_names )
1700 {
1701     HANDLE handle;
1702     LRESULT ret = LB_OKAY;
1703     WIN32_FIND_DATAA entry;
1704     int pos;
1705
1706     /* don't scan directory if we just want drives exclusively */
1707     if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1708         /* scan directory */
1709         if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1710         {
1711             if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1712         }
1713         else
1714         {
1715             do
1716             {
1717                 char buffer[270];
1718                 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1719                 {
1720                     if (!(attrib & DDL_DIRECTORY) ||
1721                         !strcmp( entry.cAlternateFileName, "." )) continue;
1722                     if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1723                     else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1724                 }
1725                 else  /* not a directory */
1726                 {
1727 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1728                  FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1729
1730                     if ((attrib & DDL_EXCLUSIVE) &&
1731                         ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1732                         continue;
1733 #undef ATTRIBS
1734                     if (long_names) strcpy( buffer, entry.cFileName );
1735                     else strcpy( buffer, entry.cAlternateFileName );
1736                 }
1737                 if (!long_names) CharLowerA( buffer );
1738                 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1739                 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1740                     break;
1741             } while (FindNextFileA( handle, &entry ));
1742             FindClose( handle );
1743         }
1744     }
1745
1746     /* scan drives */
1747     if ((ret >= 0) && (attrib & DDL_DRIVES))
1748     {
1749         char buffer[] = "[-a-]";
1750         char root[] = "A:\\";
1751         int drive;
1752         for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1753         {
1754             if (GetDriveTypeA(root) <= DRIVE_NO_ROOT_DIR) continue;
1755             if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1756                 break;
1757         }
1758     }
1759     return ret;
1760 }
1761
1762
1763 /***********************************************************************
1764  *           LISTBOX_HandleVScroll
1765  */
1766 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1767                                       WPARAM wParam, LPARAM lParam )
1768 {
1769     SCROLLINFO info;
1770
1771     if (descr->style & LBS_MULTICOLUMN) return 0;
1772     switch(LOWORD(wParam))
1773     {
1774     case SB_LINEUP:
1775         LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1776         break;
1777     case SB_LINEDOWN:
1778         LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1779         break;
1780     case SB_PAGEUP:
1781         LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1782                             LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1783         break;
1784     case SB_PAGEDOWN:
1785         LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1786                             LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1787         break;
1788     case SB_THUMBPOSITION:
1789         LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1790         break;
1791     case SB_THUMBTRACK:
1792         info.cbSize = sizeof(info);
1793         info.fMask = SIF_TRACKPOS;
1794         GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1795         LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1796         break;
1797     case SB_TOP:
1798         LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1799         break;
1800     case SB_BOTTOM:
1801         LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1802         break;
1803     }
1804     return 0;
1805 }
1806
1807
1808 /***********************************************************************
1809  *           LISTBOX_HandleHScroll
1810  */
1811 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1812                                       WPARAM wParam, LPARAM lParam )
1813 {
1814     SCROLLINFO info;
1815     INT page;
1816
1817     if (descr->style & LBS_MULTICOLUMN)
1818     {
1819         switch(LOWORD(wParam))
1820         {
1821         case SB_LINELEFT:
1822             LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1823                                 TRUE );
1824             break;
1825         case SB_LINERIGHT:
1826             LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1827                                 TRUE );
1828             break;
1829         case SB_PAGELEFT:
1830             page = descr->width / descr->column_width;
1831             if (page < 1) page = 1;
1832             LISTBOX_SetTopItem( wnd, descr,
1833                              descr->top_item - page * descr->page_size, TRUE );
1834             break;
1835         case SB_PAGERIGHT:
1836             page = descr->width / descr->column_width;
1837             if (page < 1) page = 1;
1838             LISTBOX_SetTopItem( wnd, descr,
1839                              descr->top_item + page * descr->page_size, TRUE );
1840             break;
1841         case SB_THUMBPOSITION:
1842             LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1843                                 TRUE );
1844             break;
1845         case SB_THUMBTRACK:
1846             info.cbSize = sizeof(info);
1847             info.fMask  = SIF_TRACKPOS;
1848             GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1849             LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1850                                 TRUE );
1851             break;
1852         case SB_LEFT:
1853             LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1854             break;
1855         case SB_RIGHT:
1856             LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1857             break;
1858         }
1859     }
1860     else if (descr->horz_extent)
1861     {
1862         switch(LOWORD(wParam))
1863         {
1864         case SB_LINELEFT:
1865             LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1866             break;
1867         case SB_LINERIGHT:
1868             LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1869             break;
1870         case SB_PAGELEFT:
1871             LISTBOX_SetHorizontalPos( wnd, descr,
1872                                       descr->horz_pos - descr->width );
1873             break;
1874         case SB_PAGERIGHT:
1875             LISTBOX_SetHorizontalPos( wnd, descr,
1876                                       descr->horz_pos + descr->width );
1877             break;
1878         case SB_THUMBPOSITION:
1879             LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1880             break;
1881         case SB_THUMBTRACK:
1882             info.cbSize = sizeof(info);
1883             info.fMask = SIF_TRACKPOS;
1884             GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1885             LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1886             break;
1887         case SB_LEFT:
1888             LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1889             break;
1890         case SB_RIGHT:
1891             LISTBOX_SetHorizontalPos( wnd, descr,
1892                                       descr->horz_extent - descr->width );
1893             break;
1894         }
1895     }
1896     return 0;
1897 }
1898
1899 static LRESULT LISTBOX_HandleMouseWheel(WND *wnd, LB_DESCR *descr,WPARAM wParam, LPARAM lParam )
1900 {
1901     short gcWheelDelta = 0;
1902     UINT pulScrollLines = 3;
1903
1904     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1905
1906     gcWheelDelta -= (short) HIWORD(wParam);
1907
1908     if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1909     {
1910         int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1911         cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1912         LISTBOX_SetTopItem( wnd, descr, descr->top_item + cLineScroll, TRUE );
1913     }
1914     return 0;
1915 }
1916
1917 /***********************************************************************
1918  *           LISTBOX_HandleLButtonDown
1919  */
1920 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1921                                           WPARAM wParam, INT x, INT y )
1922 {
1923     INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1924     TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1925                  wnd->hwndSelf, x, y, index );
1926     if (!descr->caret_on && (descr->in_focus)) return 0;
1927
1928     if (!descr->in_focus)
1929     {
1930         if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1931         else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1932                                              : descr->lphc->self->hwndSelf );
1933     }
1934
1935     if (index == -1) return 0;
1936
1937     if (descr->style & LBS_EXTENDEDSEL)
1938     {
1939         /* we should perhaps make sure that all items are deselected
1940            FIXME: needed for !LBS_EXTENDEDSEL, too ?
1941            if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1942            LISTBOX_SetSelection( wnd, descr, -1, FALSE, FALSE);
1943         */
1944
1945         if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1946         if (wParam & MK_CONTROL)
1947         {
1948             LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1949             LISTBOX_SetSelection( wnd, descr, index,
1950                                   !descr->items[index].selected,
1951                                   (descr->style & LBS_NOTIFY) != 0);
1952         }
1953         else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1954     }
1955     else
1956     {
1957         LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1958         LISTBOX_SetSelection( wnd, descr, index,
1959                               (!(descr->style & LBS_MULTIPLESEL) ||
1960                                !descr->items[index].selected),
1961                               (descr->style & LBS_NOTIFY) != 0 );
1962     }
1963
1964     descr->captured = TRUE;
1965     SetCapture( wnd->hwndSelf );
1966
1967     if (!descr->lphc)
1968     {
1969         if (descr->style & LBS_NOTIFY )
1970             SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1971                             MAKELPARAM( x, y ) );
1972         if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1973         {
1974             POINT pt;
1975             
1976             pt.x = x;
1977             pt.y = y;
1978
1979             if (DragDetect( wnd->hwndSelf, pt ))
1980                 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1981         }
1982     }
1983     return 0;
1984 }
1985
1986
1987 /*************************************************************************
1988  * LISTBOX_HandleLButtonDownCombo [Internal] 
1989  *
1990  * Process LButtonDown message for the ComboListBox
1991  *
1992  * PARAMS
1993  *     pWnd       [I] The windows internal structure
1994  *     pDescr     [I] The ListBox internal structure
1995  *     wParam     [I] Key Flag (WM_LBUTTONDOWN doc for more info)
1996  *     x          [I] X Mouse Coordinate
1997  *     y          [I] Y Mouse Coordinate
1998  *
1999  * RETURNS
2000  *     0 since we are processing the WM_LBUTTONDOWN Message
2001  *
2002  * NOTES
2003  *  This function is only to be used when a ListBox is a ComboListBox
2004  */
2005
2006 static LRESULT LISTBOX_HandleLButtonDownCombo( WND *pWnd, LB_DESCR *pDescr,
2007                                                UINT msg, WPARAM wParam, INT x, INT y)
2008 {
2009     RECT clientRect, screenRect;
2010     POINT mousePos;
2011
2012     mousePos.x = x;
2013     mousePos.y = y;
2014
2015     GetClientRect(pWnd->hwndSelf, &clientRect);
2016
2017     if(PtInRect(&clientRect, mousePos))
2018     {  
2019        /* MousePos is in client, resume normal processing */
2020         if (msg == WM_LBUTTONDOWN)
2021         {
2022            pDescr->lphc->droppedIndex = pDescr->nb_items ? pDescr->selected_item : -1;
2023            return LISTBOX_HandleLButtonDown( pWnd, pDescr, wParam, x, y);
2024         }
2025         else if (pDescr->style & LBS_NOTIFY)
2026             SEND_NOTIFICATION( pWnd, pDescr, LBN_DBLCLK );
2027         return 0;
2028     }
2029     else
2030     {
2031         POINT screenMousePos;
2032         HWND hWndOldCapture;
2033
2034         /* Check the Non-Client Area */
2035         screenMousePos = mousePos;
2036         hWndOldCapture = GetCapture();
2037         ReleaseCapture();
2038         GetWindowRect(pWnd->hwndSelf, &screenRect);
2039         ClientToScreen(pWnd->hwndSelf, &screenMousePos);
2040
2041         if(!PtInRect(&screenRect, screenMousePos))
2042         { 
2043             LISTBOX_SetSelection( pWnd, pDescr, pDescr->lphc->droppedIndex, FALSE, FALSE );
2044             COMBO_FlipListbox( pDescr->lphc, FALSE, FALSE );
2045             return 0;
2046         }
2047         else
2048         {
2049             /* Check to see the NC is a scrollbar */
2050             INT nHitTestType=0;
2051             /* Check Vertical scroll bar */
2052             if (pWnd->dwStyle & WS_VSCROLL)
2053             {
2054                 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2055                 if (PtInRect( &clientRect, mousePos )) 
2056                 {
2057                     nHitTestType = HTVSCROLL;
2058                 }
2059             }
2060               /* Check horizontal scroll bar */
2061             if (pWnd->dwStyle & WS_HSCROLL)
2062             {
2063                 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2064                 if (PtInRect( &clientRect, mousePos ))
2065                 {
2066                     nHitTestType = HTHSCROLL;
2067                 }
2068             }
2069             /* Windows sends this message when a scrollbar is clicked 
2070              */
2071             
2072             if(nHitTestType != 0)
2073             {
2074                 SendMessageA(pWnd->hwndSelf, WM_NCLBUTTONDOWN, nHitTestType, 
2075                     MAKELONG(screenMousePos.x, screenMousePos.y));
2076             }
2077             /* Resume the Capture after scrolling is complete 
2078              */
2079             if(hWndOldCapture != 0)
2080             {
2081                 SetCapture(hWndOldCapture);
2082             }
2083         }
2084     }
2085     return 0;
2086 }
2087
2088 /***********************************************************************
2089  *           LISTBOX_HandleLButtonUp
2090  */
2091 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
2092 {
2093     if (LISTBOX_Timer != LB_TIMER_NONE)
2094         KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2095     LISTBOX_Timer = LB_TIMER_NONE;
2096     if (descr->captured)
2097     {
2098         descr->captured = FALSE;
2099         if (GetCapture() == wnd->hwndSelf) ReleaseCapture();
2100         if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2101             SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2102     }
2103     return 0;
2104 }
2105
2106
2107 /***********************************************************************
2108  *           LISTBOX_HandleTimer
2109  *
2110  * Handle scrolling upon a timer event.
2111  * Return TRUE if scrolling should continue.
2112  */
2113 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
2114                                     INT index, TIMER_DIRECTION dir )
2115 {
2116     switch(dir)
2117     {
2118     case LB_TIMER_UP:
2119         if (descr->top_item) index = descr->top_item - 1;
2120         else index = 0;
2121         break;
2122     case LB_TIMER_LEFT:
2123         if (descr->top_item) index -= descr->page_size;
2124         break;
2125     case LB_TIMER_DOWN:
2126         index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
2127         if (index == descr->focus_item) index++;
2128         if (index >= descr->nb_items) index = descr->nb_items - 1;
2129         break;
2130     case LB_TIMER_RIGHT:
2131         if (index + descr->page_size < descr->nb_items)
2132             index += descr->page_size;
2133         break;
2134     case LB_TIMER_NONE:
2135         break;
2136     }
2137     if (index == descr->focus_item) return FALSE;
2138     LISTBOX_MoveCaret( wnd, descr, index, FALSE );
2139     return TRUE;
2140 }
2141
2142
2143 /***********************************************************************
2144  *           LISTBOX_HandleSystemTimer
2145  *
2146  * WM_SYSTIMER handler.
2147  */
2148 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
2149 {
2150     if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
2151     {
2152         KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2153         LISTBOX_Timer = LB_TIMER_NONE;
2154     }
2155     return 0;
2156 }
2157
2158
2159 /***********************************************************************
2160  *           LISTBOX_HandleMouseMove
2161  *
2162  * WM_MOUSEMOVE handler.
2163  */
2164 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
2165                                      INT x, INT y )
2166 {
2167     INT index;
2168     TIMER_DIRECTION dir = LB_TIMER_NONE;
2169
2170     if (!descr->captured) return;
2171
2172     if (descr->style & LBS_MULTICOLUMN)
2173     {
2174         if (y < 0) y = 0;
2175         else if (y >= descr->item_height * descr->page_size)
2176             y = descr->item_height * descr->page_size - 1;
2177
2178         if (x < 0)
2179         {
2180             dir = LB_TIMER_LEFT;
2181             x = 0;
2182         }
2183         else if (x >= descr->width)
2184         {
2185             dir = LB_TIMER_RIGHT;
2186             x = descr->width - 1;
2187         }
2188     }
2189     else
2190     {
2191         if (y < 0) dir = LB_TIMER_UP;  /* above */
2192         else if (y >= descr->height) dir = LB_TIMER_DOWN;  /* below */
2193     }
2194
2195     index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
2196     if (index == -1) index = descr->focus_item;
2197     if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
2198
2199     /* Start/stop the system timer */
2200
2201     if (dir != LB_TIMER_NONE)
2202         SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2203     else if (LISTBOX_Timer != LB_TIMER_NONE)
2204         KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2205     LISTBOX_Timer = dir;
2206 }
2207
2208
2209 /***********************************************************************
2210  *           LISTBOX_HandleKeyDown
2211  */
2212 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
2213 {
2214     INT caret = -1;
2215     BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2216     if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2217        bForceSelection = FALSE; /* only for single select list */
2218
2219     if (descr->style & LBS_WANTKEYBOARDINPUT)
2220     {
2221         caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
2222                                 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2223                                 wnd->hwndSelf );
2224         if (caret == -2) return 0;
2225     }
2226     if (caret == -1) switch(wParam)
2227     {
2228     case VK_LEFT:
2229         if (descr->style & LBS_MULTICOLUMN)
2230         {
2231             bForceSelection = FALSE;
2232             if (descr->focus_item >= descr->page_size)
2233                 caret = descr->focus_item - descr->page_size;
2234             break;
2235         }
2236         /* fall through */
2237     case VK_UP:
2238         caret = descr->focus_item - 1;
2239         if (caret < 0) caret = 0;
2240         break;
2241     case VK_RIGHT:
2242         if (descr->style & LBS_MULTICOLUMN)
2243         {
2244             bForceSelection = FALSE;
2245             if (descr->focus_item + descr->page_size < descr->nb_items)
2246                 caret = descr->focus_item + descr->page_size;
2247             break;
2248         }
2249         /* fall through */
2250     case VK_DOWN:
2251         caret = descr->focus_item + 1;
2252         if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2253         break;
2254
2255     case VK_PRIOR:
2256         if (descr->style & LBS_MULTICOLUMN)
2257         {
2258             INT page = descr->width / descr->column_width;
2259             if (page < 1) page = 1;
2260             caret = descr->focus_item - (page * descr->page_size) + 1;
2261         }
2262         else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
2263         if (caret < 0) caret = 0;
2264         break;
2265     case VK_NEXT:
2266         if (descr->style & LBS_MULTICOLUMN)
2267         {
2268             INT page = descr->width / descr->column_width;
2269             if (page < 1) page = 1;
2270             caret = descr->focus_item + (page * descr->page_size) - 1;
2271         }
2272         else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
2273         if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2274         break;
2275     case VK_HOME:
2276         caret = 0;
2277         break;
2278     case VK_END:
2279         caret = descr->nb_items - 1;
2280         break;
2281     case VK_SPACE:
2282         if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2283         else if (descr->style & LBS_MULTIPLESEL)
2284         {
2285             LISTBOX_SetSelection( wnd, descr, descr->focus_item,
2286                                   !descr->items[descr->focus_item].selected,
2287                                   (descr->style & LBS_NOTIFY) != 0 );
2288         }
2289         break;
2290     default:
2291         bForceSelection = FALSE;
2292     }
2293     if (bForceSelection) /* focused item is used instead of key */
2294         caret = descr->focus_item;
2295     if (caret >= 0)
2296     {
2297         if ((descr->style & LBS_EXTENDEDSEL) &&
2298             !(GetKeyState( VK_SHIFT ) & 0x8000))
2299             descr->anchor_item = caret;
2300         LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2301         LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2302         if (descr->style & LBS_NOTIFY)
2303         {
2304             if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
2305             {
2306                 /* make sure that combo parent doesn't hide us */
2307                 descr->lphc->wState |= CBF_NOROLLUP;
2308             }
2309             if (descr->nb_items) SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2310         }
2311     }
2312     return 0;
2313 }
2314
2315
2316 /***********************************************************************
2317  *           LISTBOX_HandleChar
2318  */
2319 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
2320                                    WPARAM wParam )
2321 {
2322     INT caret = -1;
2323     char str[2];
2324     
2325     str[0] = wParam & 0xff; 
2326     str[1] = '\0';
2327
2328     if (descr->style & LBS_WANTKEYBOARDINPUT)
2329     {
2330         caret = SendMessageA( descr->owner, WM_CHARTOITEM,
2331                                 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2332                                 wnd->hwndSelf );
2333         if (caret == -2) return 0;
2334     }
2335     if (caret == -1)
2336         caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2337     if (caret != -1)
2338     {
2339         if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2340            LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2341         LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2342         if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2343             SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2344     }
2345     return 0;
2346 }
2347
2348
2349 /***********************************************************************
2350  *           LISTBOX_Create
2351  */
2352 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2353 {
2354     LB_DESCR *descr;
2355     MEASUREITEMSTRUCT mis;
2356     RECT rect;
2357
2358     if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2359         return FALSE;
2360     if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2361     {
2362         HeapFree( GetProcessHeap(), 0, descr );
2363         return FALSE;
2364     }
2365     GetClientRect( wnd->hwndSelf, &rect );
2366     descr->owner         = GetParent( wnd->hwndSelf );
2367     descr->style         = wnd->dwStyle;
2368     descr->width         = rect.right - rect.left;
2369     descr->height        = rect.bottom - rect.top;
2370     descr->items         = NULL;
2371     descr->nb_items      = 0;
2372     descr->top_item      = 0;
2373     descr->selected_item = -1;
2374     descr->focus_item    = 0;
2375     descr->anchor_item   = -1;
2376     descr->item_height   = 1;
2377     descr->page_size     = 1;
2378     descr->column_width  = 150;
2379     descr->horz_extent   = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2380     descr->horz_pos      = 0;
2381     descr->nb_tabs       = 0;
2382     descr->tabs          = NULL;
2383     descr->caret_on      = lphc ? FALSE : TRUE;
2384     if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2385     descr->in_focus      = FALSE;
2386     descr->captured      = FALSE;
2387     descr->font          = 0;
2388     descr->locale        = 0;  /* FIXME */
2389     descr->lphc          = lphc;
2390
2391     if( ( GetExpWinVer16( wnd->hInstance ) & 0xFF00 ) == 0x0300
2392         && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2393     {
2394         /* Win95 document "List Box Differences" from MSDN:
2395            If a list box in a version 3.x application has either the
2396            WS_HSCROLL or WS_VSCROLL style, the list box receives both
2397            horizontal and vertical scroll bars.
2398         */
2399         descr->style |= WS_VSCROLL | WS_HSCROLL;
2400     }
2401
2402     if( lphc )
2403     {
2404         TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2405                      wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2406         descr->owner = lphc->self->hwndSelf;
2407     }
2408
2409     *(LB_DESCR **)wnd->wExtra = descr;
2410
2411 /*    if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2412  */
2413     if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2414     if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2415     if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2416     descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2417
2418     if (descr->style & LBS_OWNERDRAWFIXED)
2419     {
2420         if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2421         {
2422             /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2423           descr->item_height = lphc->fixedOwnerDrawHeight;
2424         }
2425         else
2426         {
2427             mis.CtlType    = ODT_LISTBOX;
2428             mis.CtlID      = wnd->wIDmenu;
2429             mis.itemID     = -1;
2430             mis.itemWidth  =  0;
2431             mis.itemData   =  0;
2432             mis.itemHeight = descr->item_height;
2433             SendMessageA( descr->owner, WM_MEASUREITEM, wnd->wIDmenu, (LPARAM)&mis );
2434             descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2435         }
2436     }
2437
2438     TRACE("owner: %04x, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2439     return TRUE;
2440 }
2441
2442
2443 /***********************************************************************
2444  *           LISTBOX_Destroy
2445  */
2446 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2447 {
2448     LISTBOX_ResetContent( wnd, descr );
2449     HeapDestroy( descr->heap );
2450     HeapFree( GetProcessHeap(), 0, descr );
2451     wnd->wExtra[0] = 0;
2452     return TRUE;
2453 }
2454
2455
2456 /***********************************************************************
2457  *           ListBoxWndProc
2458  */
2459 static inline LRESULT WINAPI ListBoxWndProc_locked( WND* wnd, UINT msg,
2460                                       WPARAM wParam, LPARAM lParam )
2461 {
2462     LRESULT ret;
2463     LB_DESCR *descr;
2464     HWND        hwnd = wnd->hwndSelf;
2465
2466     if (!wnd) return 0;
2467     if (!(descr = *(LB_DESCR **)wnd->wExtra))
2468     {
2469         switch (msg)
2470         {
2471             case WM_CREATE:
2472             {
2473                 if (!LISTBOX_Create( wnd, NULL ))
2474                      return -1;
2475                 TRACE("creating wnd=%04x descr=%p\n",
2476                       hwnd, *(LB_DESCR **)wnd->wExtra );
2477                 return 0;
2478             }
2479             case WM_NCCREATE:
2480             {
2481                 /*
2482                  * When a listbox is not in a combobox and the look
2483                  * is win95,  the WS_BORDER style is replaced with 
2484                  * the WS_EX_CLIENTEDGE style.
2485                  */
2486                 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2487                      (wnd->dwStyle & WS_BORDER) )
2488                 {
2489                     wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2490                     wnd->dwStyle     &= ~ WS_BORDER;
2491                 }
2492             }
2493         }
2494
2495         /* Ignore all other messages before we get a WM_CREATE */
2496         return DefWindowProcA( hwnd, msg, wParam, lParam );
2497     }
2498
2499     TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2500                  wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2501     switch(msg)
2502     {
2503     case LB_RESETCONTENT16:
2504     case LB_RESETCONTENT:
2505         LISTBOX_ResetContent( wnd, descr );
2506         LISTBOX_UpdateScroll( wnd, descr );
2507         InvalidateRect( wnd->hwndSelf, NULL, TRUE );
2508         return 0;
2509
2510     case LB_ADDSTRING16:
2511         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2512         /* fall through */
2513     case LB_ADDSTRING:
2514         wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2515         return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2516
2517     case LB_INSERTSTRING16:
2518         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2519         wParam = (INT)(INT16)wParam;
2520         /* fall through */
2521     case LB_INSERTSTRING:
2522         return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2523
2524     case LB_ADDFILE16:
2525         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2526         /* fall through */
2527     case LB_ADDFILE:
2528         wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2529         return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2530
2531     case LB_DELETESTRING16:
2532     case LB_DELETESTRING:
2533         if (LISTBOX_RemoveItem( wnd, descr, wParam) != LB_ERR)
2534            return descr->nb_items;
2535         else
2536            return LB_ERR;       
2537
2538     case LB_GETITEMDATA16:
2539     case LB_GETITEMDATA:
2540         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2541             return LB_ERR;
2542         return descr->items[wParam].data;
2543
2544     case LB_SETITEMDATA16:
2545     case LB_SETITEMDATA:
2546         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2547             return LB_ERR;
2548         descr->items[wParam].data = (DWORD)lParam;
2549         return LB_OKAY;
2550
2551     case LB_GETCOUNT16:
2552     case LB_GETCOUNT:
2553         return descr->nb_items;
2554
2555     case LB_GETTEXT16:
2556         lParam = (LPARAM)MapSL(lParam);
2557         /* fall through */
2558     case LB_GETTEXT:
2559         return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2560
2561     case LB_GETTEXTLEN16:
2562         /* fall through */
2563     case LB_GETTEXTLEN:
2564         if (wParam >= descr->nb_items)
2565             return LB_ERR;
2566         return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2567                                    : sizeof(DWORD));
2568
2569     case LB_GETCURSEL16:
2570     case LB_GETCURSEL:
2571         if (descr->nb_items==0)
2572           return LB_ERR;
2573         if (!IS_MULTISELECT(descr))
2574           return descr->selected_item;
2575         /* else */
2576         if (descr->selected_item!=-1)
2577           return descr->selected_item;
2578         /* else */
2579         return descr->focus_item;
2580         /* otherwise, if the user tries to move the selection with the    */
2581         /* arrow keys, we will give the application something to choke on */
2582     case LB_GETTOPINDEX16:
2583     case LB_GETTOPINDEX:
2584         return descr->top_item;
2585
2586     case LB_GETITEMHEIGHT16:
2587     case LB_GETITEMHEIGHT:
2588         return LISTBOX_GetItemHeight( wnd, descr, wParam );
2589
2590     case LB_SETITEMHEIGHT16:
2591         lParam = LOWORD(lParam);
2592         /* fall through */
2593     case LB_SETITEMHEIGHT:
2594         return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2595
2596     case LB_ITEMFROMPOINT:
2597         {
2598             POINT pt;
2599             RECT rect;
2600
2601             pt.x = LOWORD(lParam);
2602             pt.y = HIWORD(lParam);
2603             rect.left = 0;
2604             rect.top = 0;
2605             rect.right = descr->width;
2606             rect.bottom = descr->height;
2607
2608             return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2609                              !PtInRect( &rect, pt ) );
2610         }
2611
2612     case LB_SETCARETINDEX16:
2613     case LB_SETCARETINDEX:
2614         if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2615         if (LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam ) == LB_ERR)
2616             return LB_ERR;
2617         else if (ISWIN31)
2618              return wParam;
2619         else
2620              return LB_OKAY;
2621
2622     case LB_GETCARETINDEX16:
2623     case LB_GETCARETINDEX:
2624         return descr->focus_item;
2625
2626     case LB_SETTOPINDEX16:
2627     case LB_SETTOPINDEX:
2628         return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2629
2630     case LB_SETCOLUMNWIDTH16:
2631     case LB_SETCOLUMNWIDTH:
2632         return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2633
2634     case LB_GETITEMRECT16:
2635         {
2636             RECT rect;
2637             ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2638             CONV_RECT32TO16( &rect, MapSL(lParam) );
2639         }
2640         return ret;
2641
2642     case LB_GETITEMRECT:
2643         return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2644
2645     case LB_FINDSTRING16:
2646         wParam = (INT)(INT16)wParam;
2647         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2648         /* fall through */
2649     case LB_FINDSTRING:
2650         return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2651
2652     case LB_FINDSTRINGEXACT16:
2653         wParam = (INT)(INT16)wParam;
2654         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2655         /* fall through */
2656     case LB_FINDSTRINGEXACT:
2657         return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2658
2659     case LB_SELECTSTRING16:
2660         wParam = (INT)(INT16)wParam;
2661         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2662         /* fall through */
2663     case LB_SELECTSTRING:
2664         {
2665             INT index = LISTBOX_FindString( wnd, descr, wParam,
2666                                               (LPCSTR)lParam, FALSE );
2667             if (index == LB_ERR)
2668                 return LB_ERR;
2669             LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2670             return index;
2671         }
2672
2673     case LB_GETSEL16:
2674         wParam = (INT)(INT16)wParam;
2675         /* fall through */
2676     case LB_GETSEL:
2677         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2678             return LB_ERR;
2679         return descr->items[wParam].selected;
2680
2681     case LB_SETSEL16:
2682         lParam = (INT)(INT16)lParam;
2683         /* fall through */
2684     case LB_SETSEL:
2685         return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2686
2687     case LB_SETCURSEL16:
2688         wParam = (INT)(INT16)wParam;
2689         /* fall through */
2690     case LB_SETCURSEL:
2691         if (IS_MULTISELECT(descr)) return LB_ERR;
2692         LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );  
2693         return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2694
2695     case LB_GETSELCOUNT16:
2696     case LB_GETSELCOUNT:
2697         return LISTBOX_GetSelCount( wnd, descr );
2698
2699     case LB_GETSELITEMS16:
2700         return LISTBOX_GetSelItems16( wnd, descr, wParam,
2701                                       (LPINT16)MapSL(lParam) );
2702
2703     case LB_GETSELITEMS:
2704         return LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2705
2706     case LB_SELITEMRANGE16:
2707     case LB_SELITEMRANGE:
2708         if (LOWORD(lParam) <= HIWORD(lParam))
2709             return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2710                                             HIWORD(lParam), wParam );
2711         else
2712             return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2713                                             LOWORD(lParam), wParam );
2714
2715     case LB_SELITEMRANGEEX16:
2716     case LB_SELITEMRANGEEX:
2717         if ((INT)lParam >= (INT)wParam)
2718             return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2719         else
2720             return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2721
2722     case LB_GETHORIZONTALEXTENT16:
2723     case LB_GETHORIZONTALEXTENT:
2724         return descr->horz_extent;
2725
2726     case LB_SETHORIZONTALEXTENT16:
2727     case LB_SETHORIZONTALEXTENT:
2728         return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2729
2730     case LB_GETANCHORINDEX16:
2731     case LB_GETANCHORINDEX:
2732         return descr->anchor_item;
2733
2734     case LB_SETANCHORINDEX16:
2735         wParam = (INT)(INT16)wParam;
2736         /* fall through */
2737     case LB_SETANCHORINDEX:
2738         if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2739             return LB_ERR;
2740         descr->anchor_item = (INT)wParam;
2741         return LB_OKAY;
2742
2743     case LB_DIR16:
2744         /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2745          * be set automatically (this is different in Win32) */
2746         if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2747         return LISTBOX_Directory( wnd, descr, wParam, MapSL(lParam), FALSE );
2748
2749     case LB_DIR:
2750         return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2751
2752     case LB_GETLOCALE:
2753         return descr->locale;
2754
2755     case LB_SETLOCALE:
2756         descr->locale = (LCID)wParam;  /* FIXME: should check for valid lcid */
2757         return LB_OKAY;
2758
2759     case LB_INITSTORAGE:
2760         return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2761
2762     case LB_SETCOUNT:
2763         return LISTBOX_SetCount( wnd, descr, (INT)wParam );
2764
2765     case LB_SETTABSTOPS16:
2766         return LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2767
2768     case LB_SETTABSTOPS:
2769         return LISTBOX_SetTabStops( wnd, descr, wParam, (LPINT)lParam, FALSE );
2770
2771     case LB_CARETON16:
2772     case LB_CARETON:
2773         if (descr->caret_on)
2774             return LB_OKAY;
2775         descr->caret_on = TRUE;
2776         if ((descr->focus_item != -1) && (descr->in_focus))
2777             LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2778         return LB_OKAY;
2779
2780     case LB_CARETOFF16:
2781     case LB_CARETOFF:
2782         if (!descr->caret_on)
2783             return LB_OKAY;
2784         descr->caret_on = FALSE;
2785         if ((descr->focus_item != -1) && (descr->in_focus))
2786             LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2787         return LB_OKAY;
2788         
2789     case WM_DESTROY:
2790         return LISTBOX_Destroy( wnd, descr );
2791
2792     case WM_ENABLE:
2793         InvalidateRect( hwnd, NULL, TRUE );
2794         return 0;
2795
2796     case WM_SETREDRAW:
2797         LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2798         return 0;
2799
2800     case WM_GETDLGCODE:
2801         return DLGC_WANTARROWS | DLGC_WANTCHARS;
2802
2803     case WM_PAINT:
2804         {
2805             PAINTSTRUCT ps;
2806             HDC hdc = ( wParam ) ? ((HDC)wParam)
2807                                    :  BeginPaint( hwnd, &ps );
2808             ret = LISTBOX_Paint( wnd, descr, hdc );
2809             if( !wParam ) EndPaint( hwnd, &ps );
2810         }
2811         return ret;
2812     case WM_SIZE:
2813         LISTBOX_UpdateSize( wnd, descr );
2814         return 0;
2815     case WM_GETFONT:
2816         return descr->font;
2817     case WM_SETFONT:
2818         LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2819         if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2820         return 0;
2821     case WM_SETFOCUS:
2822         descr->in_focus = TRUE;
2823         descr->caret_on = TRUE;
2824         if (descr->focus_item != -1)
2825             LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2826         SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2827         return 0;
2828     case WM_KILLFOCUS:
2829         descr->in_focus = FALSE;
2830         if ((descr->focus_item != -1) && descr->caret_on)
2831             LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2832         SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2833         return 0;
2834     case WM_HSCROLL:
2835         return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2836     case WM_VSCROLL:
2837         return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2838     case WM_MOUSEACTIVATE:
2839         return MA_NOACTIVATE;
2840     case WM_MOUSEWHEEL:
2841         if (wParam & (MK_SHIFT | MK_CONTROL))
2842             return DefWindowProcA( hwnd, msg, wParam, lParam );
2843         return LISTBOX_HandleMouseWheel( wnd, descr, wParam, lParam );
2844     case WM_LBUTTONDOWN:
2845         return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2846                                           (INT16)LOWORD(lParam),
2847                                           (INT16)HIWORD(lParam) );
2848     case WM_LBUTTONDBLCLK:
2849         if (descr->style & LBS_NOTIFY)
2850             SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2851         return 0;
2852     case WM_MOUSEMOVE:
2853         if (GetCapture() == hwnd)
2854             LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2855                                      (INT16)HIWORD(lParam) );
2856         return 0;
2857     case WM_LBUTTONUP:
2858         return LISTBOX_HandleLButtonUp( wnd, descr );
2859     case WM_KEYDOWN:
2860         return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2861     case WM_CHAR:
2862         return LISTBOX_HandleChar( wnd, descr, wParam );
2863     case WM_SYSTIMER:
2864         return LISTBOX_HandleSystemTimer( wnd, descr );
2865     case WM_ERASEBKGND:
2866         if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
2867         {
2868             RECT rect;
2869             HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2870                                               wParam, (LPARAM)wnd->hwndSelf );
2871             GetClientRect(hwnd, &rect);
2872             if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2873         }
2874         return 1;
2875     case WM_DROPFILES:
2876         if( !descr->lphc ) 
2877             return SendMessageA( descr->owner, msg, wParam, lParam );
2878         break;
2879
2880     case WM_DROPOBJECT:
2881     case WM_QUERYDROPOBJECT:
2882     case WM_DRAGSELECT:
2883     case WM_DRAGMOVE:
2884         if( !descr->lphc )
2885         {
2886             LPDRAGINFO16 dragInfo = MapSL( lParam );
2887             dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2888                                                 dragInfo->pt.y );
2889             return SendMessageA( descr->owner, msg, wParam, lParam );
2890         }
2891         break;
2892
2893     default:
2894         if ((msg >= WM_USER) && (msg < 0xc000))
2895             WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2896                          hwnd, msg, wParam, lParam );
2897         return DefWindowProcA( hwnd, msg, wParam, lParam );
2898     }
2899     return 0;
2900 }
2901
2902 /***********************************************************************
2903  *           ListBoxWndProcA
2904  *
2905  * This is just a wrapper for the real wndproc, it only does window locking
2906  * and unlocking.
2907  */
2908 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
2909 {
2910     WND*        wndPtr = WIN_FindWndPtr( hwnd );
2911     LRESULT     res = ListBoxWndProc_locked(wndPtr,msg,wParam,lParam);
2912
2913     WIN_ReleaseWndPtr(wndPtr);
2914     return res;
2915 }
2916
2917 /***********************************************************************
2918  *           COMBO_Directory
2919  */
2920 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2921 {
2922     WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2923
2924     if( wnd )
2925     {
2926         LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2927         if( descr )
2928         {
2929             LRESULT     lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2930
2931             RedrawWindow( lphc->self->hwndSelf, NULL, 0, 
2932                             RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2933             WIN_ReleaseWndPtr(wnd);
2934             return lRet;
2935         }
2936         WIN_ReleaseWndPtr(wnd);
2937     }
2938     return CB_ERR;
2939 }
2940
2941 /***********************************************************************
2942  *           ComboLBWndProc_locked
2943  *
2944  * The real combo listbox wndproc, but called with locked WND struct.
2945  */
2946 static inline LRESULT WINAPI ComboLBWndProc_locked( WND* wnd, UINT msg,
2947                                WPARAM wParam, LPARAM lParam )
2948 {
2949     LRESULT lRet = 0;
2950     HWND hwnd = wnd->hwndSelf;
2951
2952     if (wnd)
2953     {
2954         LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2955
2956         TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
2957                      wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2958
2959         if( descr || msg == WM_CREATE )
2960         {
2961             LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2962
2963             switch( msg )
2964             {
2965                 case WM_CREATE:
2966 #define lpcs    ((LPCREATESTRUCTA)lParam)
2967                      TRACE_(combo)("\tpassed parent handle = 0x%08x\n", 
2968                                   (UINT)lpcs->lpCreateParams);
2969
2970                      lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2971 #undef  lpcs
2972                      return LISTBOX_Create( wnd, lphc );
2973                 case WM_MOUSEMOVE:
2974                      if ( (TWEAK_WineLook > WIN31_LOOK) &&
2975                           (CB_GETTYPE(lphc) != CBS_SIMPLE) )
2976                      {
2977                        POINT   mousePos;
2978                        BOOL    captured;
2979                        RECT    clientRect;
2980
2981                        mousePos.x = (INT16)LOWORD(lParam);
2982                        mousePos.y = (INT16)HIWORD(lParam);
2983
2984                        /*
2985                         * If we are in a dropdown combobox, we simulate that
2986                         * the mouse is captured to show the tracking of the item.
2987                         */
2988                        GetClientRect(hwnd, &clientRect);
2989
2990                        if (PtInRect( &clientRect, mousePos ))
2991                        {
2992                            captured = descr->captured;
2993                            descr->captured = TRUE;                       
2994                            
2995                            LISTBOX_HandleMouseMove( wnd, descr, 
2996                                                     mousePos.x, mousePos.y);
2997
2998                            descr->captured = captured;
2999
3000                        }
3001                        else
3002                        {
3003                            LISTBOX_HandleMouseMove( wnd, descr, 
3004                                                     mousePos.x, mousePos.y);
3005                        }
3006
3007                        return 0;
3008
3009                      }
3010                      else
3011                      {
3012                        /*
3013                         * If we are in Win3.1 look, go with the default behavior.
3014                         */
3015                        return ListBoxWndProcA( hwnd, msg, wParam, lParam );
3016                      }
3017                 case WM_LBUTTONUP:
3018                      if (TWEAK_WineLook > WIN31_LOOK)
3019                      {
3020                        POINT mousePos;
3021                        RECT  clientRect;
3022
3023                        /*
3024                         * If the mouse button "up" is not in the listbox,
3025                         * we make sure there is no selection by re-selecting the
3026                         * item that was selected when the listbox was made visible.
3027                         */
3028                        mousePos.x = (INT16)LOWORD(lParam);
3029                        mousePos.y = (INT16)HIWORD(lParam);
3030
3031                        GetClientRect(hwnd, &clientRect);
3032
3033                        /*
3034                         * When the user clicks outside the combobox and the focus
3035                         * is lost, the owning combobox will send a fake buttonup with
3036                         * 0xFFFFFFF as the mouse location, we must also revert the
3037                         * selection to the original selection.
3038                         */
3039                        if ( (lParam == 0xFFFFFFFF) ||
3040                             (!PtInRect( &clientRect, mousePos )) )
3041                        {
3042                          LISTBOX_MoveCaret( wnd,
3043                                             descr, 
3044                                             lphc->droppedIndex, 
3045                                             FALSE );
3046                        }
3047                      }
3048                      return LISTBOX_HandleLButtonUp( wnd, descr );
3049                 case WM_LBUTTONDBLCLK:
3050                 case WM_LBUTTONDOWN:
3051                      return LISTBOX_HandleLButtonDownCombo(wnd, descr, msg, wParam, 
3052                                           (INT16)LOWORD(lParam),
3053                                           (INT16)HIWORD(lParam) );
3054                 case WM_MOUSEACTIVATE:
3055                      return MA_NOACTIVATE;
3056                 case WM_NCACTIVATE:
3057                      return FALSE;
3058                 case WM_KEYDOWN:
3059                      if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3060                      {
3061                          /* for some reason(?) Windows makes it possible to
3062                           * show/hide ComboLBox by sending it WM_KEYDOWNs */
3063
3064                          if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3065                              ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3066                                && (wParam == VK_DOWN || wParam == VK_UP)) )
3067                          {
3068                              COMBO_FlipListbox( lphc, FALSE, FALSE );
3069                              return 0;
3070                          }
3071                      }
3072                      return LISTBOX_HandleKeyDown( wnd, descr, wParam );
3073
3074                 case LB_SETCURSEL16:
3075                 case LB_SETCURSEL:
3076                      lRet = ListBoxWndProcA( hwnd, msg, wParam, lParam );
3077                      lRet =(lRet == LB_ERR) ? lRet : descr->selected_item; 
3078                      return lRet;
3079                 case WM_NCDESTROY:
3080                      if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3081                          lphc->hWndLBox = 0;
3082                      /* fall through */
3083
3084                 default:
3085                     return ListBoxWndProcA( hwnd, msg, wParam, lParam );
3086             }
3087         }
3088         lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
3089
3090         TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
3091     }
3092     return lRet;
3093 }
3094
3095 /***********************************************************************
3096  *           ComboLBWndProc
3097  *
3098  *  NOTE: in Windows, winproc address of the ComboLBox is the same 
3099  *        as that of the Listbox.
3100  * 
3101  * This is just a wrapper for the real wndproc, it only does window locking
3102  * and unlocking.
3103  */
3104 LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg,
3105                                WPARAM wParam, LPARAM lParam )
3106 {
3107     WND *wnd = WIN_FindWndPtr( hwnd );
3108     LRESULT res = ComboLBWndProc_locked(wnd,msg,wParam,lParam);
3109
3110     WIN_ReleaseWndPtr(wnd);
3111     return res;
3112 }