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