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