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