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