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