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