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