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