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