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