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