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