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