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