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