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