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