Remove unnecessary single quoting of debugstr_xxx strings.
[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 (!wnd) return 0;
2470     if (!(descr = *(LB_DESCR **)wnd->wExtra))
2471     {
2472         switch (msg)
2473         {
2474             case WM_CREATE:
2475             {
2476                 if (!LISTBOX_Create( wnd, NULL ))
2477                      return -1;
2478                 TRACE("creating wnd=%04x descr=%p\n",
2479                       hwnd, *(LB_DESCR **)wnd->wExtra );
2480                 return 0;
2481             }
2482             case WM_NCCREATE:
2483             {
2484                 /*
2485                  * When a listbox is not in a combobox and the look
2486                  * is win95,  the WS_BORDER style is replaced with 
2487                  * the WS_EX_CLIENTEDGE style.
2488                  */
2489                 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2490                      (wnd->dwStyle & WS_BORDER) )
2491                 {
2492                     wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2493                     wnd->dwStyle     &= ~ WS_BORDER;
2494                 }
2495             }
2496         }
2497
2498         /* Ignore all other messages before we get a WM_CREATE */
2499         return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2500                          DefWindowProcA( hwnd, msg, wParam, lParam );
2501     }
2502
2503     TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2504           wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2505     switch(msg)
2506     {
2507     case LB_RESETCONTENT16:
2508     case LB_RESETCONTENT:
2509         LISTBOX_ResetContent( wnd, descr );
2510         LISTBOX_UpdateScroll( wnd, descr );
2511         InvalidateRect( wnd->hwndSelf, NULL, TRUE );
2512         return 0;
2513
2514     case LB_ADDSTRING16:
2515         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2516         /* fall through */
2517     case LB_ADDSTRING:
2518     {
2519         INT ret;
2520         LPWSTR textW;
2521         if(unicode || !HAS_STRINGS(descr))
2522             textW = (LPWSTR)lParam;
2523         else
2524         {
2525             LPSTR textA = (LPSTR)lParam;
2526             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2527             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2528                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2529         }
2530         wParam = LISTBOX_FindStringPos( wnd, descr, textW, FALSE );
2531         ret = LISTBOX_InsertString( wnd, descr, wParam, textW );
2532         if (!unicode && HAS_STRINGS(descr))
2533             HeapFree(GetProcessHeap(), 0, textW);
2534         return ret;
2535     }
2536
2537     case LB_INSERTSTRING16:
2538         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2539         wParam = (INT)(INT16)wParam;
2540         /* fall through */
2541     case LB_INSERTSTRING:
2542     {
2543         INT ret;
2544         LPWSTR textW;
2545         if(unicode || !HAS_STRINGS(descr))
2546             textW = (LPWSTR)lParam;
2547         else
2548         {
2549             LPSTR textA = (LPSTR)lParam;
2550             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2551             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2552                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2553         }
2554         ret = LISTBOX_InsertString( wnd, descr, wParam, textW );
2555         if(!unicode && HAS_STRINGS(descr))
2556             HeapFree(GetProcessHeap(), 0, textW);
2557         return ret;
2558     }
2559
2560     case LB_ADDFILE16:
2561         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2562         /* fall through */
2563     case LB_ADDFILE:
2564     {
2565         INT ret;
2566         LPWSTR textW;
2567         if(unicode || !HAS_STRINGS(descr))
2568             textW = (LPWSTR)lParam;
2569         else
2570         {
2571             LPSTR textA = (LPSTR)lParam;
2572             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2573             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2574                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2575         }
2576         wParam = LISTBOX_FindFileStrPos( wnd, descr, textW );
2577         ret = LISTBOX_InsertString( wnd, descr, wParam, textW );
2578         if(!unicode && HAS_STRINGS(descr))
2579             HeapFree(GetProcessHeap(), 0, textW);
2580         return ret;
2581     }
2582
2583     case LB_DELETESTRING16:
2584     case LB_DELETESTRING:
2585         if (LISTBOX_RemoveItem( wnd, descr, wParam) != LB_ERR)
2586            return descr->nb_items;
2587         else
2588            return LB_ERR;
2589
2590     case LB_GETITEMDATA16:
2591     case LB_GETITEMDATA:
2592         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2593             return LB_ERR;
2594         return descr->items[wParam].data;
2595
2596     case LB_SETITEMDATA16:
2597     case LB_SETITEMDATA:
2598         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2599             return LB_ERR;
2600         descr->items[wParam].data = (DWORD)lParam;
2601         return LB_OKAY;
2602
2603     case LB_GETCOUNT16:
2604     case LB_GETCOUNT:
2605         return descr->nb_items;
2606
2607     case LB_GETTEXT16:
2608         lParam = (LPARAM)MapSL(lParam);
2609         /* fall through */
2610     case LB_GETTEXT:
2611         return LISTBOX_GetText( descr, wParam, lParam, unicode );
2612
2613     case LB_GETTEXTLEN16:
2614         /* fall through */
2615     case LB_GETTEXTLEN:
2616         if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2617             return LB_ERR;
2618         return (HAS_STRINGS(descr) ? strlenW(descr->items[wParam].str)
2619                                    : sizeof(DWORD));
2620
2621     case LB_GETCURSEL16:
2622     case LB_GETCURSEL:
2623         if (descr->nb_items==0)
2624           return LB_ERR;
2625         if (!IS_MULTISELECT(descr))
2626           return descr->selected_item;
2627         /* else */
2628         if (descr->selected_item!=-1)
2629           return descr->selected_item;
2630         /* else */
2631         return descr->focus_item;
2632         /* otherwise, if the user tries to move the selection with the    */
2633         /* arrow keys, we will give the application something to choke on */
2634     case LB_GETTOPINDEX16:
2635     case LB_GETTOPINDEX:
2636         return descr->top_item;
2637
2638     case LB_GETITEMHEIGHT16:
2639     case LB_GETITEMHEIGHT:
2640         return LISTBOX_GetItemHeight( descr, wParam );
2641
2642     case LB_SETITEMHEIGHT16:
2643         lParam = LOWORD(lParam);
2644         /* fall through */
2645     case LB_SETITEMHEIGHT:
2646         return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2647
2648     case LB_ITEMFROMPOINT:
2649         {
2650             POINT pt;
2651             RECT rect;
2652
2653             pt.x = LOWORD(lParam);
2654             pt.y = HIWORD(lParam);
2655             rect.left = 0;
2656             rect.top = 0;
2657             rect.right = descr->width;
2658             rect.bottom = descr->height;
2659
2660             return MAKELONG( LISTBOX_GetItemFromPoint(descr, pt.x, pt.y),
2661                              !PtInRect( &rect, pt ) );
2662         }
2663
2664     case LB_SETCARETINDEX16:
2665     case LB_SETCARETINDEX:
2666         if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2667         if (LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam ) == LB_ERR)
2668             return LB_ERR;
2669         else if (ISWIN31)
2670              return wParam;
2671         else
2672              return LB_OKAY;
2673
2674     case LB_GETCARETINDEX16:
2675     case LB_GETCARETINDEX:
2676         return descr->focus_item;
2677
2678     case LB_SETTOPINDEX16:
2679     case LB_SETTOPINDEX:
2680         return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2681
2682     case LB_SETCOLUMNWIDTH16:
2683     case LB_SETCOLUMNWIDTH:
2684         return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2685
2686     case LB_GETITEMRECT16:
2687         {
2688             RECT rect;
2689             ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2690             CONV_RECT32TO16( &rect, MapSL(lParam) );
2691         }
2692         return ret;
2693
2694     case LB_GETITEMRECT:
2695         return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2696
2697     case LB_FINDSTRING16:
2698         wParam = (INT)(INT16)wParam;
2699         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2700         /* fall through */
2701     case LB_FINDSTRING:
2702     {
2703         INT ret;
2704         LPWSTR textW;
2705         if(unicode || !HAS_STRINGS(descr))
2706             textW = (LPWSTR)lParam;
2707         else
2708         {
2709             LPSTR textA = (LPSTR)lParam;
2710             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2711             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2712                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2713         }
2714         ret = LISTBOX_FindString( wnd, descr, wParam, textW, FALSE );
2715         if(!unicode && HAS_STRINGS(descr))
2716             HeapFree(GetProcessHeap(), 0, textW);
2717         return ret;
2718     }
2719
2720     case LB_FINDSTRINGEXACT16:
2721         wParam = (INT)(INT16)wParam;
2722         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2723         /* fall through */
2724     case LB_FINDSTRINGEXACT:
2725     {
2726         INT ret;
2727         LPWSTR textW;
2728         if(unicode || !HAS_STRINGS(descr))
2729             textW = (LPWSTR)lParam;
2730         else
2731         {
2732             LPSTR textA = (LPSTR)lParam;
2733             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2734             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2735                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2736         }
2737         ret = LISTBOX_FindString( wnd, descr, wParam, textW, TRUE );
2738         if(!unicode && HAS_STRINGS(descr))
2739             HeapFree(GetProcessHeap(), 0, textW);
2740         return ret;
2741     }
2742
2743     case LB_SELECTSTRING16:
2744         wParam = (INT)(INT16)wParam;
2745         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2746         /* fall through */
2747     case LB_SELECTSTRING:
2748     {
2749         INT index;
2750         LPWSTR textW;
2751
2752         if(HAS_STRINGS(descr))
2753             TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2754                                                      debugstr_a((LPSTR)lParam));
2755         if(unicode || !HAS_STRINGS(descr))
2756             textW = (LPWSTR)lParam;
2757         else
2758         {
2759             LPSTR textA = (LPSTR)lParam;
2760             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2761             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2762                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2763         }
2764         index = LISTBOX_FindString( wnd, descr, wParam, textW, FALSE );
2765         if(!unicode && HAS_STRINGS(descr))
2766             HeapFree(GetProcessHeap(), 0, textW);
2767         if (index != LB_ERR)
2768         {
2769             LISTBOX_SetCaretIndex( wnd, descr, index, TRUE );
2770             LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2771         }
2772         return index;
2773     }
2774
2775     case LB_GETSEL16:
2776         wParam = (INT)(INT16)wParam;
2777         /* fall through */
2778     case LB_GETSEL:
2779         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2780             return LB_ERR;
2781         return descr->items[wParam].selected;
2782
2783     case LB_SETSEL16:
2784         lParam = (INT)(INT16)lParam;
2785         /* fall through */
2786     case LB_SETSEL:
2787         return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2788
2789     case LB_SETCURSEL16:
2790         wParam = (INT)(INT16)wParam;
2791         /* fall through */
2792     case LB_SETCURSEL:
2793         if (IS_MULTISELECT(descr)) return LB_ERR;
2794         LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );  
2795         return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2796
2797     case LB_GETSELCOUNT16:
2798     case LB_GETSELCOUNT:
2799         return LISTBOX_GetSelCount( descr );
2800
2801     case LB_GETSELITEMS16:
2802         return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2803
2804     case LB_GETSELITEMS:
2805         return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2806
2807     case LB_SELITEMRANGE16:
2808     case LB_SELITEMRANGE:
2809         if (LOWORD(lParam) <= HIWORD(lParam))
2810             return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2811                                             HIWORD(lParam), wParam );
2812         else
2813             return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2814                                             LOWORD(lParam), wParam );
2815
2816     case LB_SELITEMRANGEEX16:
2817     case LB_SELITEMRANGEEX:
2818         if ((INT)lParam >= (INT)wParam)
2819             return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2820         else
2821             return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2822
2823     case LB_GETHORIZONTALEXTENT16:
2824     case LB_GETHORIZONTALEXTENT:
2825         return descr->horz_extent;
2826
2827     case LB_SETHORIZONTALEXTENT16:
2828     case LB_SETHORIZONTALEXTENT:
2829         return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2830
2831     case LB_GETANCHORINDEX16:
2832     case LB_GETANCHORINDEX:
2833         return descr->anchor_item;
2834
2835     case LB_SETANCHORINDEX16:
2836         wParam = (INT)(INT16)wParam;
2837         /* fall through */
2838     case LB_SETANCHORINDEX:
2839         if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2840             return LB_ERR;
2841         descr->anchor_item = (INT)wParam;
2842         return LB_OKAY;
2843
2844     case LB_DIR16:
2845         /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2846          * be set automatically (this is different in Win32) */
2847         if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2848         lParam = (LPARAM)MapSL(lParam);
2849         /* fall through */
2850     case LB_DIR:
2851     {
2852         INT ret;
2853         LPWSTR textW;
2854         if(unicode)
2855             textW = (LPWSTR)lParam;
2856         else
2857         {
2858             LPSTR textA = (LPSTR)lParam;
2859             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2860             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2861                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2862         }
2863         ret = LISTBOX_Directory( wnd, descr, wParam, textW, msg == LB_DIR );
2864         if(!unicode)
2865             HeapFree(GetProcessHeap(), 0, textW);
2866         return ret;
2867     }
2868
2869     case LB_GETLOCALE:
2870         return descr->locale;
2871
2872     case LB_SETLOCALE:
2873         descr->locale = (LCID)wParam;  /* FIXME: should check for valid lcid */
2874         return LB_OKAY;
2875
2876     case LB_INITSTORAGE:
2877         return LISTBOX_InitStorage( wnd, descr, wParam );
2878
2879     case LB_SETCOUNT:
2880         return LISTBOX_SetCount( wnd, descr, (INT)wParam );
2881
2882     case LB_SETTABSTOPS16:
2883         return LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2884
2885     case LB_SETTABSTOPS:
2886         return LISTBOX_SetTabStops( wnd, descr, wParam, (LPINT)lParam, FALSE );
2887
2888     case LB_CARETON16:
2889     case LB_CARETON:
2890         if (descr->caret_on)
2891             return LB_OKAY;
2892         descr->caret_on = TRUE;
2893         if ((descr->focus_item != -1) && (descr->in_focus))
2894             LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2895         return LB_OKAY;
2896
2897     case LB_CARETOFF16:
2898     case LB_CARETOFF:
2899         if (!descr->caret_on)
2900             return LB_OKAY;
2901         descr->caret_on = FALSE;
2902         if ((descr->focus_item != -1) && (descr->in_focus))
2903             LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2904         return LB_OKAY;
2905         
2906     case WM_DESTROY:
2907         return LISTBOX_Destroy( wnd, descr );
2908
2909     case WM_ENABLE:
2910         InvalidateRect( hwnd, NULL, TRUE );
2911         return 0;
2912
2913     case WM_SETREDRAW:
2914         LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2915         return 0;
2916
2917     case WM_GETDLGCODE:
2918         return DLGC_WANTARROWS | DLGC_WANTCHARS;
2919
2920     case WM_PAINT:
2921         {
2922             PAINTSTRUCT ps;
2923             HDC hdc = ( wParam ) ? ((HDC)wParam)
2924                                    :  BeginPaint( hwnd, &ps );
2925             ret = LISTBOX_Paint( wnd, descr, hdc );
2926             if( !wParam ) EndPaint( hwnd, &ps );
2927         }
2928         return ret;
2929     case WM_SIZE:
2930         LISTBOX_UpdateSize( wnd, descr );
2931         return 0;
2932     case WM_GETFONT:
2933         return descr->font;
2934     case WM_SETFONT:
2935         LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2936         if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2937         return 0;
2938     case WM_SETFOCUS:
2939         descr->in_focus = TRUE;
2940         descr->caret_on = TRUE;
2941         if (descr->focus_item != -1)
2942             LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2943         SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2944         return 0;
2945     case WM_KILLFOCUS:
2946         descr->in_focus = FALSE;
2947         if ((descr->focus_item != -1) && descr->caret_on)
2948             LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2949         SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2950         return 0;
2951     case WM_HSCROLL:
2952         return LISTBOX_HandleHScroll( wnd, descr, wParam );
2953     case WM_VSCROLL:
2954         return LISTBOX_HandleVScroll( wnd, descr, wParam );
2955     case WM_MOUSEWHEEL:
2956         if (wParam & (MK_SHIFT | MK_CONTROL))
2957             return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2958                              DefWindowProcA( hwnd, msg, wParam, lParam );
2959         return LISTBOX_HandleMouseWheel( wnd, descr, wParam );
2960     case WM_LBUTTONDOWN:
2961         return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2962                                           (INT16)LOWORD(lParam),
2963                                           (INT16)HIWORD(lParam) );
2964     case WM_LBUTTONDBLCLK:
2965         if (descr->style & LBS_NOTIFY)
2966             SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2967         return 0;
2968     case WM_MOUSEMOVE:
2969         if (GetCapture() == hwnd)
2970             LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2971                                      (INT16)HIWORD(lParam) );
2972         return 0;
2973     case WM_LBUTTONUP:
2974         return LISTBOX_HandleLButtonUp( wnd, descr );
2975     case WM_KEYDOWN:
2976         return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2977     case WM_CHAR:
2978     {
2979         WCHAR charW;
2980         if(unicode)
2981             charW = (WCHAR)wParam;
2982         else
2983         {
2984             CHAR charA = (CHAR)wParam;
2985             MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
2986         }
2987         return LISTBOX_HandleChar( wnd, descr, charW );
2988     }
2989     case WM_SYSTIMER:
2990         return LISTBOX_HandleSystemTimer( wnd, descr );
2991     case WM_ERASEBKGND:
2992         if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
2993         {
2994             RECT rect;
2995             HBRUSH hbrush = SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
2996                                               wParam, (LPARAM)wnd->hwndSelf );
2997             TRACE("hbrush = %04x\n", hbrush);
2998             if(!hbrush)
2999                 hbrush = GetSysColorBrush(COLOR_WINDOW);
3000             if(hbrush)
3001             {
3002                 GetClientRect(hwnd, &rect);
3003                 FillRect((HDC)wParam, &rect, hbrush);
3004             }
3005         }
3006         return 1;
3007     case WM_DROPFILES:
3008         if( !descr->lphc )
3009             return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3010                              SendMessageA( descr->owner, msg, wParam, lParam );
3011         break;
3012
3013     case WM_DROPOBJECT:
3014     case WM_QUERYDROPOBJECT:
3015     case WM_DRAGSELECT:
3016     case WM_DRAGMOVE:
3017         if( !descr->lphc )
3018         {
3019             LPDRAGINFO16 dragInfo = MapSL( lParam );
3020             dragInfo->l = LISTBOX_GetItemFromPoint( descr, dragInfo->pt.x,
3021                                                 dragInfo->pt.y );
3022             return SendMessage16( descr->owner, msg, wParam, lParam );
3023         }
3024         break;
3025
3026     default:
3027         if ((msg >= WM_USER) && (msg < 0xc000))
3028             WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
3029                  hwnd, msg, wParam, lParam );
3030         return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3031                          DefWindowProcA( hwnd, msg, wParam, lParam );
3032     }
3033     return 0;
3034 }
3035
3036 /***********************************************************************
3037  *           ListBoxWndProcA
3038  *
3039  * This is just a wrapper for the real wndproc, it only does window locking
3040  * and unlocking.
3041  */
3042 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3043 {
3044     WND*        wndPtr = WIN_FindWndPtr( hwnd );
3045     LRESULT     res = ListBoxWndProc_locked(wndPtr, msg, wParam, lParam, FALSE);
3046
3047     WIN_ReleaseWndPtr(wndPtr);
3048     return res;
3049 }
3050
3051 /***********************************************************************
3052  *           ListBoxWndProcW
3053  */
3054 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3055 {
3056     WND*        wndPtr = WIN_FindWndPtr( hwnd );
3057     LRESULT     res = ListBoxWndProc_locked(wndPtr, msg, wParam, lParam, TRUE);
3058
3059     WIN_ReleaseWndPtr(wndPtr);
3060     return res;
3061 }
3062
3063 /***********************************************************************
3064  *           ComboLBWndProc_locked
3065  *
3066  * The real combo listbox wndproc, but called with locked WND struct.
3067  */
3068 static LRESULT WINAPI ComboLBWndProc_locked( WND* wnd, UINT msg,
3069                                              WPARAM wParam, LPARAM lParam, BOOL unicode )
3070 {
3071     LRESULT lRet = 0;
3072     HWND hwnd;
3073
3074     if (wnd)
3075     {
3076         LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
3077
3078         TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
3079                      wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
3080
3081         hwnd = wnd->hwndSelf;
3082
3083         if( descr || msg == WM_CREATE )
3084         {
3085             LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
3086
3087             switch( msg )
3088             {
3089                 case WM_CREATE:
3090 #define lpcs    ((LPCREATESTRUCTA)lParam)
3091                      TRACE_(combo)("\tpassed parent handle = 0x%08x\n", 
3092                                   (UINT)lpcs->lpCreateParams);
3093
3094                      lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
3095 #undef  lpcs
3096                      return LISTBOX_Create( wnd, lphc );
3097                 case WM_MOUSEMOVE:
3098                      if ( (TWEAK_WineLook > WIN31_LOOK) &&
3099                           (CB_GETTYPE(lphc) != CBS_SIMPLE) )
3100                      {
3101                        POINT   mousePos;
3102                        BOOL    captured;
3103                        RECT    clientRect;
3104
3105                        mousePos.x = (INT16)LOWORD(lParam);
3106                        mousePos.y = (INT16)HIWORD(lParam);
3107
3108                        /*
3109                         * If we are in a dropdown combobox, we simulate that
3110                         * the mouse is captured to show the tracking of the item.
3111                         */
3112                        GetClientRect(hwnd, &clientRect);
3113
3114                        if (PtInRect( &clientRect, mousePos ))
3115                        {
3116                            captured = descr->captured;
3117                            descr->captured = TRUE;                       
3118                            
3119                            LISTBOX_HandleMouseMove( wnd, descr, 
3120                                                     mousePos.x, mousePos.y);
3121
3122                            descr->captured = captured;
3123
3124                        }
3125                        else
3126                        {
3127                            LISTBOX_HandleMouseMove( wnd, descr, 
3128                                                     mousePos.x, mousePos.y);
3129                        }
3130
3131                        return 0;
3132
3133                      }
3134                      else
3135                      {
3136                        /*
3137                         * If we are in Win3.1 look, go with the default behavior.
3138                         */
3139                        return unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3140                                         ListBoxWndProcA( hwnd, msg, wParam, lParam );
3141                      }
3142                 case WM_LBUTTONUP:
3143                      if (TWEAK_WineLook > WIN31_LOOK)
3144                      {
3145                        POINT mousePos;
3146                        RECT  clientRect;
3147
3148                        /*
3149                         * If the mouse button "up" is not in the listbox,
3150                         * we make sure there is no selection by re-selecting the
3151                         * item that was selected when the listbox was made visible.
3152                         */
3153                        mousePos.x = (INT16)LOWORD(lParam);
3154                        mousePos.y = (INT16)HIWORD(lParam);
3155
3156                        GetClientRect(hwnd, &clientRect);
3157
3158                        /*
3159                         * When the user clicks outside the combobox and the focus
3160                         * is lost, the owning combobox will send a fake buttonup with
3161                         * 0xFFFFFFF as the mouse location, we must also revert the
3162                         * selection to the original selection.
3163                         */
3164                        if ( (lParam == (LPARAM)-1) ||
3165                             (!PtInRect( &clientRect, mousePos )) )
3166                        {
3167                          LISTBOX_MoveCaret( wnd,
3168                                             descr, 
3169                                             lphc->droppedIndex, 
3170                                             FALSE );
3171                        }
3172                      }
3173                      return LISTBOX_HandleLButtonUp( wnd, descr );
3174                 case WM_LBUTTONDBLCLK:
3175                 case WM_LBUTTONDOWN:
3176                      return LISTBOX_HandleLButtonDownCombo(wnd, descr, msg, wParam, 
3177                                           (INT16)LOWORD(lParam),
3178                                           (INT16)HIWORD(lParam) );
3179                 case WM_NCACTIVATE:
3180                      return FALSE;
3181                 case WM_KEYDOWN:
3182                      if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3183                      {
3184                          /* for some reason(?) Windows makes it possible to
3185                           * show/hide ComboLBox by sending it WM_KEYDOWNs */
3186
3187                          if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3188                              ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3189                                && (wParam == VK_DOWN || wParam == VK_UP)) )
3190                          {
3191                              COMBO_FlipListbox( lphc, FALSE, FALSE );
3192                              return 0;
3193                          }
3194                      }
3195                      return LISTBOX_HandleKeyDown( wnd, descr, wParam );
3196
3197                 case LB_SETCURSEL16:
3198                 case LB_SETCURSEL:
3199                      lRet = unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3200                                       ListBoxWndProcA( hwnd, msg, wParam, lParam );
3201                      lRet =(lRet == LB_ERR) ? lRet : descr->selected_item; 
3202                      return lRet;
3203                 case WM_NCDESTROY:
3204                      if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3205                          lphc->hWndLBox = 0;
3206                      /* fall through */
3207
3208                 default:
3209                     return unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3210                                      ListBoxWndProcA( hwnd, msg, wParam, lParam );
3211             }
3212         }
3213         lRet = unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3214                          DefWindowProcA( hwnd, msg, wParam, lParam );
3215
3216         TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
3217     }
3218     return lRet;
3219 }
3220
3221 /***********************************************************************
3222  *           ComboLBWndProcA
3223  *
3224  *  NOTE: in Windows, winproc address of the ComboLBox is the same 
3225  *        as that of the Listbox.
3226  * 
3227  * This is just a wrapper for the real wndproc, it only does window locking
3228  * and unlocking.
3229  */
3230 LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg,
3231                                WPARAM wParam, LPARAM lParam )
3232 {
3233     WND *wnd = WIN_FindWndPtr( hwnd );
3234     LRESULT res = ComboLBWndProc_locked(wnd, msg, wParam, lParam, FALSE);
3235
3236     WIN_ReleaseWndPtr(wnd);
3237     return res;
3238 }
3239
3240 /***********************************************************************
3241  *           ComboLBWndProcW
3242  */
3243 LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3244 {
3245     WND *wnd = WIN_FindWndPtr( hwnd );
3246     LRESULT res = ComboLBWndProc_locked(wnd, msg, wParam, lParam, TRUE);
3247
3248     WIN_ReleaseWndPtr(wnd);
3249     return res;
3250 }