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