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