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