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