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