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