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