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