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