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