Added stubs for SendIMEMessageEx[A,W].
[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     /* selected_item reflects last selected/unselected item on multiple sel */
1369     descr->selected_item = last;
1370
1371     if (on)  /* Turn selection on */
1372     {
1373         for (i = first; i <= last; i++)
1374         {
1375             if (descr->items[i].selected) continue;
1376             descr->items[i].selected = TRUE;
1377             LISTBOX_InvalidateItemRect(descr, i);
1378         }
1379         LISTBOX_SetCaretIndex( descr, last, TRUE );
1380     }
1381     else  /* Turn selection off */
1382     {
1383         for (i = first; i <= last; i++)
1384         {
1385             if (!descr->items[i].selected) continue;
1386             descr->items[i].selected = FALSE;
1387             LISTBOX_InvalidateItemRect(descr, i);
1388         }
1389     }
1390     return LB_OKAY;
1391 }
1392
1393 /***********************************************************************
1394  *           LISTBOX_SetSelection
1395  */
1396 static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index,
1397                                      BOOL on, BOOL send_notify )
1398 {
1399     TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1400
1401     if (descr->style & LBS_NOSEL)
1402     {
1403         descr->selected_item = index;
1404         return LB_ERR;
1405     }
1406     if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1407     if (descr->style & LBS_MULTIPLESEL)
1408     {
1409         if (index == -1)  /* Select all items */
1410             return LISTBOX_SelectItemRange( descr, 0, -1, on );
1411         else  /* Only one item */
1412             return LISTBOX_SelectItemRange( descr, index, index, on );
1413     }
1414     else
1415     {
1416         INT oldsel = descr->selected_item;
1417         if (index == oldsel) return LB_OKAY;
1418         if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1419         if (index != -1) descr->items[index].selected = TRUE;
1420         descr->selected_item = index;
1421         if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT );
1422         if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT );
1423         if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr,
1424                                (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1425         else
1426             if( descr->lphc ) /* set selection change flag for parent combo */
1427                 descr->lphc->wState |= CBF_SELCHANGE;
1428     }
1429     return LB_OKAY;
1430 }
1431
1432
1433 /***********************************************************************
1434  *           LISTBOX_MoveCaret
1435  *
1436  * Change the caret position and extend the selection to the new caret.
1437  */
1438 static void LISTBOX_MoveCaret( LB_DESCR *descr, INT index, BOOL fully_visible )
1439 {
1440     INT oldfocus = descr->focus_item;
1441
1442     if ((index <  0) || (index >= descr->nb_items))
1443         return;
1444
1445     /* Important, repaint needs to be done in this order if
1446        you want to mimic Windows behavior:
1447        1. Remove the focus and paint the item
1448        2. Remove the selection and paint the item(s)
1449        3. Set the selection and repaint the item(s)
1450        4. Set the focus to 'index' and repaint the item */
1451
1452     /* 1. remove the focus and repaint the item */
1453     descr->focus_item = -1;
1454     if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1455         LISTBOX_RepaintItem( descr, oldfocus, ODA_FOCUS );
1456
1457     /* 2. then turn off the previous selection */
1458     /* 3. repaint the new selected item */
1459     if (descr->style & LBS_EXTENDEDSEL)
1460     {
1461         if (descr->anchor_item != -1)
1462         {
1463             INT first = min( index, descr->anchor_item );
1464             INT last  = max( index, descr->anchor_item );
1465             if (first > 0)
1466                 LISTBOX_SelectItemRange( descr, 0, first - 1, FALSE );
1467             LISTBOX_SelectItemRange( descr, last + 1, -1, FALSE );
1468             LISTBOX_SelectItemRange( descr, first, last, TRUE );
1469         }
1470     }
1471     else if (!(descr->style & LBS_MULTIPLESEL))
1472     {
1473         /* Set selection to new caret item */
1474         LISTBOX_SetSelection( descr, index, TRUE, FALSE );
1475     }
1476
1477     /* 4. repaint the new item with the focus */
1478     descr->focus_item = index;
1479     LISTBOX_MakeItemVisible( descr, index, fully_visible );
1480     if (descr->caret_on && (descr->in_focus))
1481         LISTBOX_RepaintItem( descr, index, ODA_FOCUS );
1482 }
1483
1484
1485 /***********************************************************************
1486  *           LISTBOX_InsertItem
1487  */
1488 static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index,
1489                                    LPWSTR str, DWORD data )
1490 {
1491     LB_ITEMDATA *item;
1492     INT max_items;
1493     INT oldfocus = descr->focus_item;
1494
1495     if (index == -1) index = descr->nb_items;
1496     else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1497     if (!descr->items) max_items = 0;
1498     else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1499     if (descr->nb_items == max_items)
1500     {
1501         /* We need to grow the array */
1502         max_items += LB_ARRAY_GRANULARITY;
1503         if (descr->items)
1504             item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1505                                   max_items * sizeof(LB_ITEMDATA) );
1506         else
1507             item = HeapAlloc( GetProcessHeap(), 0,
1508                                   max_items * sizeof(LB_ITEMDATA) );
1509         if (!item)
1510         {
1511             SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1512             return LB_ERRSPACE;
1513         }
1514         descr->items = item;
1515     }
1516
1517     /* Insert the item structure */
1518
1519     item = &descr->items[index];
1520     if (index < descr->nb_items)
1521         RtlMoveMemory( item + 1, item,
1522                        (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1523     item->str      = str;
1524     item->data     = data;
1525     item->height   = 0;
1526     item->selected = FALSE;
1527     descr->nb_items++;
1528
1529     /* Get item height */
1530
1531     if (descr->style & LBS_OWNERDRAWVARIABLE)
1532     {
1533         MEASUREITEMSTRUCT mis;
1534         UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1535
1536         mis.CtlType    = ODT_LISTBOX;
1537         mis.CtlID      = id;
1538         mis.itemID     = index;
1539         mis.itemData   = descr->items[index].data;
1540         mis.itemHeight = descr->item_height;
1541         SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1542         item->height = mis.itemHeight ? mis.itemHeight : 1;
1543         TRACE("[%p]: measure item %d (%s) = %d\n",
1544               descr->self, index, str ? debugstr_w(str) : "", item->height );
1545     }
1546
1547     /* Repaint the items */
1548
1549     LISTBOX_UpdateScroll( descr );
1550     LISTBOX_InvalidateItems( descr, index );
1551
1552     /* Move selection and focused item */
1553     /* If listbox was empty, set focus to the first item */
1554     if (descr->nb_items == 1)
1555          LISTBOX_SetCaretIndex( descr, 0, FALSE );
1556     /* single select don't change selection index in win31 */
1557     else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1558     {
1559         descr->selected_item++;
1560         LISTBOX_SetSelection( descr, descr->selected_item-1, TRUE, FALSE );
1561     }
1562     else
1563     {
1564         if (index <= descr->selected_item)
1565         {
1566             descr->selected_item++;
1567             descr->focus_item = oldfocus; /* focus not changed */
1568         }
1569     }
1570     return LB_OKAY;
1571 }
1572
1573
1574 /***********************************************************************
1575  *           LISTBOX_InsertString
1576  */
1577 static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index, LPCWSTR str )
1578 {
1579     LPWSTR new_str = NULL;
1580     DWORD data = 0;
1581     LRESULT ret;
1582
1583     if (HAS_STRINGS(descr))
1584     {
1585         static const WCHAR empty_stringW[] = { 0 };
1586         if (!str) str = empty_stringW;
1587         if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1588         {
1589             SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1590             return LB_ERRSPACE;
1591         }
1592         strcpyW(new_str, str);
1593     }
1594     else data = (DWORD)str;
1595
1596     if (index == -1) index = descr->nb_items;
1597     if ((ret = LISTBOX_InsertItem( descr, index, new_str, data )) != 0)
1598     {
1599         HeapFree( GetProcessHeap(), 0, new_str );
1600         return ret;
1601     }
1602
1603     TRACE("[%p]: added item %d %s\n",
1604           descr->self, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1605     return index;
1606 }
1607
1608
1609 /***********************************************************************
1610  *           LISTBOX_DeleteItem
1611  *
1612  * Delete the content of an item. 'index' must be a valid index.
1613  */
1614 static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index )
1615 {
1616     /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1617      *       while Win95 sends it for all items with user data.
1618      *       It's probably better to send it too often than not
1619      *       often enough, so this is what we do here.
1620      */
1621     if (IS_OWNERDRAW(descr) || descr->items[index].data)
1622     {
1623         DELETEITEMSTRUCT dis;
1624         UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1625
1626         dis.CtlType  = ODT_LISTBOX;
1627         dis.CtlID    = id;
1628         dis.itemID   = index;
1629         dis.hwndItem = descr->self;
1630         dis.itemData = descr->items[index].data;
1631         SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1632     }
1633     if (HAS_STRINGS(descr) && descr->items[index].str)
1634         HeapFree( GetProcessHeap(), 0, descr->items[index].str );
1635 }
1636
1637
1638 /***********************************************************************
1639  *           LISTBOX_RemoveItem
1640  *
1641  * Remove an item from the listbox and delete its content.
1642  */
1643 static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index )
1644 {
1645     LB_ITEMDATA *item;
1646     INT max_items;
1647
1648     if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1649     else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1650
1651     /* We need to invalidate the original rect instead of the updated one. */
1652     LISTBOX_InvalidateItems( descr, index );
1653
1654     LISTBOX_DeleteItem( descr, index );
1655
1656     /* Remove the item */
1657
1658     item = &descr->items[index];
1659     if (index < descr->nb_items-1)
1660         RtlMoveMemory( item, item + 1,
1661                        (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1662     descr->nb_items--;
1663     if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1664
1665     /* Shrink the item array if possible */
1666
1667     max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1668     if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1669     {
1670         max_items -= LB_ARRAY_GRANULARITY;
1671         item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1672                             max_items * sizeof(LB_ITEMDATA) );
1673         if (item) descr->items = item;
1674     }
1675     /* Repaint the items */
1676
1677     LISTBOX_UpdateScroll( descr );
1678     /* if we removed the scrollbar, reset the top of the list
1679       (correct for owner-drawn ???) */
1680     if (descr->nb_items == descr->page_size)
1681         LISTBOX_SetTopItem( descr, 0, TRUE );
1682
1683     /* Move selection and focused item */
1684     if (!IS_MULTISELECT(descr))
1685     {
1686         if (index == descr->selected_item)
1687             descr->selected_item = -1;
1688         else if (index < descr->selected_item)
1689         {
1690             descr->selected_item--;
1691             if (ISWIN31) /* win 31 do not change the selected item number */
1692                LISTBOX_SetSelection( descr, descr->selected_item + 1, TRUE, FALSE);
1693         }
1694     }
1695
1696     if (descr->focus_item >= descr->nb_items)
1697     {
1698           descr->focus_item = descr->nb_items - 1;
1699           if (descr->focus_item < 0) descr->focus_item = 0;
1700     }
1701     return LB_OKAY;
1702 }
1703
1704
1705 /***********************************************************************
1706  *           LISTBOX_ResetContent
1707  */
1708 static void LISTBOX_ResetContent( LB_DESCR *descr )
1709 {
1710     INT i;
1711
1712     for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( descr, i );
1713     HeapFree( GetProcessHeap(), 0, descr->items );
1714     descr->nb_items      = 0;
1715     descr->top_item      = 0;
1716     descr->selected_item = -1;
1717     descr->focus_item    = 0;
1718     descr->anchor_item   = -1;
1719     descr->items         = NULL;
1720 }
1721
1722
1723 /***********************************************************************
1724  *           LISTBOX_SetCount
1725  */
1726 static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count )
1727 {
1728     LRESULT ret;
1729
1730     if (HAS_STRINGS(descr)) return LB_ERR;
1731     /* FIXME: this is far from optimal... */
1732     if (count > descr->nb_items)
1733     {
1734         while (count > descr->nb_items)
1735             if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0)
1736                 return ret;
1737     }
1738     else if (count < descr->nb_items)
1739     {
1740         while (count < descr->nb_items)
1741             if ((ret = LISTBOX_RemoveItem( descr, -1 )) < 0)
1742                 return ret;
1743     }
1744     return LB_OKAY;
1745 }
1746
1747
1748 /***********************************************************************
1749  *           LISTBOX_Directory
1750  */
1751 static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib,
1752                                   LPCWSTR filespec, BOOL long_names )
1753 {
1754     HANDLE handle;
1755     LRESULT ret = LB_OKAY;
1756     WIN32_FIND_DATAW entry;
1757     int pos;
1758
1759     /* don't scan directory if we just want drives exclusively */
1760     if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1761         /* scan directory */
1762         if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1763         {
1764              int le = GetLastError();
1765             if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR;
1766         }
1767         else
1768         {
1769             do
1770             {
1771                 WCHAR buffer[270];
1772                 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1773                 {
1774                     static const WCHAR bracketW[]  = { ']',0 };
1775                     static const WCHAR dotW[] = { '.',0 };
1776                     if (!(attrib & DDL_DIRECTORY) ||
1777                         !strcmpW( entry.cFileName, dotW )) continue;
1778                     buffer[0] = '[';
1779                     if (!long_names && entry.cAlternateFileName[0])
1780                         strcpyW( buffer + 1, entry.cAlternateFileName );
1781                     else
1782                         strcpyW( buffer + 1, entry.cFileName );
1783                     strcatW(buffer, bracketW);
1784                 }
1785                 else  /* not a directory */
1786                 {
1787 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1788                  FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1789
1790                     if ((attrib & DDL_EXCLUSIVE) &&
1791                         ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1792                         continue;
1793 #undef ATTRIBS
1794                     if (!long_names && entry.cAlternateFileName[0])
1795                         strcpyW( buffer, entry.cAlternateFileName );
1796                     else
1797                         strcpyW( buffer, entry.cFileName );
1798                 }
1799                 if (!long_names) CharLowerW( buffer );
1800                 pos = LISTBOX_FindFileStrPos( descr, buffer );
1801                 if ((ret = LISTBOX_InsertString( descr, pos, buffer )) < 0)
1802                     break;
1803             } while (FindNextFileW( handle, &entry ));
1804             FindClose( handle );
1805         }
1806     }
1807
1808     /* scan drives */
1809     if ((ret >= 0) && (attrib & DDL_DRIVES))
1810     {
1811         WCHAR buffer[] = {'[','-','a','-',']',0};
1812         WCHAR root[] = {'A',':','\\',0};
1813         int drive;
1814         for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1815         {
1816             if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1817             if ((ret = LISTBOX_InsertString( descr, -1, buffer )) < 0)
1818                 break;
1819         }
1820     }
1821     return ret;
1822 }
1823
1824
1825 /***********************************************************************
1826  *           LISTBOX_HandleVScroll
1827  */
1828 static LRESULT LISTBOX_HandleVScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1829 {
1830     SCROLLINFO info;
1831
1832     if (descr->style & LBS_MULTICOLUMN) return 0;
1833     switch(scrollReq)
1834     {
1835     case SB_LINEUP:
1836         LISTBOX_SetTopItem( descr, descr->top_item - 1, TRUE );
1837         break;
1838     case SB_LINEDOWN:
1839         LISTBOX_SetTopItem( descr, descr->top_item + 1, TRUE );
1840         break;
1841     case SB_PAGEUP:
1842         LISTBOX_SetTopItem( descr, descr->top_item -
1843                             LISTBOX_GetCurrentPageSize( descr ), TRUE );
1844         break;
1845     case SB_PAGEDOWN:
1846         LISTBOX_SetTopItem( descr, descr->top_item +
1847                             LISTBOX_GetCurrentPageSize( descr ), TRUE );
1848         break;
1849     case SB_THUMBPOSITION:
1850         LISTBOX_SetTopItem( descr, pos, TRUE );
1851         break;
1852     case SB_THUMBTRACK:
1853         info.cbSize = sizeof(info);
1854         info.fMask = SIF_TRACKPOS;
1855         GetScrollInfo( descr->self, SB_VERT, &info );
1856         LISTBOX_SetTopItem( descr, info.nTrackPos, TRUE );
1857         break;
1858     case SB_TOP:
1859         LISTBOX_SetTopItem( descr, 0, TRUE );
1860         break;
1861     case SB_BOTTOM:
1862         LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1863         break;
1864     }
1865     return 0;
1866 }
1867
1868
1869 /***********************************************************************
1870  *           LISTBOX_HandleHScroll
1871  */
1872 static LRESULT LISTBOX_HandleHScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1873 {
1874     SCROLLINFO info;
1875     INT page;
1876
1877     if (descr->style & LBS_MULTICOLUMN)
1878     {
1879         switch(scrollReq)
1880         {
1881         case SB_LINELEFT:
1882             LISTBOX_SetTopItem( descr, descr->top_item-descr->page_size,
1883                                 TRUE );
1884             break;
1885         case SB_LINERIGHT:
1886             LISTBOX_SetTopItem( descr, descr->top_item+descr->page_size,
1887                                 TRUE );
1888             break;
1889         case SB_PAGELEFT:
1890             page = descr->width / descr->column_width;
1891             if (page < 1) page = 1;
1892             LISTBOX_SetTopItem( descr,
1893                              descr->top_item - page * descr->page_size, TRUE );
1894             break;
1895         case SB_PAGERIGHT:
1896             page = descr->width / descr->column_width;
1897             if (page < 1) page = 1;
1898             LISTBOX_SetTopItem( descr,
1899                              descr->top_item + page * descr->page_size, TRUE );
1900             break;
1901         case SB_THUMBPOSITION:
1902             LISTBOX_SetTopItem( descr, pos*descr->page_size, TRUE );
1903             break;
1904         case SB_THUMBTRACK:
1905             info.cbSize = sizeof(info);
1906             info.fMask  = SIF_TRACKPOS;
1907             GetScrollInfo( descr->self, SB_VERT, &info );
1908             LISTBOX_SetTopItem( descr, info.nTrackPos*descr->page_size,
1909                                 TRUE );
1910             break;
1911         case SB_LEFT:
1912             LISTBOX_SetTopItem( descr, 0, TRUE );
1913             break;
1914         case SB_RIGHT:
1915             LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1916             break;
1917         }
1918     }
1919     else if (descr->horz_extent)
1920     {
1921         switch(scrollReq)
1922         {
1923         case SB_LINELEFT:
1924             LISTBOX_SetHorizontalPos( descr, descr->horz_pos - 1 );
1925             break;
1926         case SB_LINERIGHT:
1927             LISTBOX_SetHorizontalPos( descr, descr->horz_pos + 1 );
1928             break;
1929         case SB_PAGELEFT:
1930             LISTBOX_SetHorizontalPos( descr,
1931                                       descr->horz_pos - descr->width );
1932             break;
1933         case SB_PAGERIGHT:
1934             LISTBOX_SetHorizontalPos( descr,
1935                                       descr->horz_pos + descr->width );
1936             break;
1937         case SB_THUMBPOSITION:
1938             LISTBOX_SetHorizontalPos( descr, pos );
1939             break;
1940         case SB_THUMBTRACK:
1941             info.cbSize = sizeof(info);
1942             info.fMask = SIF_TRACKPOS;
1943             GetScrollInfo( descr->self, SB_HORZ, &info );
1944             LISTBOX_SetHorizontalPos( descr, info.nTrackPos );
1945             break;
1946         case SB_LEFT:
1947             LISTBOX_SetHorizontalPos( descr, 0 );
1948             break;
1949         case SB_RIGHT:
1950             LISTBOX_SetHorizontalPos( descr,
1951                                       descr->horz_extent - descr->width );
1952             break;
1953         }
1954     }
1955     return 0;
1956 }
1957
1958 static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta )
1959 {
1960     short gcWheelDelta = 0;
1961     UINT pulScrollLines = 3;
1962
1963     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1964
1965     gcWheelDelta -= delta;
1966
1967     if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1968     {
1969         int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1970         cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1971         LISTBOX_SetTopItem( descr, descr->top_item + cLineScroll, TRUE );
1972     }
1973     return 0;
1974 }
1975
1976 /***********************************************************************
1977  *           LISTBOX_HandleLButtonDown
1978  */
1979 static LRESULT LISTBOX_HandleLButtonDown( LB_DESCR *descr, DWORD keys, INT x, INT y )
1980 {
1981     INT index = LISTBOX_GetItemFromPoint( descr, x, y );
1982     TRACE("[%p]: lbuttondown %d,%d item %d\n", descr->self, x, y, index );
1983     if (!descr->caret_on && (descr->in_focus)) return 0;
1984
1985     if (!descr->in_focus)
1986     {
1987         if( !descr->lphc ) SetFocus( descr->self );
1988         else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
1989     }
1990
1991     if (index == -1) return 0;
1992
1993     if (descr->style & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL))
1994     {
1995         /* we should perhaps make sure that all items are deselected
1996            FIXME: needed for !LBS_EXTENDEDSEL, too ?
1997            if (!(keys & (MK_SHIFT|MK_CONTROL)))
1998            LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
1999         */
2000
2001         if (!(keys & MK_SHIFT)) descr->anchor_item = index;
2002         if (keys & MK_CONTROL)
2003         {
2004             LISTBOX_SetCaretIndex( descr, index, FALSE );
2005             LISTBOX_SetSelection( descr, index,
2006                                   !descr->items[index].selected,
2007                                   (descr->style & LBS_NOTIFY) != 0);
2008         }
2009         else
2010         {
2011             LISTBOX_MoveCaret( descr, index, FALSE );
2012
2013             if (descr->style & LBS_EXTENDEDSEL)
2014             {
2015                 LISTBOX_SetSelection( descr, index,
2016                                descr->items[index].selected,
2017                               (descr->style & LBS_NOTIFY) != 0 );
2018             }
2019             else
2020             {
2021                 LISTBOX_SetSelection( descr, index,
2022                                !descr->items[index].selected,
2023                               (descr->style & LBS_NOTIFY) != 0 );
2024             }
2025         }
2026     }
2027     else
2028     {
2029         descr->anchor_item = index;
2030         LISTBOX_MoveCaret( descr, index, FALSE );
2031         LISTBOX_SetSelection( descr, index,
2032                               TRUE, (descr->style & LBS_NOTIFY) != 0 );
2033     }
2034
2035     descr->captured = TRUE;
2036     SetCapture( descr->self );
2037
2038     if (!descr->lphc)
2039     {
2040         if (descr->style & LBS_NOTIFY )
2041             SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
2042                             MAKELPARAM( x, y ) );
2043         if (GetWindowLongW( descr->self, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
2044         {
2045             POINT pt;
2046
2047             pt.x = x;
2048             pt.y = y;
2049
2050             if (DragDetect( descr->self, pt ))
2051                 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
2052         }
2053     }
2054     return 0;
2055 }
2056
2057
2058 /*************************************************************************
2059  * LISTBOX_HandleLButtonDownCombo [Internal]
2060  *
2061  * Process LButtonDown message for the ComboListBox
2062  *
2063  * PARAMS
2064  *     pWnd       [I] The windows internal structure
2065  *     pDescr     [I] The ListBox internal structure
2066  *     keys       [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2067  *     x          [I] X Mouse Coordinate
2068  *     y          [I] Y Mouse Coordinate
2069  *
2070  * RETURNS
2071  *     0 since we are processing the WM_LBUTTONDOWN Message
2072  *
2073  * NOTES
2074  *  This function is only to be used when a ListBox is a ComboListBox
2075  */
2076
2077 static LRESULT LISTBOX_HandleLButtonDownCombo( LB_DESCR *descr, UINT msg, DWORD keys, INT x, INT y)
2078 {
2079     RECT clientRect, screenRect;
2080     POINT mousePos;
2081
2082     mousePos.x = x;
2083     mousePos.y = y;
2084
2085     GetClientRect(descr->self, &clientRect);
2086
2087     if(PtInRect(&clientRect, mousePos))
2088     {
2089        /* MousePos is in client, resume normal processing */
2090         if (msg == WM_LBUTTONDOWN)
2091         {
2092            descr->lphc->droppedIndex = descr->nb_items ? descr->selected_item : -1;
2093            return LISTBOX_HandleLButtonDown( descr, keys, x, y);
2094         }
2095         else if (descr->style & LBS_NOTIFY)
2096             SEND_NOTIFICATION( descr, LBN_DBLCLK );
2097     }
2098     else
2099     {
2100         POINT screenMousePos;
2101         HWND hWndOldCapture;
2102
2103         /* Check the Non-Client Area */
2104         screenMousePos = mousePos;
2105         hWndOldCapture = GetCapture();
2106         ReleaseCapture();
2107         GetWindowRect(descr->self, &screenRect);
2108         ClientToScreen(descr->self, &screenMousePos);
2109
2110         if(!PtInRect(&screenRect, screenMousePos))
2111         {
2112             LISTBOX_SetCaretIndex( descr, descr->lphc->droppedIndex, FALSE );
2113             LISTBOX_SetSelection( descr, descr->lphc->droppedIndex, FALSE, FALSE );
2114             COMBO_FlipListbox( descr->lphc, FALSE, FALSE );
2115         }
2116         else
2117         {
2118             /* Check to see the NC is a scrollbar */
2119             INT nHitTestType=0;
2120             LONG style = GetWindowLongW( descr->self, GWL_STYLE );
2121             /* Check Vertical scroll bar */
2122             if (style & WS_VSCROLL)
2123             {
2124                 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2125                 if (PtInRect( &clientRect, mousePos ))
2126                     nHitTestType = HTVSCROLL;
2127             }
2128               /* Check horizontal scroll bar */
2129             if (style & WS_HSCROLL)
2130             {
2131                 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2132                 if (PtInRect( &clientRect, mousePos ))
2133                     nHitTestType = HTHSCROLL;
2134             }
2135             /* Windows sends this message when a scrollbar is clicked
2136              */
2137
2138             if(nHitTestType != 0)
2139             {
2140                 SendMessageW(descr->self, WM_NCLBUTTONDOWN, nHitTestType,
2141                              MAKELONG(screenMousePos.x, screenMousePos.y));
2142             }
2143             /* Resume the Capture after scrolling is complete
2144              */
2145             if(hWndOldCapture != 0)
2146                 SetCapture(hWndOldCapture);
2147         }
2148     }
2149     return 0;
2150 }
2151
2152 /***********************************************************************
2153  *           LISTBOX_HandleLButtonUp
2154  */
2155 static LRESULT LISTBOX_HandleLButtonUp( LB_DESCR *descr )
2156 {
2157     if (LISTBOX_Timer != LB_TIMER_NONE)
2158         KillSystemTimer( descr->self, LB_TIMER_ID );
2159     LISTBOX_Timer = LB_TIMER_NONE;
2160     if (descr->captured)
2161     {
2162         descr->captured = FALSE;
2163         if (GetCapture() == descr->self) ReleaseCapture();
2164         if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2165             SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2166     }
2167     return 0;
2168 }
2169
2170
2171 /***********************************************************************
2172  *           LISTBOX_HandleTimer
2173  *
2174  * Handle scrolling upon a timer event.
2175  * Return TRUE if scrolling should continue.
2176  */
2177 static LRESULT LISTBOX_HandleTimer( LB_DESCR *descr, INT index, TIMER_DIRECTION dir )
2178 {
2179     switch(dir)
2180     {
2181     case LB_TIMER_UP:
2182         if (descr->top_item) index = descr->top_item - 1;
2183         else index = 0;
2184         break;
2185     case LB_TIMER_LEFT:
2186         if (descr->top_item) index -= descr->page_size;
2187         break;
2188     case LB_TIMER_DOWN:
2189         index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2190         if (index == descr->focus_item) index++;
2191         if (index >= descr->nb_items) index = descr->nb_items - 1;
2192         break;
2193     case LB_TIMER_RIGHT:
2194         if (index + descr->page_size < descr->nb_items)
2195             index += descr->page_size;
2196         break;
2197     case LB_TIMER_NONE:
2198         break;
2199     }
2200     if (index == descr->focus_item) return FALSE;
2201     LISTBOX_MoveCaret( descr, index, FALSE );
2202     return TRUE;
2203 }
2204
2205
2206 /***********************************************************************
2207  *           LISTBOX_HandleSystemTimer
2208  *
2209  * WM_SYSTIMER handler.
2210  */
2211 static LRESULT LISTBOX_HandleSystemTimer( LB_DESCR *descr )
2212 {
2213     if (!LISTBOX_HandleTimer( descr, descr->focus_item, LISTBOX_Timer ))
2214     {
2215         KillSystemTimer( descr->self, LB_TIMER_ID );
2216         LISTBOX_Timer = LB_TIMER_NONE;
2217     }
2218     return 0;
2219 }
2220
2221
2222 /***********************************************************************
2223  *           LISTBOX_HandleMouseMove
2224  *
2225  * WM_MOUSEMOVE handler.
2226  */
2227 static void LISTBOX_HandleMouseMove( LB_DESCR *descr,
2228                                      INT x, INT y )
2229 {
2230     INT index;
2231     TIMER_DIRECTION dir = LB_TIMER_NONE;
2232
2233     if (!descr->captured) return;
2234
2235     if (descr->style & LBS_MULTICOLUMN)
2236     {
2237         if (y < 0) y = 0;
2238         else if (y >= descr->item_height * descr->page_size)
2239             y = descr->item_height * descr->page_size - 1;
2240
2241         if (x < 0)
2242         {
2243             dir = LB_TIMER_LEFT;
2244             x = 0;
2245         }
2246         else if (x >= descr->width)
2247         {
2248             dir = LB_TIMER_RIGHT;
2249             x = descr->width - 1;
2250         }
2251     }
2252     else
2253     {
2254         if (y < 0) dir = LB_TIMER_UP;  /* above */
2255         else if (y >= descr->height) dir = LB_TIMER_DOWN;  /* below */
2256     }
2257
2258     index = LISTBOX_GetItemFromPoint( descr, x, y );
2259     if (index == -1) index = descr->focus_item;
2260     if (!LISTBOX_HandleTimer( descr, index, dir )) dir = LB_TIMER_NONE;
2261
2262     /* Start/stop the system timer */
2263
2264     if (dir != LB_TIMER_NONE)
2265         SetSystemTimer( descr->self, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2266     else if (LISTBOX_Timer != LB_TIMER_NONE)
2267         KillSystemTimer( descr->self, LB_TIMER_ID );
2268     LISTBOX_Timer = dir;
2269 }
2270
2271
2272 /***********************************************************************
2273  *           LISTBOX_HandleKeyDown
2274  */
2275 static LRESULT LISTBOX_HandleKeyDown( LB_DESCR *descr, DWORD key )
2276 {
2277     INT caret = -1;
2278     BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2279     if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2280         bForceSelection = FALSE; /* only for single select list */
2281
2282     if (descr->style & LBS_WANTKEYBOARDINPUT)
2283     {
2284         caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2285                                 MAKEWPARAM(LOWORD(key), descr->focus_item),
2286                                 (LPARAM)descr->self );
2287         if (caret == -2) return 0;
2288     }
2289     if (caret == -1) switch(key)
2290     {
2291     case VK_LEFT:
2292         if (descr->style & LBS_MULTICOLUMN)
2293         {
2294             bForceSelection = FALSE;
2295             if (descr->focus_item >= descr->page_size)
2296                 caret = descr->focus_item - descr->page_size;
2297             break;
2298         }
2299         /* fall through */
2300     case VK_UP:
2301         caret = descr->focus_item - 1;
2302         if (caret < 0) caret = 0;
2303         break;
2304     case VK_RIGHT:
2305         if (descr->style & LBS_MULTICOLUMN)
2306         {
2307             bForceSelection = FALSE;
2308             if (descr->focus_item + descr->page_size < descr->nb_items)
2309                 caret = descr->focus_item + descr->page_size;
2310             break;
2311         }
2312         /* fall through */
2313     case VK_DOWN:
2314         caret = descr->focus_item + 1;
2315         if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2316         break;
2317
2318     case VK_PRIOR:
2319         if (descr->style & LBS_MULTICOLUMN)
2320         {
2321             INT page = descr->width / descr->column_width;
2322             if (page < 1) page = 1;
2323             caret = descr->focus_item - (page * descr->page_size) + 1;
2324         }
2325         else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2326         if (caret < 0) caret = 0;
2327         break;
2328     case VK_NEXT:
2329         if (descr->style & LBS_MULTICOLUMN)
2330         {
2331             INT page = descr->width / descr->column_width;
2332             if (page < 1) page = 1;
2333             caret = descr->focus_item + (page * descr->page_size) - 1;
2334         }
2335         else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2336         if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2337         break;
2338     case VK_HOME:
2339         caret = 0;
2340         break;
2341     case VK_END:
2342         caret = descr->nb_items - 1;
2343         break;
2344     case VK_SPACE:
2345         if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2346         else if (descr->style & LBS_MULTIPLESEL)
2347         {
2348             LISTBOX_SetSelection( descr, descr->focus_item,
2349                                   !descr->items[descr->focus_item].selected,
2350                                   (descr->style & LBS_NOTIFY) != 0 );
2351         }
2352         break;
2353     default:
2354         bForceSelection = FALSE;
2355     }
2356     if (bForceSelection) /* focused item is used instead of key */
2357         caret = descr->focus_item;
2358     if (caret >= 0)
2359     {
2360         if (((descr->style & LBS_EXTENDEDSEL) &&
2361             !(GetKeyState( VK_SHIFT ) & 0x8000)) ||
2362             !IS_MULTISELECT(descr))
2363             descr->anchor_item = caret;
2364         LISTBOX_MoveCaret( descr, caret, TRUE );
2365
2366         if (descr->style & LBS_MULTIPLESEL)
2367             descr->selected_item = caret;
2368         else
2369             LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2370         if (descr->style & LBS_NOTIFY)
2371         {
2372             if( descr->lphc )
2373             {
2374                 /* make sure that combo parent doesn't hide us */
2375                 descr->lphc->wState |= CBF_NOROLLUP;
2376             }
2377             if (descr->nb_items) SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2378         }
2379     }
2380     return 0;
2381 }
2382
2383
2384 /***********************************************************************
2385  *           LISTBOX_HandleChar
2386  */
2387 static LRESULT LISTBOX_HandleChar( LB_DESCR *descr, WCHAR charW )
2388 {
2389     INT caret = -1;
2390     WCHAR str[2];
2391
2392     str[0] = charW;
2393     str[1] = '\0';
2394
2395     if (descr->style & LBS_WANTKEYBOARDINPUT)
2396     {
2397         caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2398                                 MAKEWPARAM(charW, descr->focus_item),
2399                                 (LPARAM)descr->self );
2400         if (caret == -2) return 0;
2401     }
2402     if (caret == -1)
2403         caret = LISTBOX_FindString( descr, descr->focus_item, str, FALSE);
2404     if (caret != -1)
2405     {
2406         if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2407            LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2408         LISTBOX_MoveCaret( descr, caret, TRUE );
2409         if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2410             SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2411     }
2412     return 0;
2413 }
2414
2415
2416 /***********************************************************************
2417  *           LISTBOX_Create
2418  */
2419 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2420 {
2421     LB_DESCR *descr;
2422     MEASUREITEMSTRUCT mis;
2423     RECT rect;
2424
2425     if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2426         return FALSE;
2427
2428     GetClientRect( hwnd, &rect );
2429     descr->self          = hwnd;
2430     descr->owner         = GetParent( descr->self );
2431     descr->style         = GetWindowLongW( descr->self, GWL_STYLE );
2432     descr->width         = rect.right - rect.left;
2433     descr->height        = rect.bottom - rect.top;
2434     descr->items         = NULL;
2435     descr->nb_items      = 0;
2436     descr->top_item      = 0;
2437     descr->selected_item = -1;
2438     descr->focus_item    = 0;
2439     descr->anchor_item   = -1;
2440     descr->item_height   = 1;
2441     descr->page_size     = 1;
2442     descr->column_width  = 150;
2443     descr->horz_extent   = (descr->style & WS_HSCROLL) ? 1 : 0;
2444     descr->horz_pos      = 0;
2445     descr->nb_tabs       = 0;
2446     descr->tabs          = NULL;
2447     descr->caret_on      = lphc ? FALSE : TRUE;
2448     if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2449     descr->in_focus      = FALSE;
2450     descr->captured      = FALSE;
2451     descr->font          = 0;
2452     descr->locale        = 0;  /* FIXME */
2453     descr->lphc          = lphc;
2454
2455     if (is_old_app(descr) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2456     {
2457         /* Win95 document "List Box Differences" from MSDN:
2458            If a list box in a version 3.x application has either the
2459            WS_HSCROLL or WS_VSCROLL style, the list box receives both
2460            horizontal and vertical scroll bars.
2461         */
2462         descr->style |= WS_VSCROLL | WS_HSCROLL;
2463     }
2464
2465     if( lphc )
2466     {
2467         TRACE("[%p]: resetting owner %p -> %p\n", descr->self, descr->owner, lphc->self );
2468         descr->owner = lphc->self;
2469     }
2470
2471     SetWindowLongW( descr->self, 0, (LONG)descr );
2472
2473 /*    if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2474  */
2475     if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2476     if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2477     if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2478     descr->item_height = LISTBOX_SetFont( descr, 0 );
2479
2480     if (descr->style & LBS_OWNERDRAWFIXED)
2481     {
2482         if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2483         {
2484             /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2485           descr->item_height = lphc->fixedOwnerDrawHeight;
2486         }
2487         else
2488         {
2489             UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
2490             mis.CtlType    = ODT_LISTBOX;
2491             mis.CtlID      = id;
2492             mis.itemID     = -1;
2493             mis.itemWidth  =  0;
2494             mis.itemData   =  0;
2495             mis.itemHeight = descr->item_height;
2496             SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2497             descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2498         }
2499     }
2500
2501     TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2502     return TRUE;
2503 }
2504
2505
2506 /***********************************************************************
2507  *           LISTBOX_Destroy
2508  */
2509 static BOOL LISTBOX_Destroy( LB_DESCR *descr )
2510 {
2511     LISTBOX_ResetContent( descr );
2512     SetWindowLongW( descr->self, 0, 0 );
2513     HeapFree( GetProcessHeap(), 0, descr );
2514     return TRUE;
2515 }
2516
2517
2518 /***********************************************************************
2519  *           ListBoxWndProc_common
2520  */
2521 static LRESULT WINAPI ListBoxWndProc_common( HWND hwnd, UINT msg,
2522                                              WPARAM wParam, LPARAM lParam, BOOL unicode )
2523 {
2524     LB_DESCR *descr = (LB_DESCR *)GetWindowLongW( hwnd, 0 );
2525     LPHEADCOMBO lphc = 0;
2526     LRESULT ret;
2527
2528     if (!descr)
2529     {
2530         if (!IsWindow(hwnd)) return 0;
2531
2532         if (msg == WM_CREATE)
2533         {
2534             CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam;
2535             if (lpcs->style & LBS_COMBOBOX) lphc = (LPHEADCOMBO)lpcs->lpCreateParams;
2536             if (!LISTBOX_Create( hwnd, lphc )) return -1;
2537             TRACE("creating wnd=%p descr=%lx\n", hwnd, GetWindowLongW( hwnd, 0 ) );
2538             return 0;
2539         }
2540         /* Ignore all other messages before we get a WM_CREATE */
2541         return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2542                          DefWindowProcA( hwnd, msg, wParam, lParam );
2543     }
2544     if (descr->style & LBS_COMBOBOX) lphc = descr->lphc;
2545
2546     TRACE("[%p]: msg %s wp %08x lp %08lx\n",
2547           descr->self, SPY_GetMsgName(msg, descr->self), wParam, lParam );
2548
2549     switch(msg)
2550     {
2551     case LB_RESETCONTENT16:
2552     case LB_RESETCONTENT:
2553         LISTBOX_ResetContent( descr );
2554         LISTBOX_UpdateScroll( descr );
2555         InvalidateRect( descr->self, NULL, TRUE );
2556         return 0;
2557
2558     case LB_ADDSTRING16:
2559         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2560         /* fall through */
2561     case LB_ADDSTRING:
2562     {
2563         INT ret;
2564         LPWSTR textW;
2565         if(unicode || !HAS_STRINGS(descr))
2566             textW = (LPWSTR)lParam;
2567         else
2568         {
2569             LPSTR textA = (LPSTR)lParam;
2570             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2571             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2572                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2573         }
2574         wParam = LISTBOX_FindStringPos( descr, textW, FALSE );
2575         ret = LISTBOX_InsertString( descr, wParam, textW );
2576         if (!unicode && HAS_STRINGS(descr))
2577             HeapFree(GetProcessHeap(), 0, textW);
2578         return ret;
2579     }
2580
2581     case LB_INSERTSTRING16:
2582         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2583         wParam = (INT)(INT16)wParam;
2584         /* fall through */
2585     case LB_INSERTSTRING:
2586     {
2587         INT ret;
2588         LPWSTR textW;
2589         if(unicode || !HAS_STRINGS(descr))
2590             textW = (LPWSTR)lParam;
2591         else
2592         {
2593             LPSTR textA = (LPSTR)lParam;
2594             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2595             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2596                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2597         }
2598         ret = LISTBOX_InsertString( descr, wParam, textW );
2599         if(!unicode && HAS_STRINGS(descr))
2600             HeapFree(GetProcessHeap(), 0, textW);
2601         return ret;
2602     }
2603
2604     case LB_ADDFILE16:
2605         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2606         /* fall through */
2607     case LB_ADDFILE:
2608     {
2609         INT ret;
2610         LPWSTR textW;
2611         if(unicode || !HAS_STRINGS(descr))
2612             textW = (LPWSTR)lParam;
2613         else
2614         {
2615             LPSTR textA = (LPSTR)lParam;
2616             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2617             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2618                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2619         }
2620         wParam = LISTBOX_FindFileStrPos( descr, textW );
2621         ret = LISTBOX_InsertString( descr, wParam, textW );
2622         if(!unicode && HAS_STRINGS(descr))
2623             HeapFree(GetProcessHeap(), 0, textW);
2624         return ret;
2625     }
2626
2627     case LB_DELETESTRING16:
2628     case LB_DELETESTRING:
2629         if (LISTBOX_RemoveItem( descr, wParam) != LB_ERR)
2630            return descr->nb_items;
2631         else
2632            return LB_ERR;
2633
2634     case LB_GETITEMDATA16:
2635     case LB_GETITEMDATA:
2636         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2637             return LB_ERR;
2638         return descr->items[wParam].data;
2639
2640     case LB_SETITEMDATA16:
2641     case LB_SETITEMDATA:
2642         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2643             return LB_ERR;
2644         descr->items[wParam].data = (DWORD)lParam;
2645         return LB_OKAY;
2646
2647     case LB_GETCOUNT16:
2648     case LB_GETCOUNT:
2649         return descr->nb_items;
2650
2651     case LB_GETTEXT16:
2652         lParam = (LPARAM)MapSL(lParam);
2653         /* fall through */
2654     case LB_GETTEXT:
2655         return LISTBOX_GetText( descr, wParam, (LPWSTR)lParam, unicode );
2656
2657     case LB_GETTEXTLEN16:
2658         /* fall through */
2659     case LB_GETTEXTLEN:
2660         if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2661             return LB_ERR;
2662         if (!HAS_STRINGS(descr)) return sizeof(DWORD);
2663         if (unicode) return strlenW( descr->items[wParam].str );
2664         return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
2665                                     strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
2666
2667     case LB_GETCURSEL16:
2668     case LB_GETCURSEL:
2669         if (descr->nb_items == 0)
2670             return LB_ERR;
2671         if (!IS_MULTISELECT(descr))
2672             return descr->selected_item;
2673         if (descr->selected_item != -1)
2674             return descr->selected_item;
2675         return descr->focus_item;
2676         /* otherwise, if the user tries to move the selection with the    */
2677         /* arrow keys, we will give the application something to choke on */
2678     case LB_GETTOPINDEX16:
2679     case LB_GETTOPINDEX:
2680         return descr->top_item;
2681
2682     case LB_GETITEMHEIGHT16:
2683     case LB_GETITEMHEIGHT:
2684         return LISTBOX_GetItemHeight( descr, wParam );
2685
2686     case LB_SETITEMHEIGHT16:
2687         lParam = LOWORD(lParam);
2688         /* fall through */
2689     case LB_SETITEMHEIGHT:
2690         return LISTBOX_SetItemHeight( descr, wParam, lParam, TRUE );
2691
2692     case LB_ITEMFROMPOINT:
2693         {
2694             POINT pt;
2695             RECT rect;
2696
2697             pt.x = LOWORD(lParam);
2698             pt.y = HIWORD(lParam);
2699             rect.left = 0;
2700             rect.top = 0;
2701             rect.right = descr->width;
2702             rect.bottom = descr->height;
2703
2704             return MAKELONG( LISTBOX_GetItemFromPoint(descr, pt.x, pt.y),
2705                              !PtInRect( &rect, pt ) );
2706         }
2707
2708     case LB_SETCARETINDEX16:
2709     case LB_SETCARETINDEX:
2710         if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2711         if (LISTBOX_SetCaretIndex( descr, wParam, !lParam ) == LB_ERR)
2712             return LB_ERR;
2713         else if (ISWIN31)
2714              return wParam;
2715         else
2716              return LB_OKAY;
2717
2718     case LB_GETCARETINDEX16:
2719     case LB_GETCARETINDEX:
2720         return descr->focus_item;
2721
2722     case LB_SETTOPINDEX16:
2723     case LB_SETTOPINDEX:
2724         return LISTBOX_SetTopItem( descr, wParam, TRUE );
2725
2726     case LB_SETCOLUMNWIDTH16:
2727     case LB_SETCOLUMNWIDTH:
2728         return LISTBOX_SetColumnWidth( descr, wParam );
2729
2730     case LB_GETITEMRECT16:
2731         {
2732             RECT rect;
2733             RECT16 *r16 = MapSL(lParam);
2734             ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2735             r16->left   = rect.left;
2736             r16->top    = rect.top;
2737             r16->right  = rect.right;
2738             r16->bottom = rect.bottom;
2739         }
2740         return ret;
2741
2742     case LB_GETITEMRECT:
2743         return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2744
2745     case LB_FINDSTRING16:
2746         wParam = (INT)(INT16)wParam;
2747         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2748         /* fall through */
2749     case LB_FINDSTRING:
2750     {
2751         INT ret;
2752         LPWSTR textW;
2753         if(unicode || !HAS_STRINGS(descr))
2754             textW = (LPWSTR)lParam;
2755         else
2756         {
2757             LPSTR textA = (LPSTR)lParam;
2758             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2759             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2760                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2761         }
2762         ret = LISTBOX_FindString( descr, wParam, textW, FALSE );
2763         if(!unicode && HAS_STRINGS(descr))
2764             HeapFree(GetProcessHeap(), 0, textW);
2765         return ret;
2766     }
2767
2768     case LB_FINDSTRINGEXACT16:
2769         wParam = (INT)(INT16)wParam;
2770         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2771         /* fall through */
2772     case LB_FINDSTRINGEXACT:
2773     {
2774         INT ret;
2775         LPWSTR textW;
2776         if(unicode || !HAS_STRINGS(descr))
2777             textW = (LPWSTR)lParam;
2778         else
2779         {
2780             LPSTR textA = (LPSTR)lParam;
2781             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2782             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2783                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2784         }
2785         ret = LISTBOX_FindString( descr, wParam, textW, TRUE );
2786         if(!unicode && HAS_STRINGS(descr))
2787             HeapFree(GetProcessHeap(), 0, textW);
2788         return ret;
2789     }
2790
2791     case LB_SELECTSTRING16:
2792         wParam = (INT)(INT16)wParam;
2793         if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2794         /* fall through */
2795     case LB_SELECTSTRING:
2796     {
2797         INT index;
2798         LPWSTR textW;
2799
2800         if(HAS_STRINGS(descr))
2801             TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2802                                                      debugstr_a((LPSTR)lParam));
2803         if(unicode || !HAS_STRINGS(descr))
2804             textW = (LPWSTR)lParam;
2805         else
2806         {
2807             LPSTR textA = (LPSTR)lParam;
2808             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2809             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2810                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2811         }
2812         index = LISTBOX_FindString( descr, wParam, textW, FALSE );
2813         if(!unicode && HAS_STRINGS(descr))
2814             HeapFree(GetProcessHeap(), 0, textW);
2815         if (index != LB_ERR)
2816         {
2817             LISTBOX_MoveCaret( descr, index, TRUE );
2818             LISTBOX_SetSelection( descr, index, TRUE, FALSE );
2819         }
2820         return index;
2821     }
2822
2823     case LB_GETSEL16:
2824         wParam = (INT)(INT16)wParam;
2825         /* fall through */
2826     case LB_GETSEL:
2827         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2828             return LB_ERR;
2829         return descr->items[wParam].selected;
2830
2831     case LB_SETSEL16:
2832         lParam = (INT)(INT16)lParam;
2833         /* fall through */
2834     case LB_SETSEL:
2835         return LISTBOX_SetSelection( descr, lParam, wParam, FALSE );
2836
2837     case LB_SETCURSEL16:
2838         wParam = (INT)(INT16)wParam;
2839         /* fall through */
2840     case LB_SETCURSEL:
2841         if (IS_MULTISELECT(descr)) return LB_ERR;
2842         LISTBOX_SetCaretIndex( descr, wParam, TRUE );
2843         ret = LISTBOX_SetSelection( descr, wParam, TRUE, FALSE );
2844         if (lphc && ret != LB_ERR) ret = descr->selected_item;
2845         return ret;
2846
2847     case LB_GETSELCOUNT16:
2848     case LB_GETSELCOUNT:
2849         return LISTBOX_GetSelCount( descr );
2850
2851     case LB_GETSELITEMS16:
2852         return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2853
2854     case LB_GETSELITEMS:
2855         return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2856
2857     case LB_SELITEMRANGE16:
2858     case LB_SELITEMRANGE:
2859         if (LOWORD(lParam) <= HIWORD(lParam))
2860             return LISTBOX_SelectItemRange( descr, LOWORD(lParam),
2861                                             HIWORD(lParam), wParam );
2862         else
2863             return LISTBOX_SelectItemRange( descr, HIWORD(lParam),
2864                                             LOWORD(lParam), wParam );
2865
2866     case LB_SELITEMRANGEEX16:
2867     case LB_SELITEMRANGEEX:
2868         if ((INT)lParam >= (INT)wParam)
2869             return LISTBOX_SelectItemRange( descr, wParam, lParam, TRUE );
2870         else
2871             return LISTBOX_SelectItemRange( descr, lParam, wParam, FALSE);
2872
2873     case LB_GETHORIZONTALEXTENT16:
2874     case LB_GETHORIZONTALEXTENT:
2875         return descr->horz_extent;
2876
2877     case LB_SETHORIZONTALEXTENT16:
2878     case LB_SETHORIZONTALEXTENT:
2879         return LISTBOX_SetHorizontalExtent( descr, wParam );
2880
2881     case LB_GETANCHORINDEX16:
2882     case LB_GETANCHORINDEX:
2883         return descr->anchor_item;
2884
2885     case LB_SETANCHORINDEX16:
2886         wParam = (INT)(INT16)wParam;
2887         /* fall through */
2888     case LB_SETANCHORINDEX:
2889         if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2890             return LB_ERR;
2891         descr->anchor_item = (INT)wParam;
2892         return LB_OKAY;
2893
2894     case LB_DIR16:
2895         /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2896          * be set automatically (this is different in Win32) */
2897         if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2898             lParam = (LPARAM)MapSL(lParam);
2899         /* fall through */
2900     case LB_DIR:
2901     {
2902         INT ret;
2903         LPWSTR textW;
2904         if(unicode)
2905             textW = (LPWSTR)lParam;
2906         else
2907         {
2908             LPSTR textA = (LPSTR)lParam;
2909             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2910             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2911                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2912         }
2913         ret = LISTBOX_Directory( descr, wParam, textW, msg == LB_DIR );
2914         if(!unicode)
2915             HeapFree(GetProcessHeap(), 0, textW);
2916         return ret;
2917     }
2918
2919     case LB_GETLOCALE:
2920         return descr->locale;
2921
2922     case LB_SETLOCALE:
2923         descr->locale = (LCID)wParam;  /* FIXME: should check for valid lcid */
2924         return LB_OKAY;
2925
2926     case LB_INITSTORAGE:
2927         return LISTBOX_InitStorage( descr, wParam );
2928
2929     case LB_SETCOUNT:
2930         return LISTBOX_SetCount( descr, (INT)wParam );
2931
2932     case LB_SETTABSTOPS16:
2933         return LISTBOX_SetTabStops( descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2934
2935     case LB_SETTABSTOPS:
2936         return LISTBOX_SetTabStops( descr, wParam, (LPINT)lParam, FALSE );
2937
2938     case LB_CARETON16:
2939     case LB_CARETON:
2940         if (descr->caret_on)
2941             return LB_OKAY;
2942         descr->caret_on = TRUE;
2943         if ((descr->focus_item != -1) && (descr->in_focus))
2944             LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2945         return LB_OKAY;
2946
2947     case LB_CARETOFF16:
2948     case LB_CARETOFF:
2949         if (!descr->caret_on)
2950             return LB_OKAY;
2951         descr->caret_on = FALSE;
2952         if ((descr->focus_item != -1) && (descr->in_focus))
2953             LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2954         return LB_OKAY;
2955
2956     case WM_DESTROY:
2957         return LISTBOX_Destroy( descr );
2958
2959     case WM_ENABLE:
2960         InvalidateRect( descr->self, NULL, TRUE );
2961         return 0;
2962
2963     case WM_SETREDRAW:
2964         LISTBOX_SetRedraw( descr, wParam != 0 );
2965         return 0;
2966
2967     case WM_GETDLGCODE:
2968         return DLGC_WANTARROWS | DLGC_WANTCHARS;
2969
2970     case WM_PAINT:
2971         {
2972             PAINTSTRUCT ps;
2973             HDC hdc = ( wParam ) ? ((HDC)wParam) :  BeginPaint( descr->self, &ps );
2974             ret = LISTBOX_Paint( descr, hdc );
2975             if( !wParam ) EndPaint( descr->self, &ps );
2976         }
2977         return ret;
2978     case WM_SIZE:
2979         LISTBOX_UpdateSize( descr );
2980         return 0;
2981     case WM_GETFONT:
2982         return (LRESULT)descr->font;
2983     case WM_SETFONT:
2984         LISTBOX_SetFont( descr, (HFONT)wParam );
2985         if (lParam) InvalidateRect( descr->self, 0, TRUE );
2986         return 0;
2987     case WM_SETFOCUS:
2988         descr->in_focus = TRUE;
2989         descr->caret_on = TRUE;
2990         if (descr->focus_item != -1)
2991             LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2992         SEND_NOTIFICATION( descr, LBN_SETFOCUS );
2993         return 0;
2994     case WM_KILLFOCUS:
2995         descr->in_focus = FALSE;
2996         if ((descr->focus_item != -1) && descr->caret_on)
2997             LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2998         SEND_NOTIFICATION( descr, LBN_KILLFOCUS );
2999         return 0;
3000     case WM_HSCROLL:
3001         return LISTBOX_HandleHScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3002     case WM_VSCROLL:
3003         return LISTBOX_HandleVScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3004     case WM_MOUSEWHEEL:
3005         if (wParam & (MK_SHIFT | MK_CONTROL))
3006             return DefWindowProcW( descr->self, msg, wParam, lParam );
3007         return LISTBOX_HandleMouseWheel( descr, (SHORT)HIWORD(wParam) );
3008     case WM_LBUTTONDOWN:
3009         if (lphc)
3010             return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3011                                                   (INT16)LOWORD(lParam),
3012                                                   (INT16)HIWORD(lParam) );
3013         return LISTBOX_HandleLButtonDown( descr, wParam,
3014                                           (INT16)LOWORD(lParam),
3015                                           (INT16)HIWORD(lParam) );
3016     case WM_LBUTTONDBLCLK:
3017         if (lphc)
3018             return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3019                                                   (INT16)LOWORD(lParam),
3020                                                   (INT16)HIWORD(lParam) );
3021         if (descr->style & LBS_NOTIFY)
3022             SEND_NOTIFICATION( descr, LBN_DBLCLK );
3023         return 0;
3024     case WM_MOUSEMOVE:
3025         if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) )
3026         {
3027             BOOL    captured = descr->captured;
3028             POINT   mousePos;
3029             RECT    clientRect;
3030
3031             mousePos.x = (INT16)LOWORD(lParam);
3032             mousePos.y = (INT16)HIWORD(lParam);
3033
3034             /*
3035              * If we are in a dropdown combobox, we simulate that
3036              * the mouse is captured to show the tracking of the item.
3037              */
3038             if (GetClientRect(descr->self, &clientRect) && PtInRect( &clientRect, mousePos ))
3039                 descr->captured = TRUE;
3040
3041             LISTBOX_HandleMouseMove( descr, mousePos.x, mousePos.y);
3042
3043             descr->captured = captured;
3044         } 
3045         else if (GetCapture() == descr->self)
3046         {
3047             LISTBOX_HandleMouseMove( descr, (INT16)LOWORD(lParam),
3048                                      (INT16)HIWORD(lParam) );
3049         }
3050         return 0;
3051     case WM_LBUTTONUP:
3052         if (lphc)
3053         {
3054             POINT mousePos;
3055             RECT  clientRect;
3056
3057             /*
3058              * If the mouse button "up" is not in the listbox,
3059              * we make sure there is no selection by re-selecting the
3060              * item that was selected when the listbox was made visible.
3061              */
3062             mousePos.x = (INT16)LOWORD(lParam);
3063             mousePos.y = (INT16)HIWORD(lParam);
3064
3065             GetClientRect(descr->self, &clientRect);
3066
3067             /*
3068              * When the user clicks outside the combobox and the focus
3069              * is lost, the owning combobox will send a fake buttonup with
3070              * 0xFFFFFFF as the mouse location, we must also revert the
3071              * selection to the original selection.
3072              */
3073             if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) )
3074                 LISTBOX_MoveCaret( descr, lphc->droppedIndex, FALSE );
3075         }
3076         return LISTBOX_HandleLButtonUp( descr );
3077     case WM_KEYDOWN:
3078         if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3079         {
3080             /* for some reason Windows makes it possible to
3081              * show/hide ComboLBox by sending it WM_KEYDOWNs */
3082
3083             if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3084                 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3085                   && (wParam == VK_DOWN || wParam == VK_UP)) )
3086             {
3087                 COMBO_FlipListbox( lphc, FALSE, FALSE );
3088                 return 0;
3089             }
3090         }
3091         return LISTBOX_HandleKeyDown( descr, wParam );
3092     case WM_CHAR:
3093     {
3094         WCHAR charW;
3095         if(unicode)
3096             charW = (WCHAR)wParam;
3097         else
3098         {
3099             CHAR charA = (CHAR)wParam;
3100             MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
3101         }
3102         return LISTBOX_HandleChar( descr, charW );
3103     }
3104     case WM_SYSTIMER:
3105         return LISTBOX_HandleSystemTimer( descr );
3106     case WM_ERASEBKGND:
3107         if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
3108         {
3109             RECT rect;
3110             HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
3111                                               wParam, (LPARAM)descr->self );
3112             TRACE("hbrush = %p\n", hbrush);
3113             if(!hbrush)
3114                 hbrush = GetSysColorBrush(COLOR_WINDOW);
3115             if(hbrush)
3116             {
3117                 GetClientRect(descr->self, &rect);
3118                 FillRect((HDC)wParam, &rect, hbrush);
3119             }
3120         }
3121         return 1;
3122     case WM_DROPFILES:
3123         if( lphc ) return 0;
3124         return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3125                          SendMessageA( descr->owner, msg, wParam, lParam );
3126
3127     case WM_NCDESTROY:
3128         if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3129             lphc->hWndLBox = 0;
3130         break;
3131
3132     case WM_NCACTIVATE:
3133         if (lphc) return 0;
3134         break;
3135
3136     default:
3137         if ((msg >= WM_USER) && (msg < 0xc000))
3138             WARN("[%p]: unknown msg %04x wp %08x lp %08lx\n",
3139                  hwnd, msg, wParam, lParam );
3140     }
3141
3142     return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3143                      DefWindowProcA( hwnd, msg, wParam, lParam );
3144 }
3145
3146 /***********************************************************************
3147  *           ListBoxWndProcA
3148  */
3149 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3150 {
3151     return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3152 }
3153
3154 /***********************************************************************
3155  *           ListBoxWndProcW
3156  */
3157 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3158 {
3159     return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE );
3160 }