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