Remove previously disabled code.
[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 "user.h"
20 #include "controls.h"
21 #include "debugtools.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         TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
757
758         if(unicode)
759         {
760             LPWSTR buffer = (LPWSTR)lParam;
761             strcpyW( buffer, descr->items[index].str );
762             return strlenW(buffer);
763         }
764         else
765         {
766             LPSTR buffer = (LPSTR)lParam;
767             return WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, buffer, 0x7FFFFFFF, NULL, NULL) - 1;
768         }
769     } else {
770         if (lParam)
771             *((LPDWORD)lParam)=*(LPDWORD)(&descr->items[index].data);
772         return sizeof(DWORD);
773     }
774 }
775
776
777 /***********************************************************************
778  *           LISTBOX_FindStringPos
779  *
780  * Find the nearest string located before a given string in sort order.
781  * If 'exact' is TRUE, return an error if we don't get an exact match.
782  */
783 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCWSTR str,
784                                     BOOL exact )
785 {
786     INT index, min, max, res = -1;
787
788     if (!(descr->style & LBS_SORT)) return -1;  /* Add it at the end */
789     min = 0;
790     max = descr->nb_items;
791     while (min != max)
792     {
793         index = (min + max) / 2;
794         if (HAS_STRINGS(descr))
795             res = lstrcmpiW( descr->items[index].str, str );
796         else
797         {
798             COMPAREITEMSTRUCT cis;
799
800             cis.CtlType    = ODT_LISTBOX;
801             cis.CtlID      = wnd->wIDmenu;
802             cis.hwndItem   = wnd->hwndSelf;
803             cis.itemID1    = index;
804             cis.itemData1  = descr->items[index].data;
805             cis.itemID2    = -1;
806             cis.itemData2  = (DWORD)str;
807             cis.dwLocaleId = descr->locale;
808             res = SendMessageW( descr->owner, WM_COMPAREITEM,
809                                   wnd->wIDmenu, (LPARAM)&cis );
810         }
811         if (!res) return index;
812         if (res > 0) max = index;
813         else min = index + 1;
814     }
815     return exact ? -1 : max;
816 }
817
818
819 /***********************************************************************
820  *           LISTBOX_FindFileStrPos
821  *
822  * Find the nearest string located before a given string in directory
823  * sort order (i.e. first files, then directories, then drives).
824  */
825 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCWSTR str )
826 {
827     INT min, max, res = -1;
828
829     if (!HAS_STRINGS(descr))
830         return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
831     min = 0;
832     max = descr->nb_items;
833     while (min != max)
834     {
835         INT index = (min + max) / 2;
836         LPCWSTR p = descr->items[index].str;
837         if (*p == '[')  /* drive or directory */
838         {
839             if (*str != '[') res = -1;
840             else if (p[1] == '-')  /* drive */
841             {
842                 if (str[1] == '-') res = str[2] - p[2];
843                 else res = -1;
844             }
845             else  /* directory */
846             {
847                 if (str[1] == '-') res = 1;
848                 else res = lstrcmpiW( str, p );
849             }
850         }
851         else  /* filename */
852         {
853             if (*str == '[') res = 1;
854             else res = lstrcmpiW( str, p );
855         }
856         if (!res) return index;
857         if (res < 0) max = index;
858         else min = index + 1;
859     }
860     return max;
861 }
862
863
864 /***********************************************************************
865  *           LISTBOX_FindString
866  *
867  * Find the item beginning with a given string.
868  */
869 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
870                                  LPCWSTR str, BOOL exact )
871 {
872     INT i;
873     LB_ITEMDATA *item;
874
875     if (start >= descr->nb_items) start = -1;
876     item = descr->items + start + 1;
877     if (HAS_STRINGS(descr))
878     {
879         if (!str || ! str[0] ) return LB_ERR;
880         if (exact)
881         {
882             for (i = start + 1; i < descr->nb_items; i++, item++)
883                 if (!lstrcmpiW( str, item->str )) return i;
884             for (i = 0, item = descr->items; i <= start; i++, item++)
885                 if (!lstrcmpiW( str, item->str )) return i;
886         }
887         else
888         {
889  /* Special case for drives and directories: ignore prefix */
890 #define CHECK_DRIVE(item) \
891     if ((item)->str[0] == '[') \
892     { \
893         if (!strncmpiW( str, (item)->str+1, len )) return i; \
894         if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
895         return i; \
896     }
897
898             INT len = strlenW(str);
899             for (i = start + 1; i < descr->nb_items; i++, item++)
900             {
901                if (!strncmpiW( str, item->str, len )) return i;
902                CHECK_DRIVE(item);
903             }
904             for (i = 0, item = descr->items; i <= start; i++, item++)
905             {
906                if (!strncmpiW( str, item->str, len )) return i;
907                CHECK_DRIVE(item);
908             }
909 #undef CHECK_DRIVE
910         }
911     }
912     else
913     {
914         if (exact && (descr->style & LBS_SORT))
915             /* If sorted, use a WM_COMPAREITEM binary search */
916             return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
917
918         /* Otherwise use a linear search */
919         for (i = start + 1; i < descr->nb_items; i++, item++)
920             if (item->data == (DWORD)str) return i;
921         for (i = 0, item = descr->items; i <= start; i++, item++)
922             if (item->data == (DWORD)str) return i;
923     }
924     return LB_ERR;
925 }
926
927
928 /***********************************************************************
929  *           LISTBOX_GetSelCount
930  */
931 static LRESULT LISTBOX_GetSelCount( LB_DESCR *descr )
932 {
933     INT i, count;
934     LB_ITEMDATA *item = descr->items;
935
936     if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
937     for (i = count = 0; i < descr->nb_items; i++, item++)
938         if (item->selected) count++;
939     return count;
940 }
941
942
943 /***********************************************************************
944  *           LISTBOX_GetSelItems16
945  */
946 static LRESULT LISTBOX_GetSelItems16( LB_DESCR *descr, INT16 max, LPINT16 array )
947 {
948     INT i, count;
949     LB_ITEMDATA *item = descr->items;
950
951     if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
952     for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
953         if (item->selected) array[count++] = (INT16)i;
954     return count;
955 }
956
957
958 /***********************************************************************
959  *           LISTBOX_GetSelItems
960  */
961 static LRESULT LISTBOX_GetSelItems( LB_DESCR *descr, INT max, LPINT array )
962 {
963     INT i, count;
964     LB_ITEMDATA *item = descr->items;
965
966     if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
967     for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
968         if (item->selected) array[count++] = i;
969     return count;
970 }
971
972
973 /***********************************************************************
974  *           LISTBOX_Paint
975  */
976 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
977 {
978     INT i, col_pos = descr->page_size - 1;
979     RECT rect;
980     RECT focusRect = {-1, -1, -1, -1};
981     HFONT oldFont = 0;
982     HBRUSH hbrush, oldBrush = 0;
983
984     if (descr->style & LBS_NOREDRAW) return 0;
985
986     SetRect( &rect, 0, 0, descr->width, descr->height );
987     if (descr->style & LBS_MULTICOLUMN)
988         rect.right = rect.left + descr->column_width;
989     else if (descr->horz_pos)
990     {
991         SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
992         rect.right += descr->horz_pos;
993     }
994
995     if (descr->font) oldFont = SelectObject( hdc, descr->font );
996     hbrush = SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
997                              hdc, (LPARAM)wnd->hwndSelf );
998     if (hbrush) oldBrush = SelectObject( hdc, hbrush );
999     if (wnd->dwStyle & WS_DISABLED)
1000         SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1001
1002     if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1003         (descr->in_focus))
1004     {
1005         /* Special case for empty listbox: paint focus rect */
1006         rect.bottom = rect.top + descr->item_height;
1007         LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
1008                            ODA_FOCUS, FALSE );
1009         rect.top = rect.bottom;
1010     }
1011
1012     /* Paint all the item, regarding the selection
1013        Focus state will be painted after  */
1014
1015     for (i = descr->top_item; i < descr->nb_items; i++)
1016     {
1017         if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1018             rect.bottom = rect.top + descr->item_height;
1019         else
1020             rect.bottom = rect.top + descr->items[i].height;
1021
1022         if (i == descr->focus_item)
1023         {
1024             /* keep the focus rect, to paint the focus item after */
1025             focusRect.left = rect.left;
1026             focusRect.right = rect.right;
1027             focusRect.top = rect.top;
1028             focusRect.bottom = rect.bottom;
1029         }
1030         LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1031         rect.top = rect.bottom;
1032
1033         if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1034         {
1035             if (!IS_OWNERDRAW(descr))
1036             {
1037                 /* Clear the bottom of the column */
1038                 if (rect.top < descr->height)
1039                 {
1040                     rect.bottom = descr->height;
1041                     ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1042                                    &rect, NULL, 0, NULL );
1043                 }
1044             }
1045
1046             /* Go to the next column */
1047             rect.left += descr->column_width;
1048             rect.right += descr->column_width;
1049             rect.top = 0;
1050             col_pos = descr->page_size - 1;
1051         }
1052         else
1053         {
1054             col_pos--;
1055             if (rect.top >= descr->height) break;
1056         }
1057     }
1058
1059     /* Paint the focus item now */
1060     if (focusRect.top != focusRect.bottom && descr->caret_on)
1061         LISTBOX_PaintItem( wnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1062
1063     if (!IS_OWNERDRAW(descr))
1064     {
1065         /* Clear the remainder of the client area */
1066         if (rect.top < descr->height)
1067         {
1068             rect.bottom = descr->height;
1069             ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1070                            &rect, NULL, 0, NULL );
1071         }
1072         if (rect.right < descr->width)
1073         {
1074             rect.left   = rect.right;
1075             rect.right  = descr->width;
1076             rect.top    = 0;
1077             rect.bottom = descr->height;
1078             ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1079                            &rect, NULL, 0, NULL );
1080         }
1081     }
1082     if (oldFont) SelectObject( hdc, oldFont );
1083     if (oldBrush) SelectObject( hdc, oldBrush );
1084     return 0;
1085 }
1086
1087
1088 /***********************************************************************
1089  *           LISTBOX_InvalidateItems
1090  *
1091  * Invalidate all items from a given item. If the specified item is not
1092  * visible, nothing happens.
1093  */
1094 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
1095 {
1096     RECT rect;
1097
1098     if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1099     {
1100         if (descr->style & LBS_NOREDRAW)
1101         {
1102             descr->style |= LBS_DISPLAYCHANGED;
1103             return;
1104         }
1105         rect.bottom = descr->height;
1106         InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1107         if (descr->style & LBS_MULTICOLUMN)
1108         {
1109             /* Repaint the other columns */
1110             rect.left  = rect.right;
1111             rect.right = descr->width;
1112             rect.top   = 0;
1113             InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1114         }
1115     }
1116 }
1117
1118
1119 /***********************************************************************
1120  *           LISTBOX_GetItemHeight
1121  */
1122 static LRESULT LISTBOX_GetItemHeight( LB_DESCR *descr, INT index )
1123 {
1124     if (descr->style & LBS_OWNERDRAWVARIABLE)
1125     {
1126         if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1127         return descr->items[index].height;
1128     }
1129     else return descr->item_height;
1130 }
1131
1132
1133 /***********************************************************************
1134  *           LISTBOX_SetItemHeight
1135  */
1136 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1137                                       INT height )
1138 {
1139     if (!height) height = 1;
1140
1141     if (descr->style & LBS_OWNERDRAWVARIABLE)
1142     {
1143         if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1144         TRACE("[%04x]: item %d height = %d\n",
1145                      wnd->hwndSelf, index, height );
1146         descr->items[index].height = height;
1147         LISTBOX_UpdateScroll( wnd, descr );
1148         LISTBOX_InvalidateItems( wnd, descr, index );
1149     }
1150     else if (height != descr->item_height)
1151     {
1152         TRACE("[%04x]: new height = %d\n",
1153                      wnd->hwndSelf, height );
1154         descr->item_height = height;
1155         LISTBOX_UpdatePage( wnd, descr );
1156         LISTBOX_UpdateScroll( wnd, descr );
1157         InvalidateRect( wnd->hwndSelf, 0, TRUE );
1158     }
1159     return LB_OKAY;
1160 }
1161
1162
1163 /***********************************************************************
1164  *           LISTBOX_SetHorizontalPos
1165  */
1166 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1167 {
1168     INT diff;
1169
1170     if (pos > descr->horz_extent - descr->width)
1171         pos = descr->horz_extent - descr->width;
1172     if (pos < 0) pos = 0;
1173     if (!(diff = descr->horz_pos - pos)) return;
1174     TRACE("[%04x]: new horz pos = %d\n",
1175                  wnd->hwndSelf, pos );
1176     descr->horz_pos = pos;
1177     LISTBOX_UpdateScroll( wnd, descr );
1178     if (abs(diff) < descr->width)
1179         ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1180                           SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1181     else
1182         InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1183 }
1184
1185
1186 /***********************************************************************
1187  *           LISTBOX_SetHorizontalExtent
1188  */
1189 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1190                                             INT extent )
1191 {
1192     if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1193         return LB_OKAY;
1194     if (extent <= 0) extent = 1;
1195     if (extent == descr->horz_extent) return LB_OKAY;
1196     TRACE("[%04x]: new horz extent = %d\n",
1197                  wnd->hwndSelf, extent );
1198     descr->horz_extent = extent;
1199     if (descr->horz_pos > extent - descr->width)
1200         LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1201     else
1202         LISTBOX_UpdateScroll( wnd, descr );
1203     return LB_OKAY;
1204 }
1205
1206
1207 /***********************************************************************
1208  *           LISTBOX_SetColumnWidth
1209  */
1210 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, INT width)
1211 {
1212     if (width == descr->column_width) return LB_OKAY;
1213     TRACE("[%04x]: new column width = %d\n",
1214                  wnd->hwndSelf, width );
1215     descr->column_width = width;
1216     LISTBOX_UpdatePage( wnd, descr );
1217     return LB_OKAY;
1218 }
1219
1220
1221 /***********************************************************************
1222  *           LISTBOX_SetFont
1223  *
1224  * Returns the item height.
1225  */
1226 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1227 {
1228     HDC hdc;
1229     HFONT oldFont = 0;
1230     TEXTMETRICW tm;
1231
1232     descr->font = font;
1233
1234     if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1235     {
1236         ERR("unable to get DC.\n" );
1237         return 16;
1238     }
1239     if (font) oldFont = SelectObject( hdc, font );
1240     GetTextMetricsW( hdc, &tm );
1241     if (oldFont) SelectObject( hdc, oldFont );
1242     ReleaseDC( wnd->hwndSelf, hdc );
1243     if (!IS_OWNERDRAW(descr))
1244         LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1245     return tm.tmHeight ;
1246 }
1247
1248
1249 /***********************************************************************
1250  *           LISTBOX_MakeItemVisible
1251  *
1252  * Make sure that a given item is partially or fully visible.
1253  */
1254 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1255                                      BOOL fully )
1256 {
1257     INT top;
1258
1259     if (index <= descr->top_item) top = index;
1260     else if (descr->style & LBS_MULTICOLUMN)
1261     {
1262         INT cols = descr->width;
1263         if (!fully) cols += descr->column_width - 1;
1264         if (cols >= descr->column_width) cols /= descr->column_width;
1265         else cols = 1;
1266         if (index < descr->top_item + (descr->page_size * cols)) return;
1267         top = index - descr->page_size * (cols - 1);
1268     }
1269     else if (descr->style & LBS_OWNERDRAWVARIABLE)
1270     {
1271         INT height = fully ? descr->items[index].height : 1;
1272         for (top = index; top > descr->top_item; top--)
1273             if ((height += descr->items[top-1].height) > descr->height) break;
1274     }
1275     else
1276     {
1277         if (index < descr->top_item + descr->page_size) return;
1278         if (!fully && (index == descr->top_item + descr->page_size) &&
1279             (descr->height > (descr->page_size * descr->item_height))) return;
1280         top = index - descr->page_size + 1;
1281     }
1282     LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1283 }
1284
1285 /***********************************************************************
1286  *           LISTBOX_SetCaretIndex
1287  *
1288  * NOTES
1289  *   index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1290  *
1291  */
1292 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1293                                       BOOL fully_visible )
1294 {
1295     INT oldfocus = descr->focus_item;          
1296
1297     if (descr->style & LBS_NOSEL) return LB_ERR;
1298     if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1299     if (index == oldfocus) return LB_OKAY;
1300     descr->focus_item = index;
1301     if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1302         LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1303
1304     LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1305     if (descr->caret_on && (descr->in_focus))
1306         LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1307
1308     return LB_OKAY;
1309 }
1310
1311
1312 /***********************************************************************
1313  *           LISTBOX_SelectItemRange
1314  *
1315  * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1316  */
1317 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1318                                         INT last, BOOL on )
1319 {
1320     INT i;
1321
1322     /* A few sanity checks */
1323
1324     if (descr->style & LBS_NOSEL) return LB_ERR;
1325     if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1326     if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1327     if (last == -1) last = descr->nb_items - 1;
1328     if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1329     if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1330     /* selected_item reflects last selected/unselected item on multiple sel */
1331     descr->selected_item = last;
1332
1333     if (on)  /* Turn selection on */
1334     {
1335         for (i = first; i <= last; i++)
1336         {
1337             if (descr->items[i].selected) continue;
1338             descr->items[i].selected = TRUE;
1339             LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1340         }
1341         LISTBOX_SetCaretIndex( wnd, descr, last, TRUE );
1342     }
1343     else  /* Turn selection off */
1344     {
1345         for (i = first; i <= last; i++)
1346         {
1347             if (!descr->items[i].selected) continue;
1348             descr->items[i].selected = FALSE;
1349             LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1350         }
1351     }
1352     return LB_OKAY;
1353 }
1354
1355 /***********************************************************************
1356  *           LISTBOX_SetSelection
1357  */
1358 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1359                                      BOOL on, BOOL send_notify )
1360 {
1361     TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1362
1363     if (descr->style & LBS_NOSEL) return LB_ERR;
1364     if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1365     if (descr->style & LBS_MULTIPLESEL)
1366     {
1367         if (index == -1)  /* Select all items */
1368             return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1369         else  /* Only one item */
1370             return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1371     }
1372     else
1373     {
1374         INT oldsel = descr->selected_item;
1375         if (index == oldsel) return LB_OKAY;
1376         if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1377         if (index != -1) descr->items[index].selected = TRUE;
1378         descr->selected_item = index;
1379         if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT );
1380         if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1381         if (send_notify && descr->nb_items) SEND_NOTIFICATION( wnd, descr,
1382                                (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1383         else
1384             if( descr->lphc ) /* set selection change flag for parent combo */
1385                 descr->lphc->wState |= CBF_SELCHANGE;
1386     }
1387     return LB_OKAY;
1388 }
1389
1390
1391 /***********************************************************************
1392  *           LISTBOX_MoveCaret
1393  *
1394  * Change the caret position and extend the selection to the new caret.
1395  */
1396 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1397                                BOOL fully_visible )
1398 {
1399     INT oldfocus = descr->focus_item;          
1400
1401     if ((index <  0) || (index >= descr->nb_items)) 
1402         return;
1403
1404     /* Important, repaint needs to be done in this order if
1405        you want to mimic Windows behavior:
1406        1. Remove the focus and paint the item  
1407        2. Remove the selection and paint the item(s)
1408        3. Set the selection and repaint the item(s)
1409        4. Set the focus to 'index' and repaint the item */
1410
1411     /* 1. remove the focus and repaint the item */
1412     descr->focus_item = -1;
1413     if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1414         LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1415
1416     /* 2. then turn off the previous selection */
1417     /* 3. repaint the new selected item */
1418     if (descr->style & LBS_EXTENDEDSEL)
1419     {
1420         if (descr->anchor_item != -1)
1421         {
1422             INT first = min( index, descr->anchor_item );
1423             INT last  = max( index, descr->anchor_item );
1424             if (first > 0)
1425                 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1426             LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1427             LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1428         }
1429     }
1430     else if (!(descr->style & LBS_MULTIPLESEL))
1431     {
1432         /* Set selection to new caret item */
1433         LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1434     }
1435    
1436     /* 4. repaint the new item with the focus */
1437     descr->focus_item = index;
1438     LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1439     if (descr->caret_on && (descr->in_focus))
1440         LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1441 }
1442
1443
1444 /***********************************************************************
1445  *           LISTBOX_InsertItem
1446  */
1447 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1448                                    LPWSTR str, DWORD data )
1449 {
1450     LB_ITEMDATA *item;
1451     INT max_items;
1452     INT oldfocus = descr->focus_item;
1453
1454     if (index == -1) index = descr->nb_items;
1455     else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1456     if (!descr->items) max_items = 0;
1457     else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1458     if (descr->nb_items == max_items)
1459     {
1460         /* We need to grow the array */
1461         max_items += LB_ARRAY_GRANULARITY;
1462         if (!(item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1463                                   max_items * sizeof(LB_ITEMDATA) )))
1464         {
1465             SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1466             return LB_ERRSPACE;
1467         }
1468         descr->items = item;
1469     }
1470
1471     /* Insert the item structure */
1472
1473     item = &descr->items[index];
1474     if (index < descr->nb_items)
1475         RtlMoveMemory( item + 1, item,
1476                        (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1477     item->str      = str;
1478     item->data     = data;
1479     item->height   = 0;
1480     item->selected = FALSE;
1481     descr->nb_items++;
1482
1483     /* Get item height */
1484
1485     if (descr->style & LBS_OWNERDRAWVARIABLE)
1486     {
1487         MEASUREITEMSTRUCT mis;
1488
1489         mis.CtlType    = ODT_LISTBOX;
1490         mis.CtlID      = wnd->wIDmenu;
1491         mis.itemID     = index;
1492         mis.itemData   = descr->items[index].data;
1493         mis.itemHeight = descr->item_height;
1494         SendMessageW( descr->owner, WM_MEASUREITEM, wnd->wIDmenu, (LPARAM)&mis );
1495         item->height = mis.itemHeight ? mis.itemHeight : 1;
1496         TRACE("[%04x]: measure item %d (%s) = %d\n",
1497               wnd->hwndSelf, index, str ? debugstr_w(str) : "", item->height );
1498     }
1499
1500     /* Repaint the items */
1501
1502     LISTBOX_UpdateScroll( wnd, descr );
1503     LISTBOX_InvalidateItems( wnd, descr, index );
1504
1505     /* Move selection and focused item */
1506     /* If listbox was empty, set focus to the first item */
1507     if (descr->nb_items == 1)
1508          LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1509     /* single select don't change selection index in win31 */
1510     else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1511     {
1512         descr->selected_item++;
1513         LISTBOX_SetSelection( wnd, descr, descr->selected_item-1, TRUE, FALSE );     
1514     }
1515     else
1516     {
1517         if (index <= descr->selected_item)
1518         {
1519            descr->selected_item++;
1520            descr->focus_item = oldfocus; /* focus not changed */
1521         }
1522     }
1523     return LB_OKAY;
1524 }
1525
1526
1527 /***********************************************************************
1528  *           LISTBOX_InsertString
1529  */
1530 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1531                                      LPCWSTR str )
1532 {
1533     LPWSTR new_str = NULL;
1534     DWORD data = 0;
1535     LRESULT ret;
1536
1537     if (HAS_STRINGS(descr))
1538     {
1539         static const WCHAR empty_stringW[] = { 0 };
1540         if (!str) str = empty_stringW;
1541         if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1542         {
1543             SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1544             return LB_ERRSPACE;
1545         }
1546         strcpyW(new_str, str);
1547     }
1548     else data = (DWORD)str;
1549
1550     if (index == -1) index = descr->nb_items;
1551     if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1552     {
1553         if (new_str) HeapFree( GetProcessHeap(), 0, new_str );
1554         return ret;
1555     }
1556
1557     TRACE("[%04x]: added item %d %s\n",
1558           wnd->hwndSelf, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1559     return index;
1560 }
1561
1562
1563 /***********************************************************************
1564  *           LISTBOX_DeleteItem
1565  *
1566  * Delete the content of an item. 'index' must be a valid index.
1567  */
1568 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1569 {
1570     /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1571      *       while Win95 sends it for all items with user data.
1572      *       It's probably better to send it too often than not
1573      *       often enough, so this is what we do here.
1574      */
1575     if (IS_OWNERDRAW(descr) || descr->items[index].data)
1576     {
1577         DELETEITEMSTRUCT dis;
1578
1579         dis.CtlType  = ODT_LISTBOX;
1580         dis.CtlID    = wnd->wIDmenu;
1581         dis.itemID   = index;
1582         dis.hwndItem = wnd->hwndSelf;
1583         dis.itemData = descr->items[index].data;
1584         SendMessageW( descr->owner, WM_DELETEITEM, wnd->wIDmenu, (LPARAM)&dis );
1585     }
1586     if (HAS_STRINGS(descr) && descr->items[index].str)
1587         HeapFree( GetProcessHeap(), 0, descr->items[index].str );
1588 }
1589
1590
1591 /***********************************************************************
1592  *           LISTBOX_RemoveItem
1593  *
1594  * Remove an item from the listbox and delete its content.
1595  */
1596 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1597 {
1598     LB_ITEMDATA *item;
1599     INT max_items;
1600
1601     if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1602     else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1603
1604     /* We need to invalidate the original rect instead of the updated one. */
1605     LISTBOX_InvalidateItems( wnd, descr, index );
1606
1607     LISTBOX_DeleteItem( wnd, descr, index );
1608
1609     /* Remove the item */
1610
1611     item = &descr->items[index];
1612     if (index < descr->nb_items-1)
1613         RtlMoveMemory( item, item + 1,
1614                        (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1615     descr->nb_items--;
1616     if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1617
1618     /* Shrink the item array if possible */
1619
1620     max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1621     if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1622     {
1623         max_items -= LB_ARRAY_GRANULARITY;
1624         item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1625                             max_items * sizeof(LB_ITEMDATA) );
1626         if (item) descr->items = item;
1627     }
1628     /* Repaint the items */
1629
1630     LISTBOX_UpdateScroll( wnd, descr );
1631     /* if we removed the scrollbar, reset the top of the list
1632       (correct for owner-drawn ???) */
1633     if (descr->nb_items == descr->page_size)
1634         LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1635
1636     /* Move selection and focused item */
1637     if (!IS_MULTISELECT(descr))
1638     {
1639         if (index == descr->selected_item)
1640             descr->selected_item = -1;
1641         else if (index < descr->selected_item)
1642     {
1643             descr->selected_item--;
1644             if (ISWIN31) /* win 31 do not change the selected item number */
1645                LISTBOX_SetSelection( wnd, descr, descr->selected_item + 1, TRUE, FALSE);
1646     }
1647     }
1648
1649     if (descr->focus_item >= descr->nb_items)
1650     {
1651           descr->focus_item = descr->nb_items - 1;
1652           if (descr->focus_item < 0) descr->focus_item = 0;
1653     }
1654     return LB_OKAY;
1655 }
1656
1657
1658 /***********************************************************************
1659  *           LISTBOX_ResetContent
1660  */
1661 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1662 {
1663     INT i;
1664
1665     for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1666     if (descr->items) HeapFree( GetProcessHeap(), 0, descr->items );
1667     descr->nb_items      = 0;
1668     descr->top_item      = 0;
1669     descr->selected_item = -1;
1670     descr->focus_item    = 0;
1671     descr->anchor_item   = -1;
1672     descr->items         = NULL;
1673 }
1674
1675
1676 /***********************************************************************
1677  *           LISTBOX_SetCount
1678  */
1679 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1680 {
1681     LRESULT ret;
1682
1683     if (HAS_STRINGS(descr)) return LB_ERR;
1684     /* FIXME: this is far from optimal... */
1685     if (count > descr->nb_items)
1686     {
1687         while (count > descr->nb_items)
1688             if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1689                 return ret;
1690     }
1691     else if (count < descr->nb_items)
1692     {
1693         while (count < descr->nb_items)
1694             if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1695                 return ret;
1696     }
1697     return LB_OKAY;
1698 }
1699
1700
1701 /***********************************************************************
1702  *           LISTBOX_Directory
1703  */
1704 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1705                                   LPCWSTR filespec, BOOL long_names )
1706 {
1707     HANDLE handle;
1708     LRESULT ret = LB_OKAY;
1709     WIN32_FIND_DATAW entry;
1710     int pos;
1711
1712     /* don't scan directory if we just want drives exclusively */
1713     if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1714         /* scan directory */
1715         if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1716         {
1717             if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1718         }
1719         else
1720         {
1721             do
1722             {
1723                 WCHAR buffer[270];
1724                 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1725                 {
1726                     static const WCHAR bracketW[]  = { ']',0 };
1727                     static const WCHAR dotW[] = { '.',0 };
1728                     if (!(attrib & DDL_DIRECTORY) ||
1729                         !strcmpW( entry.cAlternateFileName, dotW )) continue;
1730                     buffer[0] = '[';
1731                     if (long_names) strcpyW( buffer + 1, entry.cFileName );
1732                     else strcpyW( buffer + 1, entry.cAlternateFileName );
1733                     strcatW(buffer, bracketW);
1734                 }
1735                 else  /* not a directory */
1736                 {
1737 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1738                  FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1739
1740                     if ((attrib & DDL_EXCLUSIVE) &&
1741                         ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1742                         continue;
1743 #undef ATTRIBS
1744                     if (long_names) strcpyW( buffer, entry.cFileName );
1745                     else strcpyW( buffer, entry.cAlternateFileName );
1746                 }
1747                 if (!long_names) CharLowerW( buffer );
1748                 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1749                 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1750                     break;
1751             } while (FindNextFileW( handle, &entry ));
1752             FindClose( handle );
1753         }
1754     }
1755
1756     /* scan drives */
1757     if ((ret >= 0) && (attrib & DDL_DRIVES))
1758     {
1759         WCHAR buffer[] = {'[','-','a','-',']',0};
1760         WCHAR root[] = {'A',':','\\',0};
1761         int drive;
1762         for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1763         {
1764             if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1765             if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1766                 break;
1767         }
1768     }
1769     return ret;
1770 }
1771
1772
1773 /***********************************************************************
1774  *           LISTBOX_HandleVScroll
1775  */
1776 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr, WPARAM wParam )
1777 {
1778     SCROLLINFO info;
1779
1780     if (descr->style & LBS_MULTICOLUMN) return 0;
1781     switch(LOWORD(wParam))
1782     {
1783     case SB_LINEUP:
1784         LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1785         break;
1786     case SB_LINEDOWN:
1787         LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1788         break;
1789     case SB_PAGEUP:
1790         LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1791                             LISTBOX_GetCurrentPageSize( descr ), TRUE );
1792         break;
1793     case SB_PAGEDOWN:
1794         LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1795                             LISTBOX_GetCurrentPageSize( descr ), TRUE );
1796         break;
1797     case SB_THUMBPOSITION:
1798         LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1799         break;
1800     case SB_THUMBTRACK:
1801         info.cbSize = sizeof(info);
1802         info.fMask = SIF_TRACKPOS;
1803         GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1804         LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1805         break;
1806     case SB_TOP:
1807         LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1808         break;
1809     case SB_BOTTOM:
1810         LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1811         break;
1812     }
1813     return 0;
1814 }
1815
1816
1817 /***********************************************************************
1818  *           LISTBOX_HandleHScroll
1819  */
1820 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr, WPARAM wParam )
1821 {
1822     SCROLLINFO info;
1823     INT page;
1824
1825     if (descr->style & LBS_MULTICOLUMN)
1826     {
1827         switch(LOWORD(wParam))
1828         {
1829         case SB_LINELEFT:
1830             LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1831                                 TRUE );
1832             break;
1833         case SB_LINERIGHT:
1834             LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1835                                 TRUE );
1836             break;
1837         case SB_PAGELEFT:
1838             page = descr->width / descr->column_width;
1839             if (page < 1) page = 1;
1840             LISTBOX_SetTopItem( wnd, descr,
1841                              descr->top_item - page * descr->page_size, TRUE );
1842             break;
1843         case SB_PAGERIGHT:
1844             page = descr->width / descr->column_width;
1845             if (page < 1) page = 1;
1846             LISTBOX_SetTopItem( wnd, descr,
1847                              descr->top_item + page * descr->page_size, TRUE );
1848             break;
1849         case SB_THUMBPOSITION:
1850             LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1851                                 TRUE );
1852             break;
1853         case SB_THUMBTRACK:
1854             info.cbSize = sizeof(info);
1855             info.fMask  = SIF_TRACKPOS;
1856             GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1857             LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1858                                 TRUE );
1859             break;
1860         case SB_LEFT:
1861             LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1862             break;
1863         case SB_RIGHT:
1864             LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1865             break;
1866         }
1867     }
1868     else if (descr->horz_extent)
1869     {
1870         switch(LOWORD(wParam))
1871         {
1872         case SB_LINELEFT:
1873             LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1874             break;
1875         case SB_LINERIGHT:
1876             LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1877             break;
1878         case SB_PAGELEFT:
1879             LISTBOX_SetHorizontalPos( wnd, descr,
1880                                       descr->horz_pos - descr->width );
1881             break;
1882         case SB_PAGERIGHT:
1883             LISTBOX_SetHorizontalPos( wnd, descr,
1884                                       descr->horz_pos + descr->width );
1885             break;
1886         case SB_THUMBPOSITION:
1887             LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1888             break;
1889         case SB_THUMBTRACK:
1890             info.cbSize = sizeof(info);
1891             info.fMask = SIF_TRACKPOS;
1892             GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1893             LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1894             break;
1895         case SB_LEFT:
1896             LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1897             break;
1898         case SB_RIGHT:
1899             LISTBOX_SetHorizontalPos( wnd, descr,
1900                                       descr->horz_extent - descr->width );
1901             break;
1902         }
1903     }
1904     return 0;
1905 }
1906
1907 static LRESULT LISTBOX_HandleMouseWheel(WND *wnd, LB_DESCR *descr, WPARAM wParam )
1908 {
1909     short gcWheelDelta = 0;
1910     UINT pulScrollLines = 3;
1911
1912     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1913
1914     gcWheelDelta -= (short) HIWORD(wParam);
1915
1916     if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1917     {
1918         int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1919         cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1920         LISTBOX_SetTopItem( wnd, descr, descr->top_item + cLineScroll, TRUE );
1921     }
1922     return 0;
1923 }
1924
1925 /***********************************************************************
1926  *           LISTBOX_HandleLButtonDown
1927  */
1928 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1929                                           WPARAM wParam, INT x, INT y )
1930 {
1931     INT index = LISTBOX_GetItemFromPoint( descr, x, y );
1932     TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1933                  wnd->hwndSelf, x, y, index );
1934     if (!descr->caret_on && (descr->in_focus)) return 0;
1935
1936     if (!descr->in_focus)
1937     {
1938         if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1939         else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1940                                              : descr->lphc->self->hwndSelf );
1941     }
1942
1943     if (index == -1) return 0;
1944
1945     if (descr->style & LBS_EXTENDEDSEL)
1946     {
1947         /* we should perhaps make sure that all items are deselected
1948            FIXME: needed for !LBS_EXTENDEDSEL, too ?
1949            if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1950            LISTBOX_SetSelection( wnd, descr, -1, FALSE, FALSE);
1951         */
1952
1953         if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1954         if (wParam & MK_CONTROL)
1955         {
1956             LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1957             LISTBOX_SetSelection( wnd, descr, index,
1958                                   !descr->items[index].selected,
1959                                   (descr->style & LBS_NOTIFY) != 0);
1960         }
1961         else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1962     }
1963     else
1964     {
1965         LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1966         LISTBOX_SetSelection( wnd, descr, index,
1967                               (!(descr->style & LBS_MULTIPLESEL) ||
1968                                !descr->items[index].selected),
1969                               (descr->style & LBS_NOTIFY) != 0 );
1970     }
1971
1972     descr->captured = TRUE;
1973     SetCapture( wnd->hwndSelf );
1974
1975     if (!descr->lphc)
1976     {
1977         if (descr->style & LBS_NOTIFY )
1978             SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
1979                             MAKELPARAM( x, y ) );
1980         if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1981         {
1982             POINT pt;
1983             
1984             pt.x = x;
1985             pt.y = y;
1986
1987             if (DragDetect( wnd->hwndSelf, pt ))
1988                 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
1989         }
1990     }
1991     return 0;
1992 }
1993
1994
1995 /*************************************************************************
1996  * LISTBOX_HandleLButtonDownCombo [Internal] 
1997  *
1998  * Process LButtonDown message for the ComboListBox
1999  *
2000  * PARAMS
2001  *     pWnd       [I] The windows internal structure
2002  *     pDescr     [I] The ListBox internal structure
2003  *     wParam     [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2004  *     x          [I] X Mouse Coordinate
2005  *     y          [I] Y Mouse Coordinate
2006  *
2007  * RETURNS
2008  *     0 since we are processing the WM_LBUTTONDOWN Message
2009  *
2010  * NOTES
2011  *  This function is only to be used when a ListBox is a ComboListBox
2012  */
2013
2014 static LRESULT LISTBOX_HandleLButtonDownCombo( WND *pWnd, LB_DESCR *pDescr,
2015                                                UINT msg, WPARAM wParam, INT x, INT y)
2016 {
2017     RECT clientRect, screenRect;
2018     POINT mousePos;
2019
2020     mousePos.x = x;
2021     mousePos.y = y;
2022
2023     GetClientRect(pWnd->hwndSelf, &clientRect);
2024
2025     if(PtInRect(&clientRect, mousePos))
2026     {  
2027        /* MousePos is in client, resume normal processing */
2028         if (msg == WM_LBUTTONDOWN)
2029         {
2030            pDescr->lphc->droppedIndex = pDescr->nb_items ? pDescr->selected_item : -1;
2031            return LISTBOX_HandleLButtonDown( pWnd, pDescr, wParam, x, y);
2032         }
2033         else if (pDescr->style & LBS_NOTIFY)
2034             SEND_NOTIFICATION( pWnd, pDescr, LBN_DBLCLK );
2035         return 0;
2036     }
2037     else
2038     {
2039         POINT screenMousePos;
2040         HWND hWndOldCapture;
2041
2042         /* Check the Non-Client Area */
2043         screenMousePos = mousePos;
2044         hWndOldCapture = GetCapture();
2045         ReleaseCapture();
2046         GetWindowRect(pWnd->hwndSelf, &screenRect);
2047         ClientToScreen(pWnd->hwndSelf, &screenMousePos);
2048
2049         if(!PtInRect(&screenRect, screenMousePos))
2050         { 
2051             LISTBOX_SetCaretIndex( pWnd, pDescr, pDescr->lphc->droppedIndex, FALSE );
2052             LISTBOX_SetSelection( pWnd, pDescr, pDescr->lphc->droppedIndex, FALSE, FALSE );
2053             COMBO_FlipListbox( pDescr->lphc, FALSE, FALSE );
2054             return 0;
2055         }
2056         else
2057         {
2058             /* Check to see the NC is a scrollbar */
2059             INT nHitTestType=0;
2060             /* Check Vertical scroll bar */
2061             if (pWnd->dwStyle & WS_VSCROLL)
2062             {
2063                 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2064                 if (PtInRect( &clientRect, mousePos )) 
2065                 {
2066                     nHitTestType = HTVSCROLL;
2067                 }
2068             }
2069               /* Check horizontal scroll bar */
2070             if (pWnd->dwStyle & WS_HSCROLL)
2071             {
2072                 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2073                 if (PtInRect( &clientRect, mousePos ))
2074                 {
2075                     nHitTestType = HTHSCROLL;
2076                 }
2077             }
2078             /* Windows sends this message when a scrollbar is clicked 
2079              */
2080             
2081             if(nHitTestType != 0)
2082             {
2083                 SendMessageW(pWnd->hwndSelf, WM_NCLBUTTONDOWN, nHitTestType, 
2084                     MAKELONG(screenMousePos.x, screenMousePos.y));
2085             }
2086             /* Resume the Capture after scrolling is complete 
2087              */
2088             if(hWndOldCapture != 0)
2089             {
2090                 SetCapture(hWndOldCapture);
2091             }
2092         }
2093     }
2094     return 0;
2095 }
2096
2097 /***********************************************************************
2098  *           LISTBOX_HandleLButtonUp
2099  */
2100 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
2101 {
2102     if (LISTBOX_Timer != LB_TIMER_NONE)
2103         KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2104     LISTBOX_Timer = LB_TIMER_NONE;
2105     if (descr->captured)
2106     {
2107         descr->captured = FALSE;
2108         if (GetCapture() == wnd->hwndSelf) ReleaseCapture();
2109         if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2110             SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2111     }
2112     return 0;
2113 }
2114
2115
2116 /***********************************************************************
2117  *           LISTBOX_HandleTimer
2118  *
2119  * Handle scrolling upon a timer event.
2120  * Return TRUE if scrolling should continue.
2121  */
2122 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
2123                                     INT index, TIMER_DIRECTION dir )
2124 {
2125     switch(dir)
2126     {
2127     case LB_TIMER_UP:
2128         if (descr->top_item) index = descr->top_item - 1;
2129         else index = 0;
2130         break;
2131     case LB_TIMER_LEFT:
2132         if (descr->top_item) index -= descr->page_size;
2133         break;
2134     case LB_TIMER_DOWN:
2135         index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2136         if (index == descr->focus_item) index++;
2137         if (index >= descr->nb_items) index = descr->nb_items - 1;
2138         break;
2139     case LB_TIMER_RIGHT:
2140         if (index + descr->page_size < descr->nb_items)
2141             index += descr->page_size;
2142         break;
2143     case LB_TIMER_NONE:
2144         break;
2145     }
2146     if (index == descr->focus_item) return FALSE;
2147     LISTBOX_MoveCaret( wnd, descr, index, FALSE );
2148     return TRUE;
2149 }
2150
2151
2152 /***********************************************************************
2153  *           LISTBOX_HandleSystemTimer
2154  *
2155  * WM_SYSTIMER handler.
2156  */
2157 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
2158 {
2159     if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
2160     {
2161         KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2162         LISTBOX_Timer = LB_TIMER_NONE;
2163     }
2164     return 0;
2165 }
2166
2167
2168 /***********************************************************************
2169  *           LISTBOX_HandleMouseMove
2170  *
2171  * WM_MOUSEMOVE handler.
2172  */
2173 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
2174                                      INT x, INT y )
2175 {
2176     INT index;
2177     TIMER_DIRECTION dir = LB_TIMER_NONE;
2178
2179     if (!descr->captured) return;
2180
2181     if (descr->style & LBS_MULTICOLUMN)
2182     {
2183         if (y < 0) y = 0;
2184         else if (y >= descr->item_height * descr->page_size)
2185             y = descr->item_height * descr->page_size - 1;
2186
2187         if (x < 0)
2188         {
2189             dir = LB_TIMER_LEFT;
2190             x = 0;
2191         }
2192         else if (x >= descr->width)
2193         {
2194             dir = LB_TIMER_RIGHT;
2195             x = descr->width - 1;
2196         }
2197     }
2198     else
2199     {
2200         if (y < 0) dir = LB_TIMER_UP;  /* above */
2201         else if (y >= descr->height) dir = LB_TIMER_DOWN;  /* below */
2202     }
2203
2204     index = LISTBOX_GetItemFromPoint( descr, x, y );
2205     if (index == -1) index = descr->focus_item;
2206     if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
2207
2208     /* Start/stop the system timer */
2209
2210     if (dir != LB_TIMER_NONE)
2211         SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2212     else if (LISTBOX_Timer != LB_TIMER_NONE)
2213         KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2214     LISTBOX_Timer = dir;
2215 }
2216
2217
2218 /***********************************************************************
2219  *           LISTBOX_HandleKeyDown
2220  */
2221 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
2222 {
2223     INT caret = -1;
2224     BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2225     if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2226        bForceSelection = FALSE; /* only for single select list */
2227
2228     if (descr->style & LBS_WANTKEYBOARDINPUT)
2229     {
2230         caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2231                                 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2232                                 wnd->hwndSelf );
2233         if (caret == -2) return 0;
2234     }
2235     if (caret == -1) switch(wParam)
2236     {
2237     case VK_LEFT:
2238         if (descr->style & LBS_MULTICOLUMN)
2239         {
2240             bForceSelection = FALSE;
2241             if (descr->focus_item >= descr->page_size)
2242                 caret = descr->focus_item - descr->page_size;
2243             break;
2244         }
2245         /* fall through */
2246     case VK_UP:
2247         caret = descr->focus_item - 1;
2248         if (caret < 0) caret = 0;
2249         break;
2250     case VK_RIGHT:
2251         if (descr->style & LBS_MULTICOLUMN)
2252         {
2253             bForceSelection = FALSE;
2254             if (descr->focus_item + descr->page_size < descr->nb_items)
2255                 caret = descr->focus_item + descr->page_size;
2256             break;
2257         }
2258         /* fall through */
2259     case VK_DOWN:
2260         caret = descr->focus_item + 1;
2261         if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2262         break;
2263
2264     case VK_PRIOR:
2265         if (descr->style & LBS_MULTICOLUMN)
2266         {
2267             INT page = descr->width / descr->column_width;
2268             if (page < 1) page = 1;
2269             caret = descr->focus_item - (page * descr->page_size) + 1;
2270         }
2271         else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2272         if (caret < 0) caret = 0;
2273         break;
2274     case VK_NEXT:
2275         if (descr->style & LBS_MULTICOLUMN)
2276         {
2277             INT page = descr->width / descr->column_width;
2278             if (page < 1) page = 1;
2279             caret = descr->focus_item + (page * descr->page_size) - 1;
2280         }
2281         else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2282         if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2283         break;
2284     case VK_HOME:
2285         caret = 0;
2286         break;
2287     case VK_END:
2288         caret = descr->nb_items - 1;
2289         break;
2290     case VK_SPACE:
2291         if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2292         else if (descr->style & LBS_MULTIPLESEL)
2293         {
2294             LISTBOX_SetSelection( wnd, descr, descr->focus_item,
2295                                   !descr->items[descr->focus_item].selected,
2296                                   (descr->style & LBS_NOTIFY) != 0 );
2297         }
2298         break;
2299     default:
2300         bForceSelection = FALSE;
2301     }
2302     if (bForceSelection) /* focused item is used instead of key */
2303         caret = descr->focus_item;
2304     if (caret >= 0)
2305     {
2306         if ((descr->style & LBS_EXTENDEDSEL) &&
2307             !(GetKeyState( VK_SHIFT ) & 0x8000))
2308             descr->anchor_item = caret;
2309         LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2310         LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2311         if (descr->style & LBS_NOTIFY)
2312         {
2313             if( descr->lphc )
2314             {
2315                 /* make sure that combo parent doesn't hide us */
2316                 descr->lphc->wState |= CBF_NOROLLUP;
2317             }
2318             if (descr->nb_items) SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2319         }
2320     }
2321     return 0;
2322 }
2323
2324
2325 /***********************************************************************
2326  *           LISTBOX_HandleChar
2327  */
2328 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr, WCHAR charW )
2329 {
2330     INT caret = -1;
2331     WCHAR str[2];
2332
2333     str[0] = charW;
2334     str[1] = '\0';
2335
2336     if (descr->style & LBS_WANTKEYBOARDINPUT)
2337     {
2338         caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2339                                 MAKEWPARAM(charW, descr->focus_item),
2340                                 wnd->hwndSelf );
2341         if (caret == -2) return 0;
2342     }
2343     if (caret == -1)
2344         caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2345     if (caret != -1)
2346     {
2347         if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2348            LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2349         LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2350         if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2351             SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2352     }
2353     return 0;
2354 }
2355
2356
2357 /***********************************************************************
2358  *           LISTBOX_Create
2359  */
2360 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2361 {
2362     LB_DESCR *descr;
2363     MEASUREITEMSTRUCT mis;
2364     RECT rect;
2365
2366     if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2367         return FALSE;
2368
2369     GetClientRect( wnd->hwndSelf, &rect );
2370     descr->owner         = GetParent( wnd->hwndSelf );
2371     descr->style         = wnd->dwStyle;
2372     descr->width         = rect.right - rect.left;
2373     descr->height        = rect.bottom - rect.top;
2374     descr->items         = NULL;
2375     descr->nb_items      = 0;
2376     descr->top_item      = 0;
2377     descr->selected_item = -1;
2378     descr->focus_item    = 0;
2379     descr->anchor_item   = -1;
2380     descr->item_height   = 1;
2381     descr->page_size     = 1;
2382     descr->column_width  = 150;
2383     descr->horz_extent   = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2384     descr->horz_pos      = 0;
2385     descr->nb_tabs       = 0;
2386     descr->tabs          = NULL;
2387     descr->caret_on      = lphc ? FALSE : TRUE;
2388     if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2389     descr->in_focus      = FALSE;
2390     descr->captured      = FALSE;
2391     descr->font          = 0;
2392     descr->locale        = 0;  /* FIXME */
2393     descr->lphc          = lphc;
2394
2395     if( ( GetExpWinVer16( wnd->hInstance ) & 0xFF00 ) == 0x0300
2396         && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2397     {
2398         /* Win95 document "List Box Differences" from MSDN:
2399            If a list box in a version 3.x application has either the
2400            WS_HSCROLL or WS_VSCROLL style, the list box receives both
2401            horizontal and vertical scroll bars.
2402         */
2403         descr->style |= WS_VSCROLL | WS_HSCROLL;
2404     }
2405
2406     if( lphc )
2407     {
2408         TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2409                      wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2410         descr->owner = lphc->self->hwndSelf;
2411     }
2412
2413     *(LB_DESCR **)wnd->wExtra = descr;
2414
2415 /*    if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2416  */
2417     if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2418     if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2419     if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2420     descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2421
2422     if (descr->style & LBS_OWNERDRAWFIXED)
2423     {
2424         if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2425         {
2426             /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2427           descr->item_height = lphc->fixedOwnerDrawHeight;
2428         }
2429         else
2430         {
2431             mis.CtlType    = ODT_LISTBOX;
2432             mis.CtlID      = wnd->wIDmenu;
2433             mis.itemID     = -1;
2434             mis.itemWidth  =  0;
2435             mis.itemData   =  0;
2436             mis.itemHeight = descr->item_height;
2437             SendMessageW( descr->owner, WM_MEASUREITEM, wnd->wIDmenu, (LPARAM)&mis );
2438             descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2439         }
2440     }
2441
2442     TRACE("owner: %04x, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2443     return TRUE;
2444 }
2445
2446
2447 /***********************************************************************
2448  *           LISTBOX_Destroy
2449  */
2450 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2451 {
2452     LISTBOX_ResetContent( wnd, descr );
2453     HeapFree( GetProcessHeap(), 0, descr );
2454     wnd->wExtra[0] = 0;
2455     return TRUE;
2456 }
2457
2458
2459 /***********************************************************************
2460  *           ListBoxWndProc
2461  */
2462 static LRESULT WINAPI ListBoxWndProc_locked( WND* wnd, UINT msg,
2463                                              WPARAM wParam, LPARAM lParam, BOOL unicode )
2464 {
2465     LRESULT ret;
2466     LB_DESCR *descr;
2467     HWND        hwnd = wnd->hwndSelf;
2468
2469     if (!(descr = *(LB_DESCR **)wnd->wExtra))
2470     {
2471         if (msg == WM_CREATE)
2472         {
2473             if (!LISTBOX_Create( wnd, NULL ))
2474                 return -1;
2475             TRACE("creating wnd=%04x descr=%p\n", hwnd, *(LB_DESCR **)wnd->wExtra );
2476             return 0;
2477         }
2478         /* Ignore all other messages before we get a WM_CREATE */
2479         return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2480                          DefWindowProcA( hwnd, msg, wParam, lParam );
2481     }
2482
2483     TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2484           wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2485     switch(msg)
2486     {
2487     case LB_RESETCONTENT16:
2488     case LB_RESETCONTENT:
2489         LISTBOX_ResetContent( wnd, descr );
2490         LISTBOX_UpdateScroll( wnd, descr );
2491         InvalidateRect( wnd->hwndSelf, NULL, TRUE );
2492         return 0;
2493
2494     case LB_ADDSTRING16:
2495         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2496         /* fall through */
2497     case LB_ADDSTRING:
2498     {
2499         INT ret;
2500         LPWSTR textW;
2501         if(unicode || !HAS_STRINGS(descr))
2502             textW = (LPWSTR)lParam;
2503         else
2504         {
2505             LPSTR textA = (LPSTR)lParam;
2506             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2507             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2508                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2509         }
2510         wParam = LISTBOX_FindStringPos( wnd, descr, textW, FALSE );
2511         ret = LISTBOX_InsertString( wnd, descr, wParam, textW );
2512         if (!unicode && HAS_STRINGS(descr))
2513             HeapFree(GetProcessHeap(), 0, textW);
2514         return ret;
2515     }
2516
2517     case LB_INSERTSTRING16:
2518         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2519         wParam = (INT)(INT16)wParam;
2520         /* fall through */
2521     case LB_INSERTSTRING:
2522     {
2523         INT ret;
2524         LPWSTR textW;
2525         if(unicode || !HAS_STRINGS(descr))
2526             textW = (LPWSTR)lParam;
2527         else
2528         {
2529             LPSTR textA = (LPSTR)lParam;
2530             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2531             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2532                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2533         }
2534         ret = LISTBOX_InsertString( wnd, descr, wParam, textW );
2535         if(!unicode && HAS_STRINGS(descr))
2536             HeapFree(GetProcessHeap(), 0, textW);
2537         return ret;
2538     }
2539
2540     case LB_ADDFILE16:
2541         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2542         /* fall through */
2543     case LB_ADDFILE:
2544     {
2545         INT ret;
2546         LPWSTR textW;
2547         if(unicode || !HAS_STRINGS(descr))
2548             textW = (LPWSTR)lParam;
2549         else
2550         {
2551             LPSTR textA = (LPSTR)lParam;
2552             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2553             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2554                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2555         }
2556         wParam = LISTBOX_FindFileStrPos( wnd, descr, textW );
2557         ret = LISTBOX_InsertString( wnd, descr, wParam, textW );
2558         if(!unicode && HAS_STRINGS(descr))
2559             HeapFree(GetProcessHeap(), 0, textW);
2560         return ret;
2561     }
2562
2563     case LB_DELETESTRING16:
2564     case LB_DELETESTRING:
2565         if (LISTBOX_RemoveItem( wnd, descr, wParam) != LB_ERR)
2566            return descr->nb_items;
2567         else
2568            return LB_ERR;
2569
2570     case LB_GETITEMDATA16:
2571     case LB_GETITEMDATA:
2572         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2573             return LB_ERR;
2574         return descr->items[wParam].data;
2575
2576     case LB_SETITEMDATA16:
2577     case LB_SETITEMDATA:
2578         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2579             return LB_ERR;
2580         descr->items[wParam].data = (DWORD)lParam;
2581         return LB_OKAY;
2582
2583     case LB_GETCOUNT16:
2584     case LB_GETCOUNT:
2585         return descr->nb_items;
2586
2587     case LB_GETTEXT16:
2588         lParam = (LPARAM)MapSL(lParam);
2589         /* fall through */
2590     case LB_GETTEXT:
2591         return LISTBOX_GetText( descr, wParam, lParam, unicode );
2592
2593     case LB_GETTEXTLEN16:
2594         /* fall through */
2595     case LB_GETTEXTLEN:
2596         if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2597             return LB_ERR;
2598         return (HAS_STRINGS(descr) ? strlenW(descr->items[wParam].str)
2599                                    : sizeof(DWORD));
2600
2601     case LB_GETCURSEL16:
2602     case LB_GETCURSEL:
2603         if (descr->nb_items==0)
2604           return LB_ERR;
2605         if (!IS_MULTISELECT(descr))
2606           return descr->selected_item;
2607         /* else */
2608         if (descr->selected_item!=-1)
2609           return descr->selected_item;
2610         /* else */
2611         return descr->focus_item;
2612         /* otherwise, if the user tries to move the selection with the    */
2613         /* arrow keys, we will give the application something to choke on */
2614     case LB_GETTOPINDEX16:
2615     case LB_GETTOPINDEX:
2616         return descr->top_item;
2617
2618     case LB_GETITEMHEIGHT16:
2619     case LB_GETITEMHEIGHT:
2620         return LISTBOX_GetItemHeight( descr, wParam );
2621
2622     case LB_SETITEMHEIGHT16:
2623         lParam = LOWORD(lParam);
2624         /* fall through */
2625     case LB_SETITEMHEIGHT:
2626         return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2627
2628     case LB_ITEMFROMPOINT:
2629         {
2630             POINT pt;
2631             RECT rect;
2632
2633             pt.x = LOWORD(lParam);
2634             pt.y = HIWORD(lParam);
2635             rect.left = 0;
2636             rect.top = 0;
2637             rect.right = descr->width;
2638             rect.bottom = descr->height;
2639
2640             return MAKELONG( LISTBOX_GetItemFromPoint(descr, pt.x, pt.y),
2641                              !PtInRect( &rect, pt ) );
2642         }
2643
2644     case LB_SETCARETINDEX16:
2645     case LB_SETCARETINDEX:
2646         if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2647         if (LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam ) == LB_ERR)
2648             return LB_ERR;
2649         else if (ISWIN31)
2650              return wParam;
2651         else
2652              return LB_OKAY;
2653
2654     case LB_GETCARETINDEX16:
2655     case LB_GETCARETINDEX:
2656         return descr->focus_item;
2657
2658     case LB_SETTOPINDEX16:
2659     case LB_SETTOPINDEX:
2660         return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2661
2662     case LB_SETCOLUMNWIDTH16:
2663     case LB_SETCOLUMNWIDTH:
2664         return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2665
2666     case LB_GETITEMRECT16:
2667         {
2668             RECT rect;
2669             ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2670             CONV_RECT32TO16( &rect, MapSL(lParam) );
2671         }
2672         return ret;
2673
2674     case LB_GETITEMRECT:
2675         return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2676
2677     case LB_FINDSTRING16:
2678         wParam = (INT)(INT16)wParam;
2679         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2680         /* fall through */
2681     case LB_FINDSTRING:
2682     {
2683         INT ret;
2684         LPWSTR textW;
2685         if(unicode || !HAS_STRINGS(descr))
2686             textW = (LPWSTR)lParam;
2687         else
2688         {
2689             LPSTR textA = (LPSTR)lParam;
2690             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2691             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2692                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2693         }
2694         ret = LISTBOX_FindString( wnd, descr, wParam, textW, FALSE );
2695         if(!unicode && HAS_STRINGS(descr))
2696             HeapFree(GetProcessHeap(), 0, textW);
2697         return ret;
2698     }
2699
2700     case LB_FINDSTRINGEXACT16:
2701         wParam = (INT)(INT16)wParam;
2702         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2703         /* fall through */
2704     case LB_FINDSTRINGEXACT:
2705     {
2706         INT ret;
2707         LPWSTR textW;
2708         if(unicode || !HAS_STRINGS(descr))
2709             textW = (LPWSTR)lParam;
2710         else
2711         {
2712             LPSTR textA = (LPSTR)lParam;
2713             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2714             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2715                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2716         }
2717         ret = LISTBOX_FindString( wnd, descr, wParam, textW, TRUE );
2718         if(!unicode && HAS_STRINGS(descr))
2719             HeapFree(GetProcessHeap(), 0, textW);
2720         return ret;
2721     }
2722
2723     case LB_SELECTSTRING16:
2724         wParam = (INT)(INT16)wParam;
2725         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2726         /* fall through */
2727     case LB_SELECTSTRING:
2728     {
2729         INT index;
2730         LPWSTR textW;
2731
2732         if(HAS_STRINGS(descr))
2733             TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2734                                                      debugstr_a((LPSTR)lParam));
2735         if(unicode || !HAS_STRINGS(descr))
2736             textW = (LPWSTR)lParam;
2737         else
2738         {
2739             LPSTR textA = (LPSTR)lParam;
2740             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2741             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2742                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2743         }
2744         index = LISTBOX_FindString( wnd, descr, wParam, textW, FALSE );
2745         if(!unicode && HAS_STRINGS(descr))
2746             HeapFree(GetProcessHeap(), 0, textW);
2747         if (index != LB_ERR)
2748         {
2749             LISTBOX_SetCaretIndex( wnd, descr, index, TRUE );
2750             LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2751         }
2752         return index;
2753     }
2754
2755     case LB_GETSEL16:
2756         wParam = (INT)(INT16)wParam;
2757         /* fall through */
2758     case LB_GETSEL:
2759         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2760             return LB_ERR;
2761         return descr->items[wParam].selected;
2762
2763     case LB_SETSEL16:
2764         lParam = (INT)(INT16)lParam;
2765         /* fall through */
2766     case LB_SETSEL:
2767         return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2768
2769     case LB_SETCURSEL16:
2770         wParam = (INT)(INT16)wParam;
2771         /* fall through */
2772     case LB_SETCURSEL:
2773         if (IS_MULTISELECT(descr)) return LB_ERR;
2774         LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );  
2775         return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2776
2777     case LB_GETSELCOUNT16:
2778     case LB_GETSELCOUNT:
2779         return LISTBOX_GetSelCount( descr );
2780
2781     case LB_GETSELITEMS16:
2782         return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2783
2784     case LB_GETSELITEMS:
2785         return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2786
2787     case LB_SELITEMRANGE16:
2788     case LB_SELITEMRANGE:
2789         if (LOWORD(lParam) <= HIWORD(lParam))
2790             return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2791                                             HIWORD(lParam), wParam );
2792         else
2793             return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2794                                             LOWORD(lParam), wParam );
2795
2796     case LB_SELITEMRANGEEX16:
2797     case LB_SELITEMRANGEEX:
2798         if ((INT)lParam >= (INT)wParam)
2799             return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2800         else
2801             return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2802
2803     case LB_GETHORIZONTALEXTENT16:
2804     case LB_GETHORIZONTALEXTENT:
2805         return descr->horz_extent;
2806
2807     case LB_SETHORIZONTALEXTENT16:
2808     case LB_SETHORIZONTALEXTENT:
2809         return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2810
2811     case LB_GETANCHORINDEX16:
2812     case LB_GETANCHORINDEX:
2813         return descr->anchor_item;
2814
2815     case LB_SETANCHORINDEX16:
2816         wParam = (INT)(INT16)wParam;
2817         /* fall through */
2818     case LB_SETANCHORINDEX:
2819         if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2820             return LB_ERR;
2821         descr->anchor_item = (INT)wParam;
2822         return LB_OKAY;
2823
2824     case LB_DIR16:
2825         /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2826          * be set automatically (this is different in Win32) */
2827         if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2828         lParam = (LPARAM)MapSL(lParam);
2829         /* fall through */
2830     case LB_DIR:
2831     {
2832         INT ret;
2833         LPWSTR textW;
2834         if(unicode)
2835             textW = (LPWSTR)lParam;
2836         else
2837         {
2838             LPSTR textA = (LPSTR)lParam;
2839             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2840             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2841                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2842         }
2843         ret = LISTBOX_Directory( wnd, descr, wParam, textW, msg == LB_DIR );
2844         if(!unicode)
2845             HeapFree(GetProcessHeap(), 0, textW);
2846         return ret;
2847     }
2848
2849     case LB_GETLOCALE:
2850         return descr->locale;
2851
2852     case LB_SETLOCALE:
2853         descr->locale = (LCID)wParam;  /* FIXME: should check for valid lcid */
2854         return LB_OKAY;
2855
2856     case LB_INITSTORAGE:
2857         return LISTBOX_InitStorage( wnd, descr, wParam );
2858
2859     case LB_SETCOUNT:
2860         return LISTBOX_SetCount( wnd, descr, (INT)wParam );
2861
2862     case LB_SETTABSTOPS16:
2863         return LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2864
2865     case LB_SETTABSTOPS:
2866         return LISTBOX_SetTabStops( wnd, descr, wParam, (LPINT)lParam, FALSE );
2867
2868     case LB_CARETON16:
2869     case LB_CARETON:
2870         if (descr->caret_on)
2871             return LB_OKAY;
2872         descr->caret_on = TRUE;
2873         if ((descr->focus_item != -1) && (descr->in_focus))
2874             LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2875         return LB_OKAY;
2876
2877     case LB_CARETOFF16:
2878     case LB_CARETOFF:
2879         if (!descr->caret_on)
2880             return LB_OKAY;
2881         descr->caret_on = FALSE;
2882         if ((descr->focus_item != -1) && (descr->in_focus))
2883             LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2884         return LB_OKAY;
2885         
2886     case WM_DESTROY:
2887         return LISTBOX_Destroy( wnd, descr );
2888
2889     case WM_ENABLE:
2890         InvalidateRect( hwnd, NULL, TRUE );
2891         return 0;
2892
2893     case WM_SETREDRAW:
2894         LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2895         return 0;
2896
2897     case WM_GETDLGCODE:
2898         return DLGC_WANTARROWS | DLGC_WANTCHARS;
2899
2900     case WM_PAINT:
2901         {
2902             PAINTSTRUCT ps;
2903             HDC hdc = ( wParam ) ? ((HDC)wParam)
2904                                    :  BeginPaint( hwnd, &ps );
2905             ret = LISTBOX_Paint( wnd, descr, hdc );
2906             if( !wParam ) EndPaint( hwnd, &ps );
2907         }
2908         return ret;
2909     case WM_SIZE:
2910         LISTBOX_UpdateSize( wnd, descr );
2911         return 0;
2912     case WM_GETFONT:
2913         return descr->font;
2914     case WM_SETFONT:
2915         LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2916         if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2917         return 0;
2918     case WM_SETFOCUS:
2919         descr->in_focus = TRUE;
2920         descr->caret_on = TRUE;
2921         if (descr->focus_item != -1)
2922             LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2923         SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2924         return 0;
2925     case WM_KILLFOCUS:
2926         descr->in_focus = FALSE;
2927         if ((descr->focus_item != -1) && descr->caret_on)
2928             LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2929         SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2930         return 0;
2931     case WM_HSCROLL:
2932         return LISTBOX_HandleHScroll( wnd, descr, wParam );
2933     case WM_VSCROLL:
2934         return LISTBOX_HandleVScroll( wnd, descr, wParam );
2935     case WM_MOUSEWHEEL:
2936         if (wParam & (MK_SHIFT | MK_CONTROL))
2937             return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2938                              DefWindowProcA( hwnd, msg, wParam, lParam );
2939         return LISTBOX_HandleMouseWheel( wnd, descr, wParam );
2940     case WM_LBUTTONDOWN:
2941         return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2942                                           (INT16)LOWORD(lParam),
2943                                           (INT16)HIWORD(lParam) );
2944     case WM_LBUTTONDBLCLK:
2945         if (descr->style & LBS_NOTIFY)
2946             SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2947         return 0;
2948     case WM_MOUSEMOVE:
2949         if (GetCapture() == hwnd)
2950             LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2951                                      (INT16)HIWORD(lParam) );
2952         return 0;
2953     case WM_LBUTTONUP:
2954         return LISTBOX_HandleLButtonUp( wnd, descr );
2955     case WM_KEYDOWN:
2956         return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2957     case WM_CHAR:
2958     {
2959         WCHAR charW;
2960         if(unicode)
2961             charW = (WCHAR)wParam;
2962         else
2963         {
2964             CHAR charA = (CHAR)wParam;
2965             MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
2966         }
2967         return LISTBOX_HandleChar( wnd, descr, charW );
2968     }
2969     case WM_SYSTIMER:
2970         return LISTBOX_HandleSystemTimer( wnd, descr );
2971     case WM_ERASEBKGND:
2972         if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
2973         {
2974             RECT rect;
2975             HBRUSH hbrush = SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
2976                                               wParam, (LPARAM)wnd->hwndSelf );
2977             TRACE("hbrush = %04x\n", hbrush);
2978             if(!hbrush)
2979                 hbrush = GetSysColorBrush(COLOR_WINDOW);
2980             if(hbrush)
2981             {
2982                 GetClientRect(hwnd, &rect);
2983                 FillRect((HDC)wParam, &rect, hbrush);
2984             }
2985         }
2986         return 1;
2987     case WM_DROPFILES:
2988         if( !descr->lphc )
2989             return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
2990                              SendMessageA( descr->owner, msg, wParam, lParam );
2991         break;
2992
2993     case WM_DROPOBJECT:
2994     case WM_QUERYDROPOBJECT:
2995     case WM_DRAGSELECT:
2996     case WM_DRAGMOVE:
2997         if( !descr->lphc )
2998         {
2999             LPDRAGINFO16 dragInfo = MapSL( lParam );
3000             dragInfo->l = LISTBOX_GetItemFromPoint( descr, dragInfo->pt.x,
3001                                                 dragInfo->pt.y );
3002             return SendMessage16( descr->owner, msg, wParam, lParam );
3003         }
3004         break;
3005
3006     default:
3007         if ((msg >= WM_USER) && (msg < 0xc000))
3008             WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
3009                  hwnd, msg, wParam, lParam );
3010         return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3011                          DefWindowProcA( hwnd, msg, wParam, lParam );
3012     }
3013     return 0;
3014 }
3015
3016 /***********************************************************************
3017  *           ListBoxWndProcA
3018  *
3019  * This is just a wrapper for the real wndproc, it only does window locking
3020  * and unlocking.
3021  */
3022 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3023 {
3024     LRESULT res = 0;
3025     WND* wndPtr = WIN_FindWndPtr( hwnd );
3026
3027     if (wndPtr)
3028     {
3029         res = ListBoxWndProc_locked(wndPtr, msg, wParam, lParam, FALSE);
3030         WIN_ReleaseWndPtr(wndPtr);
3031     }
3032     return res;
3033 }
3034
3035 /***********************************************************************
3036  *           ListBoxWndProcW
3037  */
3038 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3039 {
3040     LRESULT res = 0;
3041     WND* wndPtr = WIN_FindWndPtr( hwnd );
3042
3043     if (wndPtr)
3044     {
3045         res = ListBoxWndProc_locked(wndPtr, msg, wParam, lParam, TRUE);
3046         WIN_ReleaseWndPtr(wndPtr);
3047     }
3048     return res;
3049 }
3050
3051 /***********************************************************************
3052  *           ComboLBWndProc_locked
3053  *
3054  * The real combo listbox wndproc, but called with locked WND struct.
3055  */
3056 static LRESULT WINAPI ComboLBWndProc_locked( WND* wnd, UINT msg,
3057                                              WPARAM wParam, LPARAM lParam, BOOL unicode )
3058 {
3059     LRESULT lRet = 0;
3060     HWND hwnd;
3061     LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
3062
3063     TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
3064                   wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
3065
3066     hwnd = wnd->hwndSelf;
3067
3068     if( descr || msg == WM_CREATE )
3069     {
3070         LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
3071
3072         switch( msg )
3073         {
3074         case WM_CREATE:
3075             {
3076                 CREATESTRUCTA *lpcs = (CREATESTRUCTA *)lParam;
3077                 TRACE_(combo)("\tpassed parent handle = %p\n",lpcs->lpCreateParams);
3078                 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
3079                 return LISTBOX_Create( wnd, lphc );
3080             }
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, descr, lphc->droppedIndex, FALSE );
3152                 }
3153             }
3154             return LISTBOX_HandleLButtonUp( wnd, descr );
3155         case WM_LBUTTONDBLCLK:
3156         case WM_LBUTTONDOWN:
3157             return LISTBOX_HandleLButtonDownCombo(wnd, descr, msg, wParam,
3158                                                   (INT16)LOWORD(lParam),
3159                                                   (INT16)HIWORD(lParam) );
3160         case WM_NCACTIVATE:
3161             return FALSE;
3162         case WM_KEYDOWN:
3163             if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3164             {
3165                 /* for some reason(?) Windows makes it possible to
3166                  * show/hide ComboLBox by sending it WM_KEYDOWNs */
3167
3168                 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3169                     ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3170                       && (wParam == VK_DOWN || wParam == VK_UP)) )
3171                 {
3172                     COMBO_FlipListbox( lphc, FALSE, FALSE );
3173                     return 0;
3174                 }
3175             }
3176             return LISTBOX_HandleKeyDown( wnd, descr, wParam );
3177
3178         case LB_SETCURSEL16:
3179         case LB_SETCURSEL:
3180             lRet = unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3181                 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3182             lRet =(lRet == LB_ERR) ? lRet : descr->selected_item; 
3183             return lRet;
3184         case WM_NCDESTROY:
3185             if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3186                 lphc->hWndLBox = 0;
3187             /* fall through */
3188
3189         default:
3190             return unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3191                 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3192         }
3193     }
3194     lRet = unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3195         DefWindowProcA( hwnd, msg, wParam, lParam );
3196
3197     TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
3198
3199     return lRet;
3200 }
3201
3202 /***********************************************************************
3203  *           ComboLBWndProcA
3204  *
3205  *  NOTE: in Windows, winproc address of the ComboLBox is the same 
3206  *        as that of the Listbox.
3207  * 
3208  * This is just a wrapper for the real wndproc, it only does window locking
3209  * and unlocking.
3210  */
3211 LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg,
3212                                WPARAM wParam, LPARAM lParam )
3213 {
3214     LRESULT res = 0;
3215     WND *wnd = WIN_FindWndPtr( hwnd );
3216
3217     if (wnd)
3218     {
3219         res = ComboLBWndProc_locked(wnd, msg, wParam, lParam, FALSE);
3220         WIN_ReleaseWndPtr(wnd);
3221     }
3222     return res;
3223 }
3224
3225 /***********************************************************************
3226  *           ComboLBWndProcW
3227  */
3228 LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3229 {
3230     LRESULT res = 0;
3231     WND *wnd = WIN_FindWndPtr( hwnd );
3232
3233     if (wnd)
3234     {
3235         res = ComboLBWndProc_locked(wnd, msg, wParam, lParam, TRUE);
3236         WIN_ReleaseWndPtr(wnd);
3237     }
3238     return res;
3239 }