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