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