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