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