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