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