rpcrt4: Make a variable static.
[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 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             else
2662                 return LB_ERRSPACE;
2663         }
2664         wParam = LISTBOX_FindStringPos( descr, textW, FALSE );
2665         ret = LISTBOX_InsertString( descr, wParam, textW );
2666         if (!unicode && HAS_STRINGS(descr))
2667             HeapFree(GetProcessHeap(), 0, textW);
2668         return ret;
2669     }
2670
2671     case LB_INSERTSTRING16:
2672         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2673         wParam = (INT)(INT16)wParam;
2674         /* fall through */
2675     case LB_INSERTSTRING:
2676     {
2677         INT ret;
2678         LPWSTR textW;
2679         if(unicode || !HAS_STRINGS(descr))
2680             textW = (LPWSTR)lParam;
2681         else
2682         {
2683             LPSTR textA = (LPSTR)lParam;
2684             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2685             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2686                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2687             else
2688                 return LB_ERRSPACE;
2689         }
2690         ret = LISTBOX_InsertString( descr, wParam, textW );
2691         if(!unicode && HAS_STRINGS(descr))
2692             HeapFree(GetProcessHeap(), 0, textW);
2693         return ret;
2694     }
2695
2696     case LB_ADDFILE16:
2697         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2698         /* fall through */
2699     case LB_ADDFILE:
2700     {
2701         INT ret;
2702         LPWSTR textW;
2703         if(unicode || !HAS_STRINGS(descr))
2704             textW = (LPWSTR)lParam;
2705         else
2706         {
2707             LPSTR textA = (LPSTR)lParam;
2708             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2709             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2710                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2711             else
2712                 return LB_ERRSPACE;
2713         }
2714         wParam = LISTBOX_FindFileStrPos( descr, textW );
2715         ret = LISTBOX_InsertString( descr, wParam, textW );
2716         if(!unicode && HAS_STRINGS(descr))
2717             HeapFree(GetProcessHeap(), 0, textW);
2718         return ret;
2719     }
2720
2721     case LB_DELETESTRING16:
2722     case LB_DELETESTRING:
2723         if (LISTBOX_RemoveItem( descr, wParam) != LB_ERR)
2724             return descr->nb_items;
2725         else
2726         {
2727             SetLastError(ERROR_INVALID_INDEX);
2728             return LB_ERR;
2729         }
2730
2731     case LB_GETITEMDATA16:
2732     case LB_GETITEMDATA:
2733         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2734         {
2735             SetLastError(ERROR_INVALID_INDEX);
2736             return LB_ERR;
2737         }
2738         return descr->items[wParam].data;
2739
2740     case LB_SETITEMDATA16:
2741     case LB_SETITEMDATA:
2742         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2743         {
2744             SetLastError(ERROR_INVALID_INDEX);
2745             return LB_ERR;
2746         }
2747         descr->items[wParam].data = lParam;
2748         /* undocumented: returns TRUE, not LB_OKAY (0) */
2749         return TRUE;
2750
2751     case LB_GETCOUNT16:
2752     case LB_GETCOUNT:
2753         return descr->nb_items;
2754
2755     case LB_GETTEXT16:
2756         lParam = (LPARAM)MapSL(lParam);
2757         /* fall through */
2758     case LB_GETTEXT:
2759         return LISTBOX_GetText( descr, wParam, (LPWSTR)lParam, unicode );
2760
2761     case LB_GETTEXTLEN16:
2762         /* fall through */
2763     case LB_GETTEXTLEN:
2764         if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2765         {
2766             SetLastError(ERROR_INVALID_INDEX);
2767             return LB_ERR;
2768         }
2769         if (!HAS_STRINGS(descr)) return sizeof(DWORD);
2770         if (unicode) return strlenW( descr->items[wParam].str );
2771         return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
2772                                     strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
2773
2774     case LB_GETCURSEL16:
2775     case LB_GETCURSEL:
2776         if (descr->nb_items == 0)
2777             return LB_ERR;
2778         if (!IS_MULTISELECT(descr))
2779             return descr->selected_item;
2780         if (descr->selected_item != -1)
2781             return descr->selected_item;
2782         return descr->focus_item;
2783         /* otherwise, if the user tries to move the selection with the    */
2784         /* arrow keys, we will give the application something to choke on */
2785     case LB_GETTOPINDEX16:
2786     case LB_GETTOPINDEX:
2787         return descr->top_item;
2788
2789     case LB_GETITEMHEIGHT16:
2790     case LB_GETITEMHEIGHT:
2791         return LISTBOX_GetItemHeight( descr, wParam );
2792
2793     case LB_SETITEMHEIGHT16:
2794         lParam = LOWORD(lParam);
2795         /* fall through */
2796     case LB_SETITEMHEIGHT:
2797         return LISTBOX_SetItemHeight( descr, wParam, lParam, TRUE );
2798
2799     case LB_ITEMFROMPOINT:
2800         {
2801             POINT pt;
2802             RECT rect;
2803             int index;
2804             BOOL hit = TRUE;
2805
2806             /* The hiword of the return value is not a client area
2807                hittest as suggested by MSDN, but rather a hittest on
2808                the returned listbox item. */
2809
2810             if(descr->nb_items == 0)
2811                 return 0x1ffff;      /* win9x returns 0x10000, we copy winnt */
2812
2813             pt.x = (short)LOWORD(lParam);
2814             pt.y = (short)HIWORD(lParam);
2815
2816             SetRect(&rect, 0, 0, descr->width, descr->height);
2817
2818             if(!PtInRect(&rect, pt))
2819             {
2820                 pt.x = min(pt.x, rect.right - 1);
2821                 pt.x = max(pt.x, 0);
2822                 pt.y = min(pt.y, rect.bottom - 1);
2823                 pt.y = max(pt.y, 0);
2824                 hit = FALSE;
2825             }
2826
2827             index = LISTBOX_GetItemFromPoint(descr, pt.x, pt.y);
2828
2829             if(index == -1)
2830             {
2831                 index = descr->nb_items - 1;
2832                 hit = FALSE;
2833             }
2834             return MAKELONG(index, hit ? 0 : 1);
2835         }
2836
2837     case LB_SETCARETINDEX16:
2838     case LB_SETCARETINDEX:
2839         if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2840         if (LISTBOX_SetCaretIndex( descr, wParam, !lParam ) == LB_ERR)
2841             return LB_ERR;
2842         else if (ISWIN31)
2843              return wParam;
2844         else
2845              return LB_OKAY;
2846
2847     case LB_GETCARETINDEX16:
2848     case LB_GETCARETINDEX:
2849         return descr->focus_item;
2850
2851     case LB_SETTOPINDEX16:
2852     case LB_SETTOPINDEX:
2853         return LISTBOX_SetTopItem( descr, wParam, TRUE );
2854
2855     case LB_SETCOLUMNWIDTH16:
2856     case LB_SETCOLUMNWIDTH:
2857         return LISTBOX_SetColumnWidth( descr, wParam );
2858
2859     case LB_GETITEMRECT16:
2860         {
2861             RECT rect;
2862             RECT16 *r16 = MapSL(lParam);
2863             ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2864             r16->left   = rect.left;
2865             r16->top    = rect.top;
2866             r16->right  = rect.right;
2867             r16->bottom = rect.bottom;
2868         }
2869         return ret;
2870
2871     case LB_GETITEMRECT:
2872         return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2873
2874     case LB_FINDSTRING16:
2875         wParam = (INT)(INT16)wParam;
2876         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2877         /* fall through */
2878     case LB_FINDSTRING:
2879     {
2880         INT ret;
2881         LPWSTR textW;
2882         if(unicode || !HAS_STRINGS(descr))
2883             textW = (LPWSTR)lParam;
2884         else
2885         {
2886             LPSTR textA = (LPSTR)lParam;
2887             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2888             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2889                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2890         }
2891         ret = LISTBOX_FindString( descr, wParam, textW, FALSE );
2892         if(!unicode && HAS_STRINGS(descr))
2893             HeapFree(GetProcessHeap(), 0, textW);
2894         return ret;
2895     }
2896
2897     case LB_FINDSTRINGEXACT16:
2898         wParam = (INT)(INT16)wParam;
2899         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2900         /* fall through */
2901     case LB_FINDSTRINGEXACT:
2902     {
2903         INT ret;
2904         LPWSTR textW;
2905         if(unicode || !HAS_STRINGS(descr))
2906             textW = (LPWSTR)lParam;
2907         else
2908         {
2909             LPSTR textA = (LPSTR)lParam;
2910             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2911             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2912                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2913         }
2914         ret = LISTBOX_FindString( descr, wParam, textW, TRUE );
2915         if(!unicode && HAS_STRINGS(descr))
2916             HeapFree(GetProcessHeap(), 0, textW);
2917         return ret;
2918     }
2919
2920     case LB_SELECTSTRING16:
2921         wParam = (INT)(INT16)wParam;
2922         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2923         /* fall through */
2924     case LB_SELECTSTRING:
2925     {
2926         INT index;
2927         LPWSTR textW;
2928
2929         if(HAS_STRINGS(descr))
2930             TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2931                                                      debugstr_a((LPSTR)lParam));
2932         if(unicode || !HAS_STRINGS(descr))
2933             textW = (LPWSTR)lParam;
2934         else
2935         {
2936             LPSTR textA = (LPSTR)lParam;
2937             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2938             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2939                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2940         }
2941         index = LISTBOX_FindString( descr, wParam, textW, FALSE );
2942         if(!unicode && HAS_STRINGS(descr))
2943             HeapFree(GetProcessHeap(), 0, textW);
2944         if (index != LB_ERR)
2945         {
2946             LISTBOX_MoveCaret( descr, index, TRUE );
2947             LISTBOX_SetSelection( descr, index, TRUE, FALSE );
2948         }
2949         return index;
2950     }
2951
2952     case LB_GETSEL16:
2953         wParam = (INT)(INT16)wParam;
2954         /* fall through */
2955     case LB_GETSEL:
2956         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2957             return LB_ERR;
2958         return descr->items[wParam].selected;
2959
2960     case LB_SETSEL16:
2961         lParam = (INT)(INT16)lParam;
2962         /* fall through */
2963     case LB_SETSEL:
2964         return LISTBOX_SetSelection( descr, lParam, wParam, FALSE );
2965
2966     case LB_SETCURSEL16:
2967         wParam = (INT)(INT16)wParam;
2968         /* fall through */
2969     case LB_SETCURSEL:
2970         if (IS_MULTISELECT(descr)) return LB_ERR;
2971         LISTBOX_SetCaretIndex( descr, wParam, FALSE );
2972         ret = LISTBOX_SetSelection( descr, wParam, TRUE, FALSE );
2973         if (ret != LB_ERR) ret = descr->selected_item;
2974         return ret;
2975
2976     case LB_GETSELCOUNT16:
2977     case LB_GETSELCOUNT:
2978         return LISTBOX_GetSelCount( descr );
2979
2980     case LB_GETSELITEMS16:
2981         return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2982
2983     case LB_GETSELITEMS:
2984         return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2985
2986     case LB_SELITEMRANGE16:
2987     case LB_SELITEMRANGE:
2988         if (LOWORD(lParam) <= HIWORD(lParam))
2989             return LISTBOX_SelectItemRange( descr, LOWORD(lParam),
2990                                             HIWORD(lParam), wParam );
2991         else
2992             return LISTBOX_SelectItemRange( descr, HIWORD(lParam),
2993                                             LOWORD(lParam), wParam );
2994
2995     case LB_SELITEMRANGEEX16:
2996     case LB_SELITEMRANGEEX:
2997         if ((INT)lParam >= (INT)wParam)
2998             return LISTBOX_SelectItemRange( descr, wParam, lParam, TRUE );
2999         else
3000             return LISTBOX_SelectItemRange( descr, lParam, wParam, FALSE);
3001
3002     case LB_GETHORIZONTALEXTENT16:
3003     case LB_GETHORIZONTALEXTENT:
3004         return descr->horz_extent;
3005
3006     case LB_SETHORIZONTALEXTENT16:
3007     case LB_SETHORIZONTALEXTENT:
3008         return LISTBOX_SetHorizontalExtent( descr, wParam );
3009
3010     case LB_GETANCHORINDEX16:
3011     case LB_GETANCHORINDEX:
3012         return descr->anchor_item;
3013
3014     case LB_SETANCHORINDEX16:
3015         wParam = (INT)(INT16)wParam;
3016         /* fall through */
3017     case LB_SETANCHORINDEX:
3018         if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
3019         {
3020             SetLastError(ERROR_INVALID_INDEX);
3021             return LB_ERR;
3022         }
3023         descr->anchor_item = (INT)wParam;
3024         return LB_OKAY;
3025
3026     case LB_DIR16:
3027         /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
3028          * be set automatically (this is different in Win32) */
3029         if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
3030             lParam = (LPARAM)MapSL(lParam);
3031         /* fall through */
3032     case LB_DIR:
3033     {
3034         INT ret;
3035         LPWSTR textW;
3036         if(unicode)
3037             textW = (LPWSTR)lParam;
3038         else
3039         {
3040             LPSTR textA = (LPSTR)lParam;
3041             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
3042             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
3043                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
3044         }
3045         ret = LISTBOX_Directory( descr, wParam, textW, msg == LB_DIR );
3046         if(!unicode)
3047             HeapFree(GetProcessHeap(), 0, textW);
3048         return ret;
3049     }
3050
3051     case LB_GETLOCALE:
3052         return descr->locale;
3053
3054     case LB_SETLOCALE:
3055     {
3056         LCID ret;
3057         if (!IsValidLocale((LCID)wParam, LCID_INSTALLED))
3058             return LB_ERR;
3059         ret = descr->locale;
3060         descr->locale = (LCID)wParam;
3061         return ret;
3062     }
3063
3064     case LB_INITSTORAGE:
3065         return LISTBOX_InitStorage( descr, wParam );
3066
3067     case LB_SETCOUNT:
3068         return LISTBOX_SetCount( descr, (INT)wParam );
3069
3070     case LB_SETTABSTOPS16:
3071         return LISTBOX_SetTabStops( descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
3072
3073     case LB_SETTABSTOPS:
3074         return LISTBOX_SetTabStops( descr, wParam, (LPINT)lParam, FALSE );
3075
3076     case LB_CARETON16:
3077     case LB_CARETON:
3078         if (descr->caret_on)
3079             return LB_OKAY;
3080         descr->caret_on = TRUE;
3081         if ((descr->focus_item != -1) && (descr->in_focus))
3082             LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3083         return LB_OKAY;
3084
3085     case LB_CARETOFF16:
3086     case LB_CARETOFF:
3087         if (!descr->caret_on)
3088             return LB_OKAY;
3089         descr->caret_on = FALSE;
3090         if ((descr->focus_item != -1) && (descr->in_focus))
3091             LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3092         return LB_OKAY;
3093
3094     case LB_GETLISTBOXINFO:
3095         FIXME("LB_GETLISTBOXINFO: stub!\n");
3096         return 0;
3097
3098     case WM_DESTROY:
3099         return LISTBOX_Destroy( descr );
3100
3101     case WM_ENABLE:
3102         InvalidateRect( descr->self, NULL, TRUE );
3103         return 0;
3104
3105     case WM_SETREDRAW:
3106         LISTBOX_SetRedraw( descr, wParam != 0 );
3107         return 0;
3108
3109     case WM_GETDLGCODE:
3110         return DLGC_WANTARROWS | DLGC_WANTCHARS;
3111
3112     case WM_PRINTCLIENT:
3113     case WM_PAINT:
3114         {
3115             PAINTSTRUCT ps;
3116             HDC hdc = ( wParam ) ? ((HDC)wParam) :  BeginPaint( descr->self, &ps );
3117             ret = LISTBOX_Paint( descr, hdc );
3118             if( !wParam ) EndPaint( descr->self, &ps );
3119         }
3120         return ret;
3121     case WM_SIZE:
3122         LISTBOX_UpdateSize( descr );
3123         return 0;
3124     case WM_GETFONT:
3125         return (LRESULT)descr->font;
3126     case WM_SETFONT:
3127         LISTBOX_SetFont( descr, (HFONT)wParam );
3128         if (lParam) InvalidateRect( descr->self, 0, TRUE );
3129         return 0;
3130     case WM_SETFOCUS:
3131         descr->in_focus = TRUE;
3132         descr->caret_on = TRUE;
3133         if (descr->focus_item != -1)
3134             LISTBOX_DrawFocusRect( descr, TRUE );
3135         SEND_NOTIFICATION( descr, LBN_SETFOCUS );
3136         return 0;
3137     case WM_KILLFOCUS:
3138         descr->in_focus = FALSE;
3139         if ((descr->focus_item != -1) && descr->caret_on)
3140             LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3141         SEND_NOTIFICATION( descr, LBN_KILLFOCUS );
3142         return 0;
3143     case WM_HSCROLL:
3144         return LISTBOX_HandleHScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3145     case WM_VSCROLL:
3146         return LISTBOX_HandleVScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3147     case WM_MOUSEWHEEL:
3148         if (wParam & (MK_SHIFT | MK_CONTROL))
3149             return DefWindowProcW( descr->self, msg, wParam, lParam );
3150         return LISTBOX_HandleMouseWheel( descr, (SHORT)HIWORD(wParam) );
3151     case WM_LBUTTONDOWN:
3152         if (lphc)
3153             return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3154                                                   (INT16)LOWORD(lParam),
3155                                                   (INT16)HIWORD(lParam) );
3156         return LISTBOX_HandleLButtonDown( descr, wParam,
3157                                           (INT16)LOWORD(lParam),
3158                                           (INT16)HIWORD(lParam) );
3159     case WM_LBUTTONDBLCLK:
3160         if (lphc)
3161             return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3162                                                   (INT16)LOWORD(lParam),
3163                                                   (INT16)HIWORD(lParam) );
3164         if (descr->style & LBS_NOTIFY)
3165             SEND_NOTIFICATION( descr, LBN_DBLCLK );
3166         return 0;
3167     case WM_MOUSEMOVE:
3168         if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) )
3169         {
3170             BOOL    captured = descr->captured;
3171             POINT   mousePos;
3172             RECT    clientRect;
3173
3174             mousePos.x = (INT16)LOWORD(lParam);
3175             mousePos.y = (INT16)HIWORD(lParam);
3176
3177             /*
3178              * If we are in a dropdown combobox, we simulate that
3179              * the mouse is captured to show the tracking of the item.
3180              */
3181             if (GetClientRect(descr->self, &clientRect) && PtInRect( &clientRect, mousePos ))
3182                 descr->captured = TRUE;
3183
3184             LISTBOX_HandleMouseMove( descr, mousePos.x, mousePos.y);
3185
3186             descr->captured = captured;
3187         } 
3188         else if (GetCapture() == descr->self)
3189         {
3190             LISTBOX_HandleMouseMove( descr, (INT16)LOWORD(lParam),
3191                                      (INT16)HIWORD(lParam) );
3192         }
3193         return 0;
3194     case WM_LBUTTONUP:
3195         if (lphc)
3196         {
3197             POINT mousePos;
3198             RECT  clientRect;
3199
3200             /*
3201              * If the mouse button "up" is not in the listbox,
3202              * we make sure there is no selection by re-selecting the
3203              * item that was selected when the listbox was made visible.
3204              */
3205             mousePos.x = (INT16)LOWORD(lParam);
3206             mousePos.y = (INT16)HIWORD(lParam);
3207
3208             GetClientRect(descr->self, &clientRect);
3209
3210             /*
3211              * When the user clicks outside the combobox and the focus
3212              * is lost, the owning combobox will send a fake buttonup with
3213              * 0xFFFFFFF as the mouse location, we must also revert the
3214              * selection to the original selection.
3215              */
3216             if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) )
3217                 LISTBOX_MoveCaret( descr, lphc->droppedIndex, FALSE );
3218         }
3219         return LISTBOX_HandleLButtonUp( descr );
3220     case WM_KEYDOWN:
3221         if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3222         {
3223             /* for some reason Windows makes it possible to
3224              * show/hide ComboLBox by sending it WM_KEYDOWNs */
3225
3226             if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3227                 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3228                   && (wParam == VK_DOWN || wParam == VK_UP)) )
3229             {
3230                 COMBO_FlipListbox( lphc, FALSE, FALSE );
3231                 return 0;
3232             }
3233         }
3234         return LISTBOX_HandleKeyDown( descr, wParam );
3235     case WM_CHAR:
3236     {
3237         WCHAR charW;
3238         if(unicode)
3239             charW = (WCHAR)wParam;
3240         else
3241         {
3242             CHAR charA = (CHAR)wParam;
3243             MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
3244         }
3245         return LISTBOX_HandleChar( descr, charW );
3246     }
3247     case WM_SYSTIMER:
3248         return LISTBOX_HandleSystemTimer( descr );
3249     case WM_ERASEBKGND:
3250         if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
3251         {
3252             RECT rect;
3253             HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
3254                                               wParam, (LPARAM)descr->self );
3255             TRACE("hbrush = %p\n", hbrush);
3256             if(!hbrush)
3257                 hbrush = GetSysColorBrush(COLOR_WINDOW);
3258             if(hbrush)
3259             {
3260                 GetClientRect(descr->self, &rect);
3261                 FillRect((HDC)wParam, &rect, hbrush);
3262             }
3263         }
3264         return 1;
3265     case WM_DROPFILES:
3266         if( lphc ) return 0;
3267         return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3268                          SendMessageA( descr->owner, msg, wParam, lParam );
3269
3270     case WM_NCDESTROY:
3271         if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3272             lphc->hWndLBox = 0;
3273         break;
3274
3275     case WM_NCACTIVATE:
3276         if (lphc) return 0;
3277         break;
3278
3279     default:
3280         if ((msg >= WM_USER) && (msg < 0xc000))
3281             WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3282                  hwnd, msg, wParam, lParam );
3283     }
3284
3285     return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3286                      DefWindowProcA( hwnd, msg, wParam, lParam );
3287 }
3288
3289 /***********************************************************************
3290  *           ListBoxWndProcA
3291  */
3292 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3293 {
3294     return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3295 }
3296
3297 /***********************************************************************
3298  *           ListBoxWndProcW
3299  */
3300 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3301 {
3302     return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE );
3303 }