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