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