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