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