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