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