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