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