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