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