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