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