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