wintrust: Add a helper function to initialize chain creation parameters.
[wine] / dlls / user32 / listbox.c
1 /*
2  * Listbox controls
3  *
4  * Copyright 1996 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * NOTES
21  *
22  * This code was audited for completeness against the documented features
23  * of Comctl32.dll version 6.0 on Oct. 9, 2004, by Dimitrie O. Paun.
24  * 
25  * Unless otherwise noted, we believe this code to be complete, as per
26  * the specification mentioned above.
27  * If you discover missing features, or bugs, please note them below.
28  *
29  * TODO:
30  *    - GetListBoxInfo()
31  *    - LB_GETLISTBOXINFO
32  *    - LBS_NODATA
33  */
34
35 #include <string.h>
36 #include <stdlib.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include "windef.h"
40 #include "winbase.h"
41 #include "wingdi.h"
42 #include "wine/winuser16.h"
43 #include "wine/unicode.h"
44 #include "user_private.h"
45 #include "controls.h"
46 #include "wine/debug.h"
47
48 WINE_DEFAULT_DEBUG_CHANNEL(listbox);
49
50 /* Items array granularity */
51 #define LB_ARRAY_GRANULARITY 16
52
53 /* Scrolling timeout in ms */
54 #define LB_SCROLL_TIMEOUT 50
55
56 /* Listbox system timer id */
57 #define LB_TIMER_ID  2
58
59 /* flag listbox changed while setredraw false - internal style */
60 #define LBS_DISPLAYCHANGED 0x80000000
61
62 /* Item structure */
63 typedef struct
64 {
65     LPWSTR    str;       /* Item text */
66     BOOL      selected;  /* Is item selected? */
67     UINT      height;    /* Item height (only for OWNERDRAWVARIABLE) */
68     ULONG_PTR data;      /* User data */
69 } LB_ITEMDATA;
70
71 /* Listbox structure */
72 typedef struct
73 {
74     HWND        self;           /* Our own window handle */
75     HWND        owner;          /* Owner window to send notifications to */
76     UINT        style;          /* Window style */
77     INT         width;          /* Window width */
78     INT         height;         /* Window height */
79     LB_ITEMDATA  *items;        /* Array of items */
80     INT         nb_items;       /* Number of items */
81     INT         top_item;       /* Top visible item */
82     INT         selected_item;  /* Selected item */
83     INT         focus_item;     /* Item that has the focus */
84     INT         anchor_item;    /* Anchor item for extended selection */
85     INT         item_height;    /* Default item height */
86     INT         page_size;      /* Items per listbox page */
87     INT         column_width;   /* Column width for multi-column listboxes */
88     INT         horz_extent;    /* Horizontal extent (0 if no hscroll) */
89     INT         horz_pos;       /* Horizontal position */
90     INT         nb_tabs;        /* Number of tabs in array */
91     INT        *tabs;           /* Array of tabs */
92     INT         avg_char_width; /* Average width of characters */
93     BOOL        caret_on;       /* Is caret on? */
94     BOOL        captured;       /* Is mouse captured? */
95     BOOL        in_focus;
96     HFONT       font;           /* Current font */
97     LCID          locale;       /* Current locale for string comparisons */
98     LPHEADCOMBO   lphc;         /* ComboLBox */
99 } LB_DESCR;
100
101
102 #define IS_OWNERDRAW(descr) \
103     ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
104
105 #define HAS_STRINGS(descr) \
106     (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
107
108
109 #define IS_MULTISELECT(descr) \
110     ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
111      !((descr)->style & LBS_NOSEL))
112
113 #define SEND_NOTIFICATION(descr,code) \
114     (SendMessageW( (descr)->owner, WM_COMMAND, \
115      MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
116
117 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
118
119 /* Current timer status */
120 typedef enum
121 {
122     LB_TIMER_NONE,
123     LB_TIMER_UP,
124     LB_TIMER_LEFT,
125     LB_TIMER_DOWN,
126     LB_TIMER_RIGHT
127 } TIMER_DIRECTION;
128
129 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
130
131 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
132 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
133
134 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect );
135
136 /*********************************************************************
137  * listbox class descriptor
138  */
139 const struct builtin_class_descr LISTBOX_builtin_class =
140 {
141     "ListBox",            /* name */
142     CS_DBLCLKS /*| CS_PARENTDC*/,  /* style */
143     ListBoxWndProcA,      /* procA */
144     ListBoxWndProcW,      /* procW */
145     sizeof(LB_DESCR *),   /* extra */
146     IDC_ARROW,            /* cursor */
147     0                     /* brush */
148 };
149
150
151 /*********************************************************************
152  * combolbox class descriptor
153  */
154 const struct builtin_class_descr COMBOLBOX_builtin_class =
155 {
156     "ComboLBox",          /* name */
157     CS_DBLCLKS | CS_SAVEBITS,  /* style */
158     ListBoxWndProcA,      /* procA */
159     ListBoxWndProcW,      /* procW */
160     sizeof(LB_DESCR *),   /* extra */
161     IDC_ARROW,            /* cursor */
162     0                     /* brush */
163 };
164
165
166 /* check whether app is a Win 3.1 app */
167 static inline BOOL is_old_app( LB_DESCR *descr )
168 {
169     return (GetExpWinVer16( GetWindowLongPtrW(descr->self, GWLP_HINSTANCE) ) & 0xFF00 ) == 0x0300;
170 }
171
172
173 /***********************************************************************
174  *           LISTBOX_GetCurrentPageSize
175  *
176  * Return the current page size
177  */
178 static INT LISTBOX_GetCurrentPageSize( LB_DESCR *descr )
179 {
180     INT i, height;
181     if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
182     for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
183     {
184         if ((height += descr->items[i].height) > descr->height) break;
185     }
186     if (i == descr->top_item) return 1;
187     else return i - descr->top_item;
188 }
189
190
191 /***********************************************************************
192  *           LISTBOX_GetMaxTopIndex
193  *
194  * Return the maximum possible index for the top of the listbox.
195  */
196 static INT LISTBOX_GetMaxTopIndex( LB_DESCR *descr )
197 {
198     INT max, page;
199
200     if (descr->style & LBS_OWNERDRAWVARIABLE)
201     {
202         page = descr->height;
203         for (max = descr->nb_items - 1; max >= 0; max--)
204             if ((page -= descr->items[max].height) < 0) break;
205         if (max < descr->nb_items - 1) max++;
206     }
207     else if (descr->style & LBS_MULTICOLUMN)
208     {
209         if ((page = descr->width / descr->column_width) < 1) page = 1;
210         max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
211         max = (max - page) * descr->page_size;
212     }
213     else
214     {
215         max = descr->nb_items - descr->page_size;
216     }
217     if (max < 0) max = 0;
218     return max;
219 }
220
221
222 /***********************************************************************
223  *           LISTBOX_UpdateScroll
224  *
225  * Update the scrollbars. Should be called whenever the content
226  * of the listbox changes.
227  */
228 static void LISTBOX_UpdateScroll( LB_DESCR *descr )
229 {
230     SCROLLINFO info;
231
232     /* Check the listbox scroll bar flags individually before we call
233        SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
234        no WS_VSCROLL, we end up with an uninitialized, visible horizontal
235        scroll bar when we do not need one.
236     if (!(descr->style & WS_VSCROLL)) return;
237     */
238
239     /*   It is important that we check descr->style, and not wnd->dwStyle,
240        for WS_VSCROLL, as the former is exactly the one passed in
241        argument to CreateWindow.
242          In Windows (and from now on in Wine :) a listbox created
243        with such a style (no WS_SCROLL) does not update
244        the scrollbar with listbox-related data, thus letting
245        the programmer use it for his/her own purposes. */
246
247     if (descr->style & LBS_NOREDRAW) return;
248     info.cbSize = sizeof(info);
249
250     if (descr->style & LBS_MULTICOLUMN)
251     {
252         info.nMin  = 0;
253         info.nMax  = (descr->nb_items - 1) / descr->page_size;
254         info.nPos  = descr->top_item / descr->page_size;
255         info.nPage = descr->width / descr->column_width;
256         if (info.nPage < 1) info.nPage = 1;
257         info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
258         if (descr->style & LBS_DISABLENOSCROLL)
259             info.fMask |= SIF_DISABLENOSCROLL;
260         if (descr->style & WS_HSCROLL)
261             SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
262         info.nMax = 0;
263         info.fMask = SIF_RANGE;
264         if (descr->style & WS_VSCROLL)
265             SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
266     }
267     else
268     {
269         info.nMin  = 0;
270         info.nMax  = descr->nb_items - 1;
271         info.nPos  = descr->top_item;
272         info.nPage = LISTBOX_GetCurrentPageSize( descr );
273         info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
274         if (descr->style & LBS_DISABLENOSCROLL)
275             info.fMask |= SIF_DISABLENOSCROLL;
276         if (descr->style & WS_VSCROLL)
277             SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
278
279         if (descr->horz_extent)
280         {
281             info.nMin  = 0;
282             info.nMax  = descr->horz_extent - 1;
283             info.nPos  = descr->horz_pos;
284             info.nPage = descr->width;
285             info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
286             if (descr->style & LBS_DISABLENOSCROLL)
287                 info.fMask |= SIF_DISABLENOSCROLL;
288             if (descr->style & WS_HSCROLL)
289                 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
290         }
291     }
292 }
293
294
295 /***********************************************************************
296  *           LISTBOX_SetTopItem
297  *
298  * Set the top item of the listbox, scrolling up or down if necessary.
299  */
300 static LRESULT LISTBOX_SetTopItem( LB_DESCR *descr, INT index, BOOL scroll )
301 {
302     INT max = LISTBOX_GetMaxTopIndex( descr );
303     if (index > max) index = max;
304     if (index < 0) index = 0;
305     if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
306     if (descr->top_item == index) return LB_OKAY;
307     if (descr->style & LBS_MULTICOLUMN)
308     {
309         INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
310         if (scroll && (abs(diff) < descr->width))
311             ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
312                               SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
313
314         else
315             scroll = FALSE;
316     }
317     else if (scroll)
318     {
319         INT diff;
320         if (descr->style & LBS_OWNERDRAWVARIABLE)
321         {
322             INT i;
323             diff = 0;
324             if (index > descr->top_item)
325             {
326                 for (i = index - 1; i >= descr->top_item; i--)
327                     diff -= descr->items[i].height;
328             }
329             else
330             {
331                 for (i = index; i < descr->top_item; i++)
332                     diff += descr->items[i].height;
333             }
334         }
335         else
336             diff = (descr->top_item - index) * descr->item_height;
337
338         if (abs(diff) < descr->height)
339             ScrollWindowEx( descr->self, 0, diff, NULL, NULL, 0, NULL,
340                             SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
341         else
342             scroll = FALSE;
343     }
344     if (!scroll) InvalidateRect( descr->self, NULL, TRUE );
345     descr->top_item = index;
346     LISTBOX_UpdateScroll( descr );
347     return LB_OKAY;
348 }
349
350
351 /***********************************************************************
352  *           LISTBOX_UpdatePage
353  *
354  * Update the page size. Should be called when the size of
355  * the client area or the item height changes.
356  */
357 static void LISTBOX_UpdatePage( LB_DESCR *descr )
358 {
359     INT page_size;
360
361     if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
362                        page_size = 1;
363     if (page_size == descr->page_size) return;
364     descr->page_size = page_size;
365     if (descr->style & LBS_MULTICOLUMN)
366         InvalidateRect( descr->self, NULL, TRUE );
367     LISTBOX_SetTopItem( descr, descr->top_item, FALSE );
368 }
369
370
371 /***********************************************************************
372  *           LISTBOX_UpdateSize
373  *
374  * Update the size of the listbox. Should be called when the size of
375  * the client area changes.
376  */
377 static void LISTBOX_UpdateSize( LB_DESCR *descr )
378 {
379     RECT rect;
380
381     GetClientRect( descr->self, &rect );
382     descr->width  = rect.right - rect.left;
383     descr->height = rect.bottom - rect.top;
384     if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
385     {
386         INT remaining;
387         RECT rect;
388
389         GetWindowRect( descr->self, &rect );
390         if(descr->item_height != 0)
391             remaining = descr->height % descr->item_height;
392         else
393             remaining = 0;
394         if ((descr->height > descr->item_height) && remaining)
395         {
396             if (is_old_app(descr))
397             { /* give a margin for error to 16 bits programs - if we need
398                  less than the height of the nonclient area, round to the
399                  *next* number of items */
400                 int ncheight = rect.bottom - rect.top - descr->height;
401                 if ((descr->item_height - remaining) <= ncheight)
402                     remaining = remaining - descr->item_height;
403             }
404             TRACE("[%p]: changing height %d -> %d\n",
405                   descr->self, descr->height, descr->height - remaining );
406             SetWindowPos( descr->self, 0, 0, 0, rect.right - rect.left,
407                           rect.bottom - rect.top - remaining,
408                           SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
409             return;
410         }
411     }
412     TRACE("[%p]: new size = %d,%d\n", descr->self, descr->width, descr->height );
413     LISTBOX_UpdatePage( descr );
414     LISTBOX_UpdateScroll( descr );
415
416     /* Invalidate the focused item so it will be repainted correctly */
417     if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
418     {
419         InvalidateRect( descr->self, &rect, FALSE );
420     }
421 }
422
423
424 /***********************************************************************
425  *           LISTBOX_GetItemRect
426  *
427  * Get the rectangle enclosing an item, in listbox client coordinates.
428  * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
429  */
430 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect )
431 {
432     /* Index <= 0 is legal even on empty listboxes */
433     if (index && (index >= descr->nb_items))
434     {
435         memset(rect, 0, sizeof(*rect));
436         SetLastError(ERROR_INVALID_INDEX);
437         return LB_ERR;
438     }
439     SetRect( rect, 0, 0, descr->width, descr->height );
440     if (descr->style & LBS_MULTICOLUMN)
441     {
442         INT col = (index / descr->page_size) -
443                         (descr->top_item / descr->page_size);
444         rect->left += col * descr->column_width;
445         rect->right = rect->left + descr->column_width;
446         rect->top += (index % descr->page_size) * descr->item_height;
447         rect->bottom = rect->top + descr->item_height;
448     }
449     else if (descr->style & LBS_OWNERDRAWVARIABLE)
450     {
451         INT i;
452         rect->right += descr->horz_pos;
453         if ((index >= 0) && (index < descr->nb_items))
454         {
455             if (index < descr->top_item)
456             {
457                 for (i = descr->top_item-1; i >= index; i--)
458                     rect->top -= descr->items[i].height;
459             }
460             else
461             {
462                 for (i = descr->top_item; i < index; i++)
463                     rect->top += descr->items[i].height;
464             }
465             rect->bottom = rect->top + descr->items[index].height;
466
467         }
468     }
469     else
470     {
471         rect->top += (index - descr->top_item) * descr->item_height;
472         rect->bottom = rect->top + descr->item_height;
473         rect->right += descr->horz_pos;
474     }
475
476     return ((rect->left < descr->width) && (rect->right > 0) &&
477             (rect->top < descr->height) && (rect->bottom > 0));
478 }
479
480
481 /***********************************************************************
482  *           LISTBOX_GetItemFromPoint
483  *
484  * Return the item nearest from point (x,y) (in client coordinates).
485  */
486 static INT LISTBOX_GetItemFromPoint( LB_DESCR *descr, INT x, INT y )
487 {
488     INT index = descr->top_item;
489
490     if (!descr->nb_items) return -1;  /* No items */
491     if (descr->style & LBS_OWNERDRAWVARIABLE)
492     {
493         INT pos = 0;
494         if (y >= 0)
495         {
496             while (index < descr->nb_items)
497             {
498                 if ((pos += descr->items[index].height) > y) break;
499                 index++;
500             }
501         }
502         else
503         {
504             while (index > 0)
505             {
506                 index--;
507                 if ((pos -= descr->items[index].height) <= y) break;
508             }
509         }
510     }
511     else if (descr->style & LBS_MULTICOLUMN)
512     {
513         if (y >= descr->item_height * descr->page_size) return -1;
514         if (y >= 0) index += y / descr->item_height;
515         if (x >= 0) index += (x / descr->column_width) * descr->page_size;
516         else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
517     }
518     else
519     {
520         index += (y / descr->item_height);
521     }
522     if (index < 0) return 0;
523     if (index >= descr->nb_items) return -1;
524     return index;
525 }
526
527
528 /***********************************************************************
529  *           LISTBOX_PaintItem
530  *
531  * Paint an item.
532  */
533 static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect, 
534                                INT index, UINT action, BOOL ignoreFocus )
535 {
536     LB_ITEMDATA *item = NULL;
537     if (index < descr->nb_items) item = &descr->items[index];
538
539     if (IS_OWNERDRAW(descr))
540     {
541         DRAWITEMSTRUCT dis;
542         RECT r;
543         HRGN hrgn;
544
545         if (!item)
546         {
547             if (action == ODA_FOCUS)
548                 DrawFocusRect( hdc, rect );
549             else
550                 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
551             return;
552         }
553
554         /* some programs mess with the clipping region when
555         drawing the item, *and* restore the previous region
556         after they are done, so a region has better to exist
557         else everything ends clipped */
558         GetClientRect(descr->self, &r);
559         hrgn = CreateRectRgnIndirect(&r);
560         SelectClipRgn( hdc, hrgn);
561         DeleteObject( hrgn );
562
563         dis.CtlType      = ODT_LISTBOX;
564         dis.CtlID        = GetWindowLongPtrW( descr->self, GWLP_ID );
565         dis.hwndItem     = descr->self;
566         dis.itemAction   = action;
567         dis.hDC          = hdc;
568         dis.itemID       = index;
569         dis.itemState    = 0;
570         if (item->selected) dis.itemState |= ODS_SELECTED;
571         if (!ignoreFocus && (descr->focus_item == index) &&
572             (descr->caret_on) &&
573             (descr->in_focus)) dis.itemState |= ODS_FOCUS;
574         if (!IsWindowEnabled(descr->self)) dis.itemState |= ODS_DISABLED;
575         dis.itemData     = item->data;
576         dis.rcItem       = *rect;
577         TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%d,%d-%d,%d\n",
578               descr->self, index, item ? debugstr_w(item->str) : "", action,
579               dis.itemState, rect->left, rect->top, rect->right, rect->bottom );
580         SendMessageW(descr->owner, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
581     }
582     else
583     {
584         COLORREF oldText = 0, oldBk = 0;
585
586         if (action == ODA_FOCUS)
587         {
588             DrawFocusRect( hdc, rect );
589             return;
590         }
591         if (item && item->selected)
592         {
593             oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
594             oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
595         }
596
597         TRACE("[%p]: painting %d (%s) action=%02x rect=%d,%d-%d,%d\n",
598               descr->self, index, item ? debugstr_w(item->str) : "", action,
599               rect->left, rect->top, rect->right, rect->bottom );
600         if (!item)
601             ExtTextOutW( hdc, rect->left + 1, rect->top,
602                            ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
603         else if (!(descr->style & LBS_USETABSTOPS))
604             ExtTextOutW( hdc, rect->left + 1, rect->top,
605                          ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
606                          strlenW(item->str), NULL );
607         else
608         {
609             /* Output empty string to paint background in the full width. */
610             ExtTextOutW( hdc, rect->left + 1, rect->top,
611                          ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
612             TabbedTextOutW( hdc, rect->left + 1 , rect->top,
613                             item->str, strlenW(item->str),
614                             descr->nb_tabs, descr->tabs, 0);
615         }
616         if (item && item->selected)
617         {
618             SetBkColor( hdc, oldBk );
619             SetTextColor( hdc, oldText );
620         }
621         if (!ignoreFocus && (descr->focus_item == index) &&
622             (descr->caret_on) &&
623             (descr->in_focus)) DrawFocusRect( hdc, rect );
624     }
625 }
626
627
628 /***********************************************************************
629  *           LISTBOX_SetRedraw
630  *
631  * Change the redraw flag.
632  */
633 static void LISTBOX_SetRedraw( LB_DESCR *descr, BOOL on )
634 {
635     if (on)
636     {
637         if (!(descr->style & LBS_NOREDRAW)) return;
638         descr->style &= ~LBS_NOREDRAW;
639         if (descr->style & LBS_DISPLAYCHANGED)
640         {     /* page was changed while setredraw false, refresh automatically */
641             InvalidateRect(descr->self, NULL, TRUE);
642             if ((descr->top_item + descr->page_size) > descr->nb_items)
643             {      /* reset top of page if less than number of items/page */
644                 descr->top_item = descr->nb_items - descr->page_size;
645                 if (descr->top_item < 0) descr->top_item = 0;
646             }
647             descr->style &= ~LBS_DISPLAYCHANGED;
648         }
649         LISTBOX_UpdateScroll( descr );
650     }
651     else descr->style |= LBS_NOREDRAW;
652 }
653
654
655 /***********************************************************************
656  *           LISTBOX_RepaintItem
657  *
658  * Repaint a single item synchronously.
659  */
660 static void LISTBOX_RepaintItem( LB_DESCR *descr, INT index, UINT action )
661 {
662     HDC hdc;
663     RECT rect;
664     HFONT oldFont = 0;
665     HBRUSH hbrush, oldBrush = 0;
666
667     /* Do not repaint the item if the item is not visible */
668     if (!IsWindowVisible(descr->self)) return;
669     if (descr->style & LBS_NOREDRAW)
670     {
671         descr->style |= LBS_DISPLAYCHANGED;
672         return;
673     }
674     if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
675     if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
676     if (descr->font) oldFont = SelectObject( hdc, descr->font );
677     hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
678                                    (WPARAM)hdc, (LPARAM)descr->self );
679     if (hbrush) oldBrush = SelectObject( hdc, hbrush );
680     if (!IsWindowEnabled(descr->self))
681         SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
682     SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
683     LISTBOX_PaintItem( descr, hdc, &rect, index, action, FALSE );
684     if (oldFont) SelectObject( hdc, oldFont );
685     if (oldBrush) SelectObject( hdc, oldBrush );
686     ReleaseDC( descr->self, hdc );
687 }
688
689
690 /***********************************************************************
691  *           LISTBOX_InitStorage
692  */
693 static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items )
694 {
695     LB_ITEMDATA *item;
696
697     nb_items += LB_ARRAY_GRANULARITY - 1;
698     nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
699     if (descr->items) {
700         nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
701         item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
702                               nb_items * sizeof(LB_ITEMDATA));
703     }
704     else {
705         item = HeapAlloc( GetProcessHeap(), 0,
706                               nb_items * sizeof(LB_ITEMDATA));
707     }
708
709     if (!item)
710     {
711         SEND_NOTIFICATION( descr, LBN_ERRSPACE );
712         return LB_ERRSPACE;
713     }
714     descr->items = item;
715     return LB_OKAY;
716 }
717
718
719 /***********************************************************************
720  *           LISTBOX_SetTabStops
721  */
722 static BOOL LISTBOX_SetTabStops( LB_DESCR *descr, INT count, LPINT tabs, BOOL short_ints )
723 {
724     INT i;
725
726     if (!(descr->style & LBS_USETABSTOPS))
727     {
728         SetLastError(ERROR_LB_WITHOUT_TABSTOPS);
729         return FALSE;
730     }
731
732     HeapFree( GetProcessHeap(), 0, descr->tabs );
733     if (!(descr->nb_tabs = count))
734     {
735         descr->tabs = NULL;
736         return TRUE;
737     }
738     if (!(descr->tabs = HeapAlloc( GetProcessHeap(), 0,
739                                             descr->nb_tabs * sizeof(INT) )))
740         return FALSE;
741     if (short_ints)
742     {
743         INT i;
744         LPINT16 p = (LPINT16)tabs;
745
746         TRACE("[%p]: settabstops ", descr->self );
747         for (i = 0; i < descr->nb_tabs; i++) {
748             descr->tabs[i] = *p++<<1; /* FIXME */
749             TRACE("%hd ", descr->tabs[i]);
750         }
751         TRACE("\n");
752     }
753     else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
754
755     /* convert into "dialog units"*/
756     for (i = 0; i < descr->nb_tabs; i++)
757         descr->tabs[i] = MulDiv(descr->tabs[i], descr->avg_char_width, 4);
758
759     return TRUE;
760 }
761
762
763 /***********************************************************************
764  *           LISTBOX_GetText
765  */
766 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPWSTR buffer, BOOL unicode )
767 {
768     if ((index < 0) || (index >= descr->nb_items))
769     {
770         SetLastError(ERROR_INVALID_INDEX);
771         return LB_ERR;
772     }
773     if (HAS_STRINGS(descr))
774     {
775         if (!buffer)
776         {
777             DWORD len = strlenW(descr->items[index].str);
778             if( unicode )
779                 return len;
780             return WideCharToMultiByte( CP_ACP, 0, descr->items[index].str, len,
781                                         NULL, 0, NULL, NULL );
782         }
783
784         TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
785
786         if(unicode)
787         {
788             strcpyW( buffer, descr->items[index].str );
789             return strlenW(buffer);
790         }
791         else
792         {
793             return WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, (LPSTR)buffer, 0x7FFFFFFF, NULL, NULL) - 1;
794         }
795     } else {
796         if (buffer)
797             *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
798         return sizeof(DWORD);
799     }
800 }
801
802 static inline INT LISTBOX_lstrcmpiW( LCID lcid, LPCWSTR str1, LPCWSTR str2 )
803 {
804     INT ret = CompareStringW( lcid, NORM_IGNORECASE, str1, -1, str2, -1 );
805     if (ret == CSTR_LESS_THAN)
806         return -1;
807     if (ret == CSTR_EQUAL)
808         return 0;
809     if (ret == CSTR_GREATER_THAN)
810         return 1;
811     return -1;
812 }
813
814 /***********************************************************************
815  *           LISTBOX_FindStringPos
816  *
817  * Find the nearest string located before a given string in sort order.
818  * If 'exact' is TRUE, return an error if we don't get an exact match.
819  */
820 static INT LISTBOX_FindStringPos( LB_DESCR *descr, LPCWSTR str, BOOL exact )
821 {
822     INT index, min, max, res = -1;
823
824     if (!(descr->style & LBS_SORT)) return -1;  /* Add it at the end */
825     min = 0;
826     max = descr->nb_items;
827     while (min != max)
828     {
829         index = (min + max) / 2;
830         if (HAS_STRINGS(descr))
831             res = LISTBOX_lstrcmpiW( descr->locale, str, descr->items[index].str);
832         else
833         {
834             COMPAREITEMSTRUCT cis;
835             UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
836
837             cis.CtlType    = ODT_LISTBOX;
838             cis.CtlID      = id;
839             cis.hwndItem   = descr->self;
840             /* note that some application (MetaStock) expects the second item
841              * to be in the listbox */
842             cis.itemID1    = -1;
843             cis.itemData1  = (ULONG_PTR)str;
844             cis.itemID2    = index;
845             cis.itemData2  = descr->items[index].data;
846             cis.dwLocaleId = descr->locale;
847             res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
848         }
849         if (!res) return index;
850         if (res < 0) max = index;
851         else min = index + 1;
852     }
853     return exact ? -1 : max;
854 }
855
856
857 /***********************************************************************
858  *           LISTBOX_FindFileStrPos
859  *
860  * Find the nearest string located before a given string in directory
861  * sort order (i.e. first files, then directories, then drives).
862  */
863 static INT LISTBOX_FindFileStrPos( LB_DESCR *descr, LPCWSTR str )
864 {
865     INT min, max, res = -1;
866
867     if (!HAS_STRINGS(descr))
868         return LISTBOX_FindStringPos( descr, str, FALSE );
869     min = 0;
870     max = descr->nb_items;
871     while (min != max)
872     {
873         INT index = (min + max) / 2;
874         LPCWSTR p = descr->items[index].str;
875         if (*p == '[')  /* drive or directory */
876         {
877             if (*str != '[') res = -1;
878             else if (p[1] == '-')  /* drive */
879             {
880                 if (str[1] == '-') res = str[2] - p[2];
881                 else res = -1;
882             }
883             else  /* directory */
884             {
885                 if (str[1] == '-') res = 1;
886                 else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
887             }
888         }
889         else  /* filename */
890         {
891             if (*str == '[') res = 1;
892             else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
893         }
894         if (!res) return index;
895         if (res < 0) max = index;
896         else min = index + 1;
897     }
898     return max;
899 }
900
901
902 /***********************************************************************
903  *           LISTBOX_FindString
904  *
905  * Find the item beginning with a given string.
906  */
907 static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exact )
908 {
909     INT i;
910     LB_ITEMDATA *item;
911
912     if (start >= descr->nb_items) start = -1;
913     item = descr->items + start + 1;
914     if (HAS_STRINGS(descr))
915     {
916         if (!str || ! str[0] ) return LB_ERR;
917         if (exact)
918         {
919             for (i = start + 1; i < descr->nb_items; i++, item++)
920                 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
921             for (i = 0, item = descr->items; i <= start; i++, item++)
922                 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
923         }
924         else
925         {
926  /* Special case for drives and directories: ignore prefix */
927 #define CHECK_DRIVE(item) \
928     if ((item)->str[0] == '[') \
929     { \
930         if (!strncmpiW( str, (item)->str+1, len )) return i; \
931         if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
932         return i; \
933     }
934
935             INT len = strlenW(str);
936             for (i = start + 1; i < descr->nb_items; i++, item++)
937             {
938                if (!strncmpiW( str, item->str, len )) return i;
939                CHECK_DRIVE(item);
940             }
941             for (i = 0, item = descr->items; i <= start; i++, item++)
942             {
943                if (!strncmpiW( str, item->str, len )) return i;
944                CHECK_DRIVE(item);
945             }
946 #undef CHECK_DRIVE
947         }
948     }
949     else
950     {
951         if (exact && (descr->style & LBS_SORT))
952             /* If sorted, use a WM_COMPAREITEM binary search */
953             return LISTBOX_FindStringPos( descr, str, TRUE );
954
955         /* Otherwise use a linear search */
956         for (i = start + 1; i < descr->nb_items; i++, item++)
957             if (item->data == (ULONG_PTR)str) return i;
958         for (i = 0, item = descr->items; i <= start; i++, item++)
959             if (item->data == (ULONG_PTR)str) return i;
960     }
961     return LB_ERR;
962 }
963
964
965 /***********************************************************************
966  *           LISTBOX_GetSelCount
967  */
968 static LRESULT LISTBOX_GetSelCount( LB_DESCR *descr )
969 {
970     INT i, count;
971     LB_ITEMDATA *item = descr->items;
972
973     if (!(descr->style & LBS_MULTIPLESEL) ||
974         (descr->style & LBS_NOSEL))
975       return LB_ERR;
976     for (i = count = 0; i < descr->nb_items; i++, item++)
977         if (item->selected) count++;
978     return count;
979 }
980
981
982 /***********************************************************************
983  *           LISTBOX_GetSelItems16
984  */
985 static LRESULT LISTBOX_GetSelItems16( LB_DESCR *descr, INT16 max, LPINT16 array )
986 {
987     INT i, count;
988     LB_ITEMDATA *item = descr->items;
989
990     if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
991     for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
992         if (item->selected) array[count++] = (INT16)i;
993     return count;
994 }
995
996
997 /***********************************************************************
998  *           LISTBOX_GetSelItems
999  */
1000 static LRESULT LISTBOX_GetSelItems( LB_DESCR *descr, INT max, LPINT array )
1001 {
1002     INT i, count;
1003     LB_ITEMDATA *item = descr->items;
1004
1005     if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1006     for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
1007         if (item->selected) array[count++] = i;
1008     return count;
1009 }
1010
1011
1012 /***********************************************************************
1013  *           LISTBOX_Paint
1014  */
1015 static LRESULT LISTBOX_Paint( LB_DESCR *descr, HDC hdc )
1016 {
1017     INT i, col_pos = descr->page_size - 1;
1018     RECT rect;
1019     RECT focusRect = {-1, -1, -1, -1};
1020     HFONT oldFont = 0;
1021     HBRUSH hbrush, oldBrush = 0;
1022
1023     if (descr->style & LBS_NOREDRAW) return 0;
1024
1025     SetRect( &rect, 0, 0, descr->width, descr->height );
1026     if (descr->style & LBS_MULTICOLUMN)
1027         rect.right = rect.left + descr->column_width;
1028     else if (descr->horz_pos)
1029     {
1030         SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
1031         rect.right += descr->horz_pos;
1032     }
1033
1034     if (descr->font) oldFont = SelectObject( hdc, descr->font );
1035     hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1036                                    (WPARAM)hdc, (LPARAM)descr->self );
1037     if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1038     if (!IsWindowEnabled(descr->self)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1039
1040     if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1041         (descr->in_focus))
1042     {
1043         /* Special case for empty listbox: paint focus rect */
1044         rect.bottom = rect.top + descr->item_height;
1045         ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1046                      &rect, NULL, 0, NULL );
1047         LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, FALSE );
1048         rect.top = rect.bottom;
1049     }
1050
1051     /* Paint all the item, regarding the selection
1052        Focus state will be painted after  */
1053
1054     for (i = descr->top_item; i < descr->nb_items; i++)
1055     {
1056         if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1057             rect.bottom = rect.top + descr->item_height;
1058         else
1059             rect.bottom = rect.top + descr->items[i].height;
1060
1061         if (i == descr->focus_item)
1062         {
1063             /* keep the focus rect, to paint the focus item after */
1064             focusRect.left = rect.left;
1065             focusRect.right = rect.right;
1066             focusRect.top = rect.top;
1067             focusRect.bottom = rect.bottom;
1068         }
1069         LISTBOX_PaintItem( descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1070         rect.top = rect.bottom;
1071
1072         if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1073         {
1074             if (!IS_OWNERDRAW(descr))
1075             {
1076                 /* Clear the bottom of the column */
1077                 if (rect.top < descr->height)
1078                 {
1079                     rect.bottom = descr->height;
1080                     ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1081                                    &rect, NULL, 0, NULL );
1082                 }
1083             }
1084
1085             /* Go to the next column */
1086             rect.left += descr->column_width;
1087             rect.right += descr->column_width;
1088             rect.top = 0;
1089             col_pos = descr->page_size - 1;
1090         }
1091         else
1092         {
1093             col_pos--;
1094             if (rect.top >= descr->height) break;
1095         }
1096     }
1097
1098     /* Paint the focus item now */
1099     if (focusRect.top != focusRect.bottom &&
1100         descr->caret_on && descr->in_focus)
1101         LISTBOX_PaintItem( descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1102
1103     if (!IS_OWNERDRAW(descr))
1104     {
1105         /* Clear the remainder of the client area */
1106         if (rect.top < descr->height)
1107         {
1108             rect.bottom = descr->height;
1109             ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1110                            &rect, NULL, 0, NULL );
1111         }
1112         if (rect.right < descr->width)
1113         {
1114             rect.left   = rect.right;
1115             rect.right  = descr->width;
1116             rect.top    = 0;
1117             rect.bottom = descr->height;
1118             ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1119                            &rect, NULL, 0, NULL );
1120         }
1121     }
1122     if (oldFont) SelectObject( hdc, oldFont );
1123     if (oldBrush) SelectObject( hdc, oldBrush );
1124     return 0;
1125 }
1126
1127
1128 /***********************************************************************
1129  *           LISTBOX_InvalidateItems
1130  *
1131  * Invalidate all items from a given item. If the specified item is not
1132  * visible, nothing happens.
1133  */
1134 static void LISTBOX_InvalidateItems( LB_DESCR *descr, INT index )
1135 {
1136     RECT rect;
1137
1138     if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1139     {
1140         if (descr->style & LBS_NOREDRAW)
1141         {
1142             descr->style |= LBS_DISPLAYCHANGED;
1143             return;
1144         }
1145         rect.bottom = descr->height;
1146         InvalidateRect( descr->self, &rect, TRUE );
1147         if (descr->style & LBS_MULTICOLUMN)
1148         {
1149             /* Repaint the other columns */
1150             rect.left  = rect.right;
1151             rect.right = descr->width;
1152             rect.top   = 0;
1153             InvalidateRect( descr->self, &rect, TRUE );
1154         }
1155     }
1156 }
1157
1158 static void LISTBOX_InvalidateItemRect( LB_DESCR *descr, INT index )
1159 {
1160     RECT rect;
1161
1162     if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1163         InvalidateRect( descr->self, &rect, TRUE );
1164 }
1165
1166 /***********************************************************************
1167  *           LISTBOX_GetItemHeight
1168  */
1169 static LRESULT LISTBOX_GetItemHeight( LB_DESCR *descr, INT index )
1170 {
1171     if (descr->style & LBS_OWNERDRAWVARIABLE && descr->nb_items > 0)
1172     {
1173         if ((index < 0) || (index >= descr->nb_items))
1174         {
1175             SetLastError(ERROR_INVALID_INDEX);
1176             return LB_ERR;
1177         }
1178         return descr->items[index].height;
1179     }
1180     else return descr->item_height;
1181 }
1182
1183
1184 /***********************************************************************
1185  *           LISTBOX_SetItemHeight
1186  */
1187 static LRESULT LISTBOX_SetItemHeight( LB_DESCR *descr, INT index, INT height, BOOL repaint )
1188 {
1189     if (height > MAXBYTE)
1190         return -1;
1191
1192     if (!height) height = 1;
1193
1194     if (descr->style & LBS_OWNERDRAWVARIABLE)
1195     {
1196         if ((index < 0) || (index >= descr->nb_items))
1197         {
1198             SetLastError(ERROR_INVALID_INDEX);
1199             return LB_ERR;
1200         }
1201         TRACE("[%p]: item %d height = %d\n", descr->self, index, height );
1202         descr->items[index].height = height;
1203         LISTBOX_UpdateScroll( descr );
1204         if (repaint)
1205             LISTBOX_InvalidateItems( descr, index );
1206     }
1207     else if (height != descr->item_height)
1208     {
1209         TRACE("[%p]: new height = %d\n", descr->self, height );
1210         descr->item_height = height;
1211         LISTBOX_UpdatePage( descr );
1212         LISTBOX_UpdateScroll( descr );
1213         if (repaint)
1214             InvalidateRect( descr->self, 0, TRUE );
1215     }
1216     return LB_OKAY;
1217 }
1218
1219
1220 /***********************************************************************
1221  *           LISTBOX_SetHorizontalPos
1222  */
1223 static void LISTBOX_SetHorizontalPos( LB_DESCR *descr, INT pos )
1224 {
1225     INT diff;
1226
1227     if (pos > descr->horz_extent - descr->width)
1228         pos = descr->horz_extent - descr->width;
1229     if (pos < 0) pos = 0;
1230     if (!(diff = descr->horz_pos - pos)) return;
1231     TRACE("[%p]: new horz pos = %d\n", descr->self, pos );
1232     descr->horz_pos = pos;
1233     LISTBOX_UpdateScroll( descr );
1234     if (abs(diff) < descr->width)
1235     {
1236         RECT rect;
1237         /* Invalidate the focused item so it will be repainted correctly */
1238         if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
1239             InvalidateRect( descr->self, &rect, TRUE );
1240         ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
1241                           SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1242     }
1243     else
1244         InvalidateRect( descr->self, NULL, TRUE );
1245 }
1246
1247
1248 /***********************************************************************
1249  *           LISTBOX_SetHorizontalExtent
1250  */
1251 static LRESULT LISTBOX_SetHorizontalExtent( LB_DESCR *descr, INT extent )
1252 {
1253     if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1254         return LB_OKAY;
1255     if (extent <= 0) extent = 1;
1256     if (extent == descr->horz_extent) return LB_OKAY;
1257     TRACE("[%p]: new horz extent = %d\n", descr->self, extent );
1258     descr->horz_extent = extent;
1259     if (descr->horz_pos > extent - descr->width)
1260         LISTBOX_SetHorizontalPos( descr, extent - descr->width );
1261     else
1262         LISTBOX_UpdateScroll( descr );
1263     return LB_OKAY;
1264 }
1265
1266
1267 /***********************************************************************
1268  *           LISTBOX_SetColumnWidth
1269  */
1270 static LRESULT LISTBOX_SetColumnWidth( LB_DESCR *descr, INT width)
1271 {
1272     if (width == descr->column_width) return LB_OKAY;
1273     TRACE("[%p]: new column width = %d\n", descr->self, width );
1274     descr->column_width = width;
1275     LISTBOX_UpdatePage( descr );
1276     return LB_OKAY;
1277 }
1278
1279
1280 /***********************************************************************
1281  *           LISTBOX_SetFont
1282  *
1283  * Returns the item height.
1284  */
1285 static INT LISTBOX_SetFont( LB_DESCR *descr, HFONT font )
1286 {
1287     HDC hdc;
1288     HFONT oldFont = 0;
1289     const char *alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1290     SIZE sz;
1291
1292     descr->font = font;
1293
1294     if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE )))
1295     {
1296         ERR("unable to get DC.\n" );
1297         return 16;
1298     }
1299     if (font) oldFont = SelectObject( hdc, font );
1300     GetTextExtentPointA( hdc, alphabet, 52, &sz);
1301     if (oldFont) SelectObject( hdc, oldFont );
1302     ReleaseDC( descr->self, hdc );
1303
1304     descr->avg_char_width = (sz.cx / 26 + 1) / 2;
1305     if (!IS_OWNERDRAW(descr))
1306         LISTBOX_SetItemHeight( descr, 0, sz.cy, FALSE );
1307     return sz.cy;
1308 }
1309
1310
1311 /***********************************************************************
1312  *           LISTBOX_MakeItemVisible
1313  *
1314  * Make sure that a given item is partially or fully visible.
1315  */
1316 static void LISTBOX_MakeItemVisible( LB_DESCR *descr, INT index, BOOL fully )
1317 {
1318     INT top;
1319
1320     if (index <= descr->top_item) top = index;
1321     else if (descr->style & LBS_MULTICOLUMN)
1322     {
1323         INT cols = descr->width;
1324         if (!fully) cols += descr->column_width - 1;
1325         if (cols >= descr->column_width) cols /= descr->column_width;
1326         else cols = 1;
1327         if (index < descr->top_item + (descr->page_size * cols)) return;
1328         top = index - descr->page_size * (cols - 1);
1329     }
1330     else if (descr->style & LBS_OWNERDRAWVARIABLE)
1331     {
1332         INT height = fully ? descr->items[index].height : 1;
1333         for (top = index; top > descr->top_item; top--)
1334             if ((height += descr->items[top-1].height) > descr->height) break;
1335     }
1336     else
1337     {
1338         if (index < descr->top_item + descr->page_size) return;
1339         if (!fully && (index == descr->top_item + descr->page_size) &&
1340             (descr->height > (descr->page_size * descr->item_height))) return;
1341         top = index - descr->page_size + 1;
1342     }
1343     LISTBOX_SetTopItem( descr, top, TRUE );
1344 }
1345
1346 /***********************************************************************
1347  *           LISTBOX_SetCaretIndex
1348  *
1349  * NOTES
1350  *   index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1351  *
1352  */
1353 static LRESULT LISTBOX_SetCaretIndex( LB_DESCR *descr, INT index, BOOL fully_visible )
1354 {
1355     INT oldfocus = descr->focus_item;
1356
1357     if (descr->style & LBS_NOSEL) return LB_ERR;
1358     if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1359     if (index == oldfocus) return LB_OKAY;
1360     descr->focus_item = index;
1361     if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1362         LISTBOX_RepaintItem( descr, oldfocus, ODA_FOCUS );
1363
1364     LISTBOX_MakeItemVisible( descr, index, fully_visible );
1365     if (descr->caret_on && (descr->in_focus))
1366         LISTBOX_RepaintItem( descr, index, ODA_FOCUS );
1367
1368     return LB_OKAY;
1369 }
1370
1371
1372 /***********************************************************************
1373  *           LISTBOX_SelectItemRange
1374  *
1375  * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1376  */
1377 static LRESULT LISTBOX_SelectItemRange( LB_DESCR *descr, INT first,
1378                                         INT last, BOOL on )
1379 {
1380     INT i;
1381
1382     /* A few sanity checks */
1383
1384     if (descr->style & LBS_NOSEL) return LB_ERR;
1385     if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1386
1387     if (!descr->nb_items) return LB_OKAY;
1388
1389     if (last == -1 || last >= descr->nb_items) last = descr->nb_items - 1;
1390     if (first < 0) first = 0;
1391     if (last < first) return LB_OKAY;
1392
1393     if (on)  /* Turn selection on */
1394     {
1395         for (i = first; i <= last; i++)
1396         {
1397             if (descr->items[i].selected) continue;
1398             descr->items[i].selected = TRUE;
1399             LISTBOX_InvalidateItemRect(descr, i);
1400         }
1401     }
1402     else  /* Turn selection off */
1403     {
1404         for (i = first; i <= last; i++)
1405         {
1406             if (!descr->items[i].selected) continue;
1407             descr->items[i].selected = FALSE;
1408             LISTBOX_InvalidateItemRect(descr, i);
1409         }
1410     }
1411     return LB_OKAY;
1412 }
1413
1414 /***********************************************************************
1415  *           LISTBOX_SetSelection
1416  */
1417 static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index,
1418                                      BOOL on, BOOL send_notify )
1419 {
1420     TRACE( "cur_sel=%d index=%d notify=%s\n",
1421            descr->selected_item, 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 %08lx 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         /* undocumented: returns TRUE, not LB_OKAY (0) */
2681         return TRUE;
2682
2683     case LB_GETCOUNT16:
2684     case LB_GETCOUNT:
2685         return descr->nb_items;
2686
2687     case LB_GETTEXT16:
2688         lParam = (LPARAM)MapSL(lParam);
2689         /* fall through */
2690     case LB_GETTEXT:
2691         return LISTBOX_GetText( descr, wParam, (LPWSTR)lParam, unicode );
2692
2693     case LB_GETTEXTLEN16:
2694         /* fall through */
2695     case LB_GETTEXTLEN:
2696         if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2697         {
2698             SetLastError(ERROR_INVALID_INDEX);
2699             return LB_ERR;
2700         }
2701         if (!HAS_STRINGS(descr)) return sizeof(DWORD);
2702         if (unicode) return strlenW( descr->items[wParam].str );
2703         return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
2704                                     strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
2705
2706     case LB_GETCURSEL16:
2707     case LB_GETCURSEL:
2708         if (descr->nb_items == 0)
2709             return LB_ERR;
2710         if (!IS_MULTISELECT(descr))
2711             return descr->selected_item;
2712         if (descr->selected_item != -1)
2713             return descr->selected_item;
2714         return descr->focus_item;
2715         /* otherwise, if the user tries to move the selection with the    */
2716         /* arrow keys, we will give the application something to choke on */
2717     case LB_GETTOPINDEX16:
2718     case LB_GETTOPINDEX:
2719         return descr->top_item;
2720
2721     case LB_GETITEMHEIGHT16:
2722     case LB_GETITEMHEIGHT:
2723         return LISTBOX_GetItemHeight( descr, wParam );
2724
2725     case LB_SETITEMHEIGHT16:
2726         lParam = LOWORD(lParam);
2727         /* fall through */
2728     case LB_SETITEMHEIGHT:
2729         return LISTBOX_SetItemHeight( descr, wParam, lParam, TRUE );
2730
2731     case LB_ITEMFROMPOINT:
2732         {
2733             POINT pt;
2734             RECT rect;
2735             int index;
2736             BOOL hit = TRUE;
2737
2738             /* The hiword of the return value is not a client area
2739                hittest as suggested by MSDN, but rather a hittest on
2740                the returned listbox item. */
2741
2742             if(descr->nb_items == 0)
2743                 return 0x1ffff;      /* win9x returns 0x10000, we copy winnt */
2744
2745             pt.x = (short)LOWORD(lParam);
2746             pt.y = (short)HIWORD(lParam);
2747
2748             SetRect(&rect, 0, 0, descr->width, descr->height);
2749
2750             if(!PtInRect(&rect, pt))
2751             {
2752                 pt.x = min(pt.x, rect.right - 1);
2753                 pt.x = max(pt.x, 0);
2754                 pt.y = min(pt.y, rect.bottom - 1);
2755                 pt.y = max(pt.y, 0);
2756                 hit = FALSE;
2757             }
2758
2759             index = LISTBOX_GetItemFromPoint(descr, pt.x, pt.y);
2760
2761             if(index == -1)
2762             {
2763                 index = descr->nb_items - 1;
2764                 hit = FALSE;
2765             }
2766             return MAKELONG(index, hit ? 0 : 1);
2767         }
2768
2769     case LB_SETCARETINDEX16:
2770     case LB_SETCARETINDEX:
2771         if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2772         if (LISTBOX_SetCaretIndex( descr, wParam, !lParam ) == LB_ERR)
2773             return LB_ERR;
2774         else if (ISWIN31)
2775              return wParam;
2776         else
2777              return LB_OKAY;
2778
2779     case LB_GETCARETINDEX16:
2780     case LB_GETCARETINDEX:
2781         return descr->focus_item;
2782
2783     case LB_SETTOPINDEX16:
2784     case LB_SETTOPINDEX:
2785         return LISTBOX_SetTopItem( descr, wParam, TRUE );
2786
2787     case LB_SETCOLUMNWIDTH16:
2788     case LB_SETCOLUMNWIDTH:
2789         return LISTBOX_SetColumnWidth( descr, wParam );
2790
2791     case LB_GETITEMRECT16:
2792         {
2793             RECT rect;
2794             RECT16 *r16 = MapSL(lParam);
2795             ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2796             r16->left   = rect.left;
2797             r16->top    = rect.top;
2798             r16->right  = rect.right;
2799             r16->bottom = rect.bottom;
2800         }
2801         return ret;
2802
2803     case LB_GETITEMRECT:
2804         return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2805
2806     case LB_FINDSTRING16:
2807         wParam = (INT)(INT16)wParam;
2808         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2809         /* fall through */
2810     case LB_FINDSTRING:
2811     {
2812         INT ret;
2813         LPWSTR textW;
2814         if(unicode || !HAS_STRINGS(descr))
2815             textW = (LPWSTR)lParam;
2816         else
2817         {
2818             LPSTR textA = (LPSTR)lParam;
2819             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2820             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2821                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2822         }
2823         ret = LISTBOX_FindString( descr, wParam, textW, FALSE );
2824         if(!unicode && HAS_STRINGS(descr))
2825             HeapFree(GetProcessHeap(), 0, textW);
2826         return ret;
2827     }
2828
2829     case LB_FINDSTRINGEXACT16:
2830         wParam = (INT)(INT16)wParam;
2831         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2832         /* fall through */
2833     case LB_FINDSTRINGEXACT:
2834     {
2835         INT ret;
2836         LPWSTR textW;
2837         if(unicode || !HAS_STRINGS(descr))
2838             textW = (LPWSTR)lParam;
2839         else
2840         {
2841             LPSTR textA = (LPSTR)lParam;
2842             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2843             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2844                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2845         }
2846         ret = LISTBOX_FindString( descr, wParam, textW, TRUE );
2847         if(!unicode && HAS_STRINGS(descr))
2848             HeapFree(GetProcessHeap(), 0, textW);
2849         return ret;
2850     }
2851
2852     case LB_SELECTSTRING16:
2853         wParam = (INT)(INT16)wParam;
2854         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2855         /* fall through */
2856     case LB_SELECTSTRING:
2857     {
2858         INT index;
2859         LPWSTR textW;
2860
2861         if(HAS_STRINGS(descr))
2862             TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2863                                                      debugstr_a((LPSTR)lParam));
2864         if(unicode || !HAS_STRINGS(descr))
2865             textW = (LPWSTR)lParam;
2866         else
2867         {
2868             LPSTR textA = (LPSTR)lParam;
2869             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2870             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2871                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2872         }
2873         index = LISTBOX_FindString( descr, wParam, textW, FALSE );
2874         if(!unicode && HAS_STRINGS(descr))
2875             HeapFree(GetProcessHeap(), 0, textW);
2876         if (index != LB_ERR)
2877         {
2878             LISTBOX_MoveCaret( descr, index, TRUE );
2879             LISTBOX_SetSelection( descr, index, TRUE, FALSE );
2880         }
2881         return index;
2882     }
2883
2884     case LB_GETSEL16:
2885         wParam = (INT)(INT16)wParam;
2886         /* fall through */
2887     case LB_GETSEL:
2888         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2889             return LB_ERR;
2890         return descr->items[wParam].selected;
2891
2892     case LB_SETSEL16:
2893         lParam = (INT)(INT16)lParam;
2894         /* fall through */
2895     case LB_SETSEL:
2896         return LISTBOX_SetSelection( descr, lParam, wParam, FALSE );
2897
2898     case LB_SETCURSEL16:
2899         wParam = (INT)(INT16)wParam;
2900         /* fall through */
2901     case LB_SETCURSEL:
2902         if (IS_MULTISELECT(descr)) return LB_ERR;
2903         LISTBOX_SetCaretIndex( descr, wParam, FALSE );
2904         ret = LISTBOX_SetSelection( descr, wParam, TRUE, FALSE );
2905         if (lphc && ret != LB_ERR) ret = descr->selected_item;
2906         return ret;
2907
2908     case LB_GETSELCOUNT16:
2909     case LB_GETSELCOUNT:
2910         return LISTBOX_GetSelCount( descr );
2911
2912     case LB_GETSELITEMS16:
2913         return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2914
2915     case LB_GETSELITEMS:
2916         return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2917
2918     case LB_SELITEMRANGE16:
2919     case LB_SELITEMRANGE:
2920         if (LOWORD(lParam) <= HIWORD(lParam))
2921             return LISTBOX_SelectItemRange( descr, LOWORD(lParam),
2922                                             HIWORD(lParam), wParam );
2923         else
2924             return LISTBOX_SelectItemRange( descr, HIWORD(lParam),
2925                                             LOWORD(lParam), wParam );
2926
2927     case LB_SELITEMRANGEEX16:
2928     case LB_SELITEMRANGEEX:
2929         if ((INT)lParam >= (INT)wParam)
2930             return LISTBOX_SelectItemRange( descr, wParam, lParam, TRUE );
2931         else
2932             return LISTBOX_SelectItemRange( descr, lParam, wParam, FALSE);
2933
2934     case LB_GETHORIZONTALEXTENT16:
2935     case LB_GETHORIZONTALEXTENT:
2936         return descr->horz_extent;
2937
2938     case LB_SETHORIZONTALEXTENT16:
2939     case LB_SETHORIZONTALEXTENT:
2940         return LISTBOX_SetHorizontalExtent( descr, wParam );
2941
2942     case LB_GETANCHORINDEX16:
2943     case LB_GETANCHORINDEX:
2944         return descr->anchor_item;
2945
2946     case LB_SETANCHORINDEX16:
2947         wParam = (INT)(INT16)wParam;
2948         /* fall through */
2949     case LB_SETANCHORINDEX:
2950         if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2951         {
2952             SetLastError(ERROR_INVALID_INDEX);
2953             return LB_ERR;
2954         }
2955         descr->anchor_item = (INT)wParam;
2956         return LB_OKAY;
2957
2958     case LB_DIR16:
2959         /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2960          * be set automatically (this is different in Win32) */
2961         if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2962             lParam = (LPARAM)MapSL(lParam);
2963         /* fall through */
2964     case LB_DIR:
2965     {
2966         INT ret;
2967         LPWSTR textW;
2968         if(unicode)
2969             textW = (LPWSTR)lParam;
2970         else
2971         {
2972             LPSTR textA = (LPSTR)lParam;
2973             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2974             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2975                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2976         }
2977         ret = LISTBOX_Directory( descr, wParam, textW, msg == LB_DIR );
2978         if(!unicode)
2979             HeapFree(GetProcessHeap(), 0, textW);
2980         return ret;
2981     }
2982
2983     case LB_GETLOCALE:
2984         return descr->locale;
2985
2986     case LB_SETLOCALE:
2987     {
2988         LCID ret;
2989         if (!IsValidLocale((LCID)wParam, LCID_INSTALLED))
2990             return LB_ERR;
2991         ret = descr->locale;
2992         descr->locale = (LCID)wParam;
2993         return ret;
2994     }
2995
2996     case LB_INITSTORAGE:
2997         return LISTBOX_InitStorage( descr, wParam );
2998
2999     case LB_SETCOUNT:
3000         return LISTBOX_SetCount( descr, (INT)wParam );
3001
3002     case LB_SETTABSTOPS16:
3003         return LISTBOX_SetTabStops( descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
3004
3005     case LB_SETTABSTOPS:
3006         return LISTBOX_SetTabStops( descr, wParam, (LPINT)lParam, FALSE );
3007
3008     case LB_CARETON16:
3009     case LB_CARETON:
3010         if (descr->caret_on)
3011             return LB_OKAY;
3012         descr->caret_on = TRUE;
3013         if ((descr->focus_item != -1) && (descr->in_focus))
3014             LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3015         return LB_OKAY;
3016
3017     case LB_CARETOFF16:
3018     case LB_CARETOFF:
3019         if (!descr->caret_on)
3020             return LB_OKAY;
3021         descr->caret_on = FALSE;
3022         if ((descr->focus_item != -1) && (descr->in_focus))
3023             LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3024         return LB_OKAY;
3025
3026     case LB_GETLISTBOXINFO:
3027         FIXME("LB_GETLISTBOXINFO: stub!\n");
3028         return 0;
3029
3030     case WM_DESTROY:
3031         return LISTBOX_Destroy( descr );
3032
3033     case WM_ENABLE:
3034         InvalidateRect( descr->self, NULL, TRUE );
3035         return 0;
3036
3037     case WM_SETREDRAW:
3038         LISTBOX_SetRedraw( descr, wParam != 0 );
3039         return 0;
3040
3041     case WM_GETDLGCODE:
3042         return DLGC_WANTARROWS | DLGC_WANTCHARS;
3043
3044     case WM_PRINTCLIENT:
3045     case WM_PAINT:
3046         {
3047             PAINTSTRUCT ps;
3048             HDC hdc = ( wParam ) ? ((HDC)wParam) :  BeginPaint( descr->self, &ps );
3049             ret = LISTBOX_Paint( descr, hdc );
3050             if( !wParam ) EndPaint( descr->self, &ps );
3051         }
3052         return ret;
3053     case WM_SIZE:
3054         LISTBOX_UpdateSize( descr );
3055         return 0;
3056     case WM_GETFONT:
3057         return (LRESULT)descr->font;
3058     case WM_SETFONT:
3059         LISTBOX_SetFont( descr, (HFONT)wParam );
3060         if (lParam) InvalidateRect( descr->self, 0, TRUE );
3061         return 0;
3062     case WM_SETFOCUS:
3063         descr->in_focus = TRUE;
3064         descr->caret_on = TRUE;
3065         if (descr->focus_item != -1)
3066             LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3067         SEND_NOTIFICATION( descr, LBN_SETFOCUS );
3068         return 0;
3069     case WM_KILLFOCUS:
3070         descr->in_focus = FALSE;
3071         if ((descr->focus_item != -1) && descr->caret_on)
3072             LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3073         SEND_NOTIFICATION( descr, LBN_KILLFOCUS );
3074         return 0;
3075     case WM_HSCROLL:
3076         return LISTBOX_HandleHScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3077     case WM_VSCROLL:
3078         return LISTBOX_HandleVScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3079     case WM_MOUSEWHEEL:
3080         if (wParam & (MK_SHIFT | MK_CONTROL))
3081             return DefWindowProcW( descr->self, msg, wParam, lParam );
3082         return LISTBOX_HandleMouseWheel( descr, (SHORT)HIWORD(wParam) );
3083     case WM_LBUTTONDOWN:
3084         if (lphc)
3085             return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3086                                                   (INT16)LOWORD(lParam),
3087                                                   (INT16)HIWORD(lParam) );
3088         return LISTBOX_HandleLButtonDown( descr, wParam,
3089                                           (INT16)LOWORD(lParam),
3090                                           (INT16)HIWORD(lParam) );
3091     case WM_LBUTTONDBLCLK:
3092         if (lphc)
3093             return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3094                                                   (INT16)LOWORD(lParam),
3095                                                   (INT16)HIWORD(lParam) );
3096         if (descr->style & LBS_NOTIFY)
3097             SEND_NOTIFICATION( descr, LBN_DBLCLK );
3098         return 0;
3099     case WM_MOUSEMOVE:
3100         if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) )
3101         {
3102             BOOL    captured = descr->captured;
3103             POINT   mousePos;
3104             RECT    clientRect;
3105
3106             mousePos.x = (INT16)LOWORD(lParam);
3107             mousePos.y = (INT16)HIWORD(lParam);
3108
3109             /*
3110              * If we are in a dropdown combobox, we simulate that
3111              * the mouse is captured to show the tracking of the item.
3112              */
3113             if (GetClientRect(descr->self, &clientRect) && PtInRect( &clientRect, mousePos ))
3114                 descr->captured = TRUE;
3115
3116             LISTBOX_HandleMouseMove( descr, mousePos.x, mousePos.y);
3117
3118             descr->captured = captured;
3119         } 
3120         else if (GetCapture() == descr->self)
3121         {
3122             LISTBOX_HandleMouseMove( descr, (INT16)LOWORD(lParam),
3123                                      (INT16)HIWORD(lParam) );
3124         }
3125         return 0;
3126     case WM_LBUTTONUP:
3127         if (lphc)
3128         {
3129             POINT mousePos;
3130             RECT  clientRect;
3131
3132             /*
3133              * If the mouse button "up" is not in the listbox,
3134              * we make sure there is no selection by re-selecting the
3135              * item that was selected when the listbox was made visible.
3136              */
3137             mousePos.x = (INT16)LOWORD(lParam);
3138             mousePos.y = (INT16)HIWORD(lParam);
3139
3140             GetClientRect(descr->self, &clientRect);
3141
3142             /*
3143              * When the user clicks outside the combobox and the focus
3144              * is lost, the owning combobox will send a fake buttonup with
3145              * 0xFFFFFFF as the mouse location, we must also revert the
3146              * selection to the original selection.
3147              */
3148             if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) )
3149                 LISTBOX_MoveCaret( descr, lphc->droppedIndex, FALSE );
3150         }
3151         return LISTBOX_HandleLButtonUp( descr );
3152     case WM_KEYDOWN:
3153         if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3154         {
3155             /* for some reason Windows makes it possible to
3156              * show/hide ComboLBox by sending it WM_KEYDOWNs */
3157
3158             if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3159                 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3160                   && (wParam == VK_DOWN || wParam == VK_UP)) )
3161             {
3162                 COMBO_FlipListbox( lphc, FALSE, FALSE );
3163                 return 0;
3164             }
3165         }
3166         return LISTBOX_HandleKeyDown( descr, wParam );
3167     case WM_CHAR:
3168     {
3169         WCHAR charW;
3170         if(unicode)
3171             charW = (WCHAR)wParam;
3172         else
3173         {
3174             CHAR charA = (CHAR)wParam;
3175             MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
3176         }
3177         return LISTBOX_HandleChar( descr, charW );
3178     }
3179     case WM_SYSTIMER:
3180         return LISTBOX_HandleSystemTimer( descr );
3181     case WM_ERASEBKGND:
3182         if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
3183         {
3184             RECT rect;
3185             HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
3186                                               wParam, (LPARAM)descr->self );
3187             TRACE("hbrush = %p\n", hbrush);
3188             if(!hbrush)
3189                 hbrush = GetSysColorBrush(COLOR_WINDOW);
3190             if(hbrush)
3191             {
3192                 GetClientRect(descr->self, &rect);
3193                 FillRect((HDC)wParam, &rect, hbrush);
3194             }
3195         }
3196         return 1;
3197     case WM_DROPFILES:
3198         if( lphc ) return 0;
3199         return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3200                          SendMessageA( descr->owner, msg, wParam, lParam );
3201
3202     case WM_NCDESTROY:
3203         if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3204             lphc->hWndLBox = 0;
3205         break;
3206
3207     case WM_NCACTIVATE:
3208         if (lphc) return 0;
3209         break;
3210
3211     default:
3212         if ((msg >= WM_USER) && (msg < 0xc000))
3213             WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3214                  hwnd, msg, wParam, lParam );
3215     }
3216
3217     return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3218                      DefWindowProcA( hwnd, msg, wParam, lParam );
3219 }
3220
3221 /***********************************************************************
3222  *           ListBoxWndProcA
3223  */
3224 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3225 {
3226     return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3227 }
3228
3229 /***********************************************************************
3230  *           ListBoxWndProcW
3231  */
3232 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3233 {
3234     return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE );
3235 }