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