4 * Copyright 1996 Alexandre Julliard
5 * Copyright 2005 Frank Richter
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.
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.
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
34 #include "wine/exception.h"
35 #include "wine/debug.h"
36 #include "wine/heap.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(listbox
);
42 /* Items array granularity (must be power of 2) */
43 #define LB_ARRAY_GRANULARITY 16
45 /* Scrolling timeout in ms */
46 #define LB_SCROLL_TIMEOUT 50
48 /* Listbox system timer id */
51 /* flag listbox changed while setredraw false - internal style */
52 #define LBS_DISPLAYCHANGED 0x80000000
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 */
63 /* Listbox structure */
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 */
73 LB_ITEMDATA
*items
; /* Array of items */
74 BYTE
*nodata_items
; /* For multi-selection LBS_NODATA */
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? */
94 HFONT font
; /* Current font */
95 LCID locale
; /* Current locale for string comparisons */
96 HEADCOMBO
*lphc
; /* ComboLBox */
100 #define IS_OWNERDRAW(descr) \
101 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
103 #define HAS_STRINGS(descr) \
104 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
107 #define IS_MULTISELECT(descr) \
108 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
109 !((descr)->style & LBS_NOSEL))
111 #define SEND_NOTIFICATION(descr,code) \
112 (SendMessageW( (descr)->owner, WM_COMMAND, \
113 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
115 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
117 /* Current timer status */
127 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
129 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
);
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.
135 For single-selection LBS_NODATA listboxes, no storage is allocated,
136 and thus descr->u.nodata_items will always be NULL.
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.
141 static size_t get_sizeof_item( const LB_DESCR
*descr
)
143 return (descr
->style
& LBS_NODATA
) ? sizeof(BYTE
) : sizeof(LB_ITEMDATA
);
146 static BOOL
resize_storage(LB_DESCR
*descr
, UINT items_size
)
150 if (items_size
> descr
->items_size
||
151 items_size
+ LB_ARRAY_GRANULARITY
* 2 < descr
->items_size
)
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
)
156 items
= heap_realloc(descr
->u
.items
, items_size
* get_sizeof_item(descr
));
159 SEND_NOTIFICATION(descr
, LBN_ERRSPACE
);
162 descr
->u
.items
= items
;
164 descr
->items_size
= items_size
;
167 if ((descr
->style
& LBS_NODATA
) && descr
->u
.nodata_items
&& items_size
> descr
->nb_items
)
169 memset(descr
->u
.nodata_items
+ descr
->nb_items
, 0,
170 (items_size
- descr
->nb_items
) * get_sizeof_item(descr
));
175 static ULONG_PTR
get_item_data( const LB_DESCR
*descr
, UINT index
)
177 return (descr
->style
& LBS_NODATA
) ? 0 : descr
->u
.items
[index
].data
;
180 static void set_item_data( LB_DESCR
*descr
, UINT index
, ULONG_PTR data
)
182 if (!(descr
->style
& LBS_NODATA
)) descr
->u
.items
[index
].data
= data
;
185 static WCHAR
*get_item_string( const LB_DESCR
*descr
, UINT index
)
187 return HAS_STRINGS(descr
) ? descr
->u
.items
[index
].str
: NULL
;
190 static void set_item_string( const LB_DESCR
*descr
, UINT index
, WCHAR
*string
)
192 if (!(descr
->style
& LBS_NODATA
)) descr
->u
.items
[index
].str
= string
;
195 static UINT
get_item_height( const LB_DESCR
*descr
, UINT index
)
197 return (descr
->style
& LBS_NODATA
) ? 0 : descr
->u
.items
[index
].height
;
200 static void set_item_height( LB_DESCR
*descr
, UINT index
, UINT height
)
202 if (!(descr
->style
& LBS_NODATA
)) descr
->u
.items
[index
].height
= height
;
205 static BOOL
is_item_selected( const LB_DESCR
*descr
, UINT index
)
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
];
212 return descr
->u
.items
[index
].selected
;
215 static void set_item_selected_state(LB_DESCR
*descr
, UINT index
, BOOL state
)
217 if (descr
->style
& (LBS_MULTIPLESEL
| LBS_EXTENDEDSEL
))
219 if (descr
->style
& LBS_NODATA
)
220 descr
->u
.nodata_items
[index
] = state
;
222 descr
->u
.items
[index
].selected
= state
;
226 static void insert_item_data(LB_DESCR
*descr
, UINT index
)
228 size_t size
= get_sizeof_item(descr
);
229 BYTE
*p
= descr
->u
.nodata_items
+ index
* size
;
231 if (!descr
->u
.items
) return;
233 if (index
< descr
->nb_items
)
234 memmove(p
+ size
, p
, (descr
->nb_items
- index
) * size
);
237 static void remove_item_data(LB_DESCR
*descr
, UINT index
)
239 size_t size
= get_sizeof_item(descr
);
240 BYTE
*p
= descr
->u
.nodata_items
+ index
* size
;
242 if (!descr
->u
.items
) return;
244 if (index
< descr
->nb_items
)
245 memmove(p
, p
+ size
, (descr
->nb_items
- index
) * size
);
248 /***********************************************************************
249 * LISTBOX_GetCurrentPageSize
251 * Return the current page size
253 static INT
LISTBOX_GetCurrentPageSize( const LB_DESCR
*descr
)
256 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
257 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
259 if ((height
+= get_item_height(descr
, i
)) > descr
->height
) break;
261 if (i
== descr
->top_item
) return 1;
262 else return i
- descr
->top_item
;
266 /***********************************************************************
267 * LISTBOX_GetMaxTopIndex
269 * Return the maximum possible index for the top of the listbox.
271 static INT
LISTBOX_GetMaxTopIndex( const LB_DESCR
*descr
)
275 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
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
++;
282 else if (descr
->style
& LBS_MULTICOLUMN
)
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
;
290 max
= descr
->nb_items
- descr
->page_size
;
292 if (max
< 0) max
= 0;
297 /***********************************************************************
298 * LISTBOX_UpdateScroll
300 * Update the scrollbars. Should be called whenever the content
301 * of the listbox changes.
303 static void LISTBOX_UpdateScroll( LB_DESCR
*descr
)
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;
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. */
322 if (descr
->style
& LBS_NOREDRAW
) return;
323 info
.cbSize
= sizeof(info
);
325 if (descr
->style
& LBS_MULTICOLUMN
)
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
);
338 info
.fMask
= SIF_RANGE
;
339 if (descr
->style
& WS_VSCROLL
)
340 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
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
);
354 if ((descr
->style
& WS_HSCROLL
) && descr
->horz_extent
)
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
);
365 if (descr
->style
& LBS_DISABLENOSCROLL
)
369 info
.fMask
= SIF_RANGE
| SIF_DISABLENOSCROLL
;
370 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
374 ShowScrollBar( descr
->self
, SB_HORZ
, FALSE
);
381 /***********************************************************************
384 * Set the top item of the listbox, scrolling up or down if necessary.
386 static LRESULT
LISTBOX_SetTopItem( LB_DESCR
*descr
, INT index
, BOOL scroll
)
388 INT max
= LISTBOX_GetMaxTopIndex( descr
);
390 TRACE("setting top item %d, scroll %d\n", index
, scroll
);
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
;
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
)
404 if (index
> descr
->top_item
)
406 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
407 dy
-= get_item_height(descr
, i
);
411 for (i
= index
; i
< descr
->top_item
; i
++)
412 dy
+= get_item_height(descr
, i
);
416 dy
= (descr
->top_item
- index
) * descr
->item_height
;
418 ScrollWindowEx( descr
->self
, dx
, dy
, NULL
, NULL
, 0, NULL
,
419 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
422 InvalidateRect( descr
->self
, NULL
, TRUE
);
423 descr
->top_item
= index
;
424 LISTBOX_UpdateScroll( descr
);
429 /***********************************************************************
432 * Update the page size. Should be called when the size of
433 * the client area or the item height changes.
435 static void LISTBOX_UpdatePage( LB_DESCR
*descr
)
439 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 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
);
449 /***********************************************************************
452 * Update the size of the listbox. Should be called when the size of
453 * the client area changes.
455 static void LISTBOX_UpdateSize( LB_DESCR
*descr
)
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
))
467 GetWindowRect( descr
->self
, &rect
);
468 if(descr
->item_height
!= 0)
469 remaining
= descr
->height
% descr
->item_height
;
472 if ((descr
->height
> descr
->item_height
) && remaining
)
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
);
482 TRACE("[%p]: new size = %d,%d\n", descr
->self
, descr
->width
, descr
->height
);
483 LISTBOX_UpdatePage( descr
);
484 LISTBOX_UpdateScroll( descr
);
486 /* Invalidate the focused item so it will be repainted correctly */
487 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
489 InvalidateRect( descr
->self
, &rect
, FALSE
);
494 /***********************************************************************
495 * LISTBOX_GetItemRect
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.
500 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
)
502 /* Index <= 0 is legal even on empty listboxes */
503 if (index
&& (index
>= descr
->nb_items
))
506 SetLastError(ERROR_INVALID_INDEX
);
509 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
510 if (descr
->style
& LBS_MULTICOLUMN
)
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
;
519 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
522 rect
->right
+= descr
->horz_pos
;
523 if ((index
>= 0) && (index
< descr
->nb_items
))
525 if (index
< descr
->top_item
)
527 for (i
= descr
->top_item
-1; i
>= index
; i
--)
528 rect
->top
-= get_item_height(descr
, i
);
532 for (i
= descr
->top_item
; i
< index
; i
++)
533 rect
->top
+= get_item_height(descr
, i
);
535 rect
->bottom
= rect
->top
+ get_item_height(descr
, index
);
541 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
542 rect
->bottom
= rect
->top
+ descr
->item_height
;
543 rect
->right
+= descr
->horz_pos
;
546 TRACE("item %d, rect %s\n", index
, wine_dbgstr_rect(rect
));
548 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
549 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
553 /***********************************************************************
554 * LISTBOX_GetItemFromPoint
556 * Return the item nearest from point (x,y) (in client coordinates).
558 static INT
LISTBOX_GetItemFromPoint( const LB_DESCR
*descr
, INT x
, INT y
)
560 INT index
= descr
->top_item
;
562 if (!descr
->nb_items
) return -1; /* No items */
563 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
568 while (index
< descr
->nb_items
)
570 if ((pos
+= get_item_height(descr
, index
)) > y
) break;
579 if ((pos
-= get_item_height(descr
, index
)) <= y
) break;
583 else if (descr
->style
& LBS_MULTICOLUMN
)
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
;
592 index
+= (y
/ descr
->item_height
);
594 if (index
< 0) return 0;
595 if (index
>= descr
->nb_items
) return -1;
600 /***********************************************************************
605 static void LISTBOX_PaintItem( LB_DESCR
*descr
, HDC hdc
, const RECT
*rect
,
606 INT index
, UINT action
, BOOL ignoreFocus
)
608 BOOL selected
= FALSE
, focused
;
609 WCHAR
*item_str
= NULL
;
611 if (index
< descr
->nb_items
)
613 item_str
= get_item_string(descr
, index
);
614 selected
= is_item_selected(descr
, index
);
617 focused
= !ignoreFocus
&& descr
->focus_item
== index
&& descr
->caret_on
&& descr
->in_focus
;
619 if (IS_OWNERDRAW(descr
))
625 if (index
>= descr
->nb_items
)
627 if (action
== ODA_FOCUS
)
628 DrawFocusRect( hdc
, rect
);
630 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
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
);
641 dis
.CtlType
= ODT_LISTBOX
;
642 dis
.CtlID
= GetWindowLongPtrW( descr
->self
, GWLP_ID
);
643 dis
.hwndItem
= descr
->self
;
644 dis
.itemAction
= action
;
649 dis
.itemState
|= ODS_SELECTED
;
651 dis
.itemState
|= ODS_FOCUS
;
652 if (!IsWindowEnabled(descr
->self
)) dis
.itemState
|= ODS_DISABLED
;
653 dis
.itemData
= get_item_data(descr
, index
);
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
);
664 COLORREF oldText
= 0, oldBk
= 0;
666 if (action
== ODA_FOCUS
)
668 DrawFocusRect( hdc
, rect
);
673 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
674 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
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
) );
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
);
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);
698 SetBkColor( hdc
, oldBk
);
699 SetTextColor( hdc
, oldText
);
702 DrawFocusRect( hdc
, rect
);
707 /***********************************************************************
710 * Change the redraw flag.
712 static void LISTBOX_SetRedraw( LB_DESCR
*descr
, BOOL on
)
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;
726 descr
->style
&= ~LBS_DISPLAYCHANGED
;
728 LISTBOX_UpdateScroll( descr
);
730 else descr
->style
|= LBS_NOREDRAW
;
734 /***********************************************************************
735 * LISTBOX_RepaintItem
737 * Repaint a single item synchronously.
739 static void LISTBOX_RepaintItem( LB_DESCR
*descr
, INT index
, UINT action
)
744 HBRUSH hbrush
, oldBrush
= 0;
746 /* Do not repaint the item if the item is not visible */
747 if (!IsWindowVisible(descr
->self
)) return;
748 if (descr
->style
& LBS_NOREDRAW
)
750 descr
->style
|= LBS_DISPLAYCHANGED
;
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
);
769 /***********************************************************************
770 * LISTBOX_DrawFocusRect
772 static void LISTBOX_DrawFocusRect( LB_DESCR
*descr
, BOOL on
)
778 /* Do not repaint the item if the item is not visible */
779 if (!IsWindowVisible(descr
->self
)) return;
781 if (descr
->focus_item
== -1) return;
782 if (!descr
->caret_on
|| !descr
->in_focus
) return;
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
);
796 /***********************************************************************
797 * LISTBOX_InitStorage
799 static LRESULT
LISTBOX_InitStorage( LB_DESCR
*descr
, INT nb_items
)
801 UINT new_size
= descr
->nb_items
+ nb_items
;
803 if (new_size
> descr
->items_size
&& !resize_storage(descr
, new_size
))
805 return descr
->items_size
;
809 /***********************************************************************
810 * LISTBOX_SetTabStops
812 static BOOL
LISTBOX_SetTabStops( LB_DESCR
*descr
, INT count
, LPINT tabs
)
816 if (!(descr
->style
& LBS_USETABSTOPS
))
818 SetLastError(ERROR_LB_WITHOUT_TABSTOPS
);
822 HeapFree( GetProcessHeap(), 0, descr
->tabs
);
823 if (!(descr
->nb_tabs
= count
))
828 if (!(descr
->tabs
= HeapAlloc( GetProcessHeap(), 0,
829 descr
->nb_tabs
* sizeof(INT
) )))
831 memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
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);
841 /***********************************************************************
844 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPWSTR buffer
, BOOL unicode
)
848 if ((index
< 0) || (index
>= descr
->nb_items
))
850 SetLastError(ERROR_INVALID_INDEX
);
854 if (HAS_STRINGS(descr
))
856 WCHAR
*str
= get_item_string(descr
, index
);
859 return lstrlenW(str
);
861 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(str
));
863 __TRY
/* hide a Delphi bug that passes a read-only buffer */
865 lstrcpyW(buffer
, str
);
866 len
= lstrlenW(buffer
);
870 WARN( "got an invalid buffer (Delphi bug?)\n" );
871 SetLastError( ERROR_INVALID_PARAMETER
);
878 *((ULONG_PTR
*)buffer
) = get_item_data(descr
, index
);
879 len
= sizeof(ULONG_PTR
);
884 static inline INT
LISTBOX_lstrcmpiW( LCID lcid
, LPCWSTR str1
, LPCWSTR str2
)
886 INT ret
= CompareStringW( lcid
, NORM_IGNORECASE
, str1
, -1, str2
, -1 );
887 if (ret
== CSTR_LESS_THAN
)
889 if (ret
== CSTR_EQUAL
)
891 if (ret
== CSTR_GREATER_THAN
)
896 /***********************************************************************
897 * LISTBOX_FindStringPos
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.
902 static INT
LISTBOX_FindStringPos( LB_DESCR
*descr
, LPCWSTR str
, BOOL exact
)
904 INT index
, min
, max
, res
;
906 if (!descr
->nb_items
|| !(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
909 max
= descr
->nb_items
- 1;
912 index
= (min
+ max
) / 2;
913 if (HAS_STRINGS(descr
))
914 res
= LISTBOX_lstrcmpiW( descr
->locale
, get_item_string(descr
, index
), str
);
917 COMPAREITEMSTRUCT cis
;
918 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
920 cis
.CtlType
= ODT_LISTBOX
;
922 cis
.hwndItem
= descr
->self
;
923 /* note that some application (MetaStock) expects the second item
924 * to be in the listbox */
926 cis
.itemData1
= get_item_data(descr
, index
);
928 cis
.itemData2
= (ULONG_PTR
)str
;
929 cis
.dwLocaleId
= descr
->locale
;
930 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
932 if (!res
) return index
;
933 if (res
> 0) max
= index
- 1;
934 else min
= index
+ 1;
936 return exact
? -1 : min
;
940 /***********************************************************************
941 * LISTBOX_FindFileStrPos
943 * Find the nearest string located before a given string in directory
944 * sort order (i.e. first files, then directories, then drives).
946 static INT
LISTBOX_FindFileStrPos( LB_DESCR
*descr
, LPCWSTR str
)
950 if (!HAS_STRINGS(descr
))
951 return LISTBOX_FindStringPos( descr
, str
, FALSE
);
953 max
= descr
->nb_items
;
956 INT index
= (min
+ max
) / 2;
957 LPCWSTR p
= get_item_string(descr
, index
);
958 if (*p
== '[') /* drive or directory */
960 if (*str
!= '[') res
= -1;
961 else if (p
[1] == '-') /* drive */
963 if (str
[1] == '-') res
= str
[2] - p
[2];
968 if (str
[1] == '-') res
= 1;
969 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
974 if (*str
== '[') res
= 1;
975 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
977 if (!res
) return index
;
978 if (res
< 0) max
= index
;
979 else min
= index
+ 1;
985 /***********************************************************************
988 * Find the item beginning with a given string.
990 static INT
LISTBOX_FindString( LB_DESCR
*descr
, INT start
, LPCWSTR str
, BOOL exact
)
994 if (descr
->style
& LBS_NODATA
) return LB_ERR
;
997 if (start
>= descr
->nb_items
) start
= 0;
998 if (HAS_STRINGS(descr
))
1000 if (!str
|| ! str
[0] ) return LB_ERR
;
1003 for (i
= 0, index
= start
; i
< descr
->nb_items
; i
++, index
++)
1005 if (index
== descr
->nb_items
) index
= 0;
1006 if (!LISTBOX_lstrcmpiW(descr
->locale
, str
, get_item_string(descr
, index
)))
1012 /* Special case for drives and directories: ignore prefix */
1013 INT len
= lstrlenW(str
);
1016 for (i
= 0, index
= start
; i
< descr
->nb_items
; i
++, index
++)
1018 if (index
== descr
->nb_items
) index
= 0;
1019 item_str
= get_item_string(descr
, index
);
1021 if (!wcsnicmp(str
, item_str
, len
)) return index
;
1022 if (item_str
[0] == '[')
1024 if (!wcsnicmp(str
, item_str
+ 1, len
)) return index
;
1025 if (item_str
[1] == '-' && !wcsnicmp(str
, item_str
+ 2, len
)) return index
;
1032 if (exact
&& (descr
->style
& LBS_SORT
))
1033 /* If sorted, use a WM_COMPAREITEM binary search */
1034 return LISTBOX_FindStringPos( descr
, str
, TRUE
);
1036 /* Otherwise use a linear search */
1037 for (i
= 0, index
= start
; i
< descr
->nb_items
; i
++, index
++)
1039 if (index
== descr
->nb_items
) index
= 0;
1040 if (get_item_data(descr
, index
) == (ULONG_PTR
)str
) return index
;
1047 /***********************************************************************
1048 * LISTBOX_GetSelCount
1050 static LRESULT
LISTBOX_GetSelCount( const LB_DESCR
*descr
)
1054 if (!(descr
->style
& LBS_MULTIPLESEL
) ||
1055 (descr
->style
& LBS_NOSEL
))
1057 for (i
= count
= 0; i
< descr
->nb_items
; i
++)
1058 if (is_item_selected(descr
, i
)) count
++;
1063 /***********************************************************************
1064 * LISTBOX_GetSelItems
1066 static LRESULT
LISTBOX_GetSelItems( const LB_DESCR
*descr
, INT max
, LPINT array
)
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
;
1077 /***********************************************************************
1080 static LRESULT
LISTBOX_Paint( LB_DESCR
*descr
, HDC hdc
)
1082 INT i
, col_pos
= descr
->page_size
- 1;
1084 RECT focusRect
= {-1, -1, -1, -1};
1086 HBRUSH hbrush
, oldBrush
= 0;
1088 if (descr
->style
& LBS_NOREDRAW
) return 0;
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
)
1095 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1096 rect
.right
+= descr
->horz_pos
;
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
) );
1105 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
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
;
1116 /* Paint all the item, regarding the selection
1117 Focus state will be painted after */
1119 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1121 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1122 rect
.bottom
= rect
.top
+ descr
->item_height
;
1124 rect
.bottom
= rect
.top
+ get_item_height(descr
, i
);
1126 /* keep the focus rect, to paint the focus item after */
1127 if (i
== descr
->focus_item
)
1130 LISTBOX_PaintItem( descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1131 rect
.top
= rect
.bottom
;
1133 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1135 if (!IS_OWNERDRAW(descr
))
1137 /* Clear the bottom of the column */
1138 if (rect
.top
< descr
->height
)
1140 rect
.bottom
= descr
->height
;
1141 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1142 &rect
, NULL
, 0, NULL
);
1146 /* Go to the next column */
1147 rect
.left
+= descr
->column_width
;
1148 rect
.right
+= descr
->column_width
;
1150 col_pos
= descr
->page_size
- 1;
1151 if (rect
.left
>= descr
->width
) break;
1156 if (rect
.top
>= descr
->height
) break;
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
);
1165 if (!IS_OWNERDRAW(descr
))
1167 /* Clear the remainder of the client area */
1168 if (rect
.top
< descr
->height
)
1170 rect
.bottom
= descr
->height
;
1171 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1172 &rect
, NULL
, 0, NULL
);
1174 if (rect
.right
< descr
->width
)
1176 rect
.left
= rect
.right
;
1177 rect
.right
= descr
->width
;
1179 rect
.bottom
= descr
->height
;
1180 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1181 &rect
, NULL
, 0, NULL
);
1184 if (oldFont
) SelectObject( hdc
, oldFont
);
1185 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1189 static LRESULT
LISTBOX_NCPaint( LB_DESCR
*descr
, HRGN region
)
1191 DWORD exstyle
= GetWindowLongW( descr
->self
, GWL_EXSTYLE
);
1192 HTHEME theme
= GetWindowTheme( descr
->self
);
1193 HRGN cliprgn
= region
;
1198 if (!theme
|| !(exstyle
& WS_EX_CLIENTEDGE
))
1199 return DefWindowProcW(descr
->self
, WM_NCPAINT
, (WPARAM
)region
, 0);
1201 cxEdge
= GetSystemMetrics(SM_CXEDGE
);
1202 cyEdge
= GetSystemMetrics(SM_CYEDGE
);
1204 GetWindowRect(descr
->self
, &r
);
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
);
1213 hdc
= GetDCEx(descr
->self
, region
, DCX_WINDOW
|DCX_INTERSECTRGN
);
1215 if (IsThemeBackgroundPartiallyTransparent (theme
, 0, 0))
1216 DrawThemeParentBackground(descr
->self
, hdc
, &r
);
1217 DrawThemeBackground (theme
, hdc
, 0, 0, &r
, 0);
1218 ReleaseDC(descr
->self
, hdc
);
1220 /* Call default proc to get the scrollbars etc. also painted */
1221 DefWindowProcW(descr
->self
, WM_NCPAINT
, (WPARAM
)cliprgn
, 0);
1222 DeleteObject(cliprgn
);
1226 /***********************************************************************
1227 * LISTBOX_InvalidateItems
1229 * Invalidate all items from a given item. If the specified item is not
1230 * visible, nothing happens.
1232 static void LISTBOX_InvalidateItems( LB_DESCR
*descr
, INT index
)
1236 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1238 if (descr
->style
& LBS_NOREDRAW
)
1240 descr
->style
|= LBS_DISPLAYCHANGED
;
1243 rect
.bottom
= descr
->height
;
1244 InvalidateRect( descr
->self
, &rect
, TRUE
);
1245 if (descr
->style
& LBS_MULTICOLUMN
)
1247 /* Repaint the other columns */
1248 rect
.left
= rect
.right
;
1249 rect
.right
= descr
->width
;
1251 InvalidateRect( descr
->self
, &rect
, TRUE
);
1256 static void LISTBOX_InvalidateItemRect( LB_DESCR
*descr
, INT index
)
1260 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1261 InvalidateRect( descr
->self
, &rect
, TRUE
);
1264 /***********************************************************************
1265 * LISTBOX_GetItemHeight
1267 static LRESULT
LISTBOX_GetItemHeight( const LB_DESCR
*descr
, INT index
)
1269 if (descr
->style
& LBS_OWNERDRAWVARIABLE
&& descr
->nb_items
> 0)
1271 if ((index
< 0) || (index
>= descr
->nb_items
))
1273 SetLastError(ERROR_INVALID_INDEX
);
1276 return get_item_height(descr
, index
);
1278 else return descr
->item_height
;
1282 /***********************************************************************
1283 * LISTBOX_SetItemHeight
1285 static LRESULT
LISTBOX_SetItemHeight( LB_DESCR
*descr
, INT index
, INT height
, BOOL repaint
)
1287 if (height
> MAXWORD
)
1290 if (!height
) height
= 1;
1292 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1294 if ((index
< 0) || (index
>= descr
->nb_items
))
1296 SetLastError(ERROR_INVALID_INDEX
);
1299 TRACE("[%p]: item %d height = %d\n", descr
->self
, index
, height
);
1300 set_item_height(descr
, index
, height
);
1301 LISTBOX_UpdateScroll( descr
);
1303 LISTBOX_InvalidateItems( descr
, index
);
1305 else if (height
!= descr
->item_height
)
1307 TRACE("[%p]: new height = %d\n", descr
->self
, height
);
1308 descr
->item_height
= height
;
1309 LISTBOX_UpdatePage( descr
);
1310 LISTBOX_UpdateScroll( descr
);
1312 InvalidateRect( descr
->self
, 0, TRUE
);
1318 /***********************************************************************
1319 * LISTBOX_SetHorizontalPos
1321 static void LISTBOX_SetHorizontalPos( LB_DESCR
*descr
, INT pos
)
1325 if (pos
> descr
->horz_extent
- descr
->width
)
1326 pos
= descr
->horz_extent
- descr
->width
;
1327 if (pos
< 0) pos
= 0;
1328 if (!(diff
= descr
->horz_pos
- pos
)) return;
1329 TRACE("[%p]: new horz pos = %d\n", descr
->self
, pos
);
1330 descr
->horz_pos
= pos
;
1331 LISTBOX_UpdateScroll( descr
);
1332 if (abs(diff
) < descr
->width
)
1335 /* Invalidate the focused item so it will be repainted correctly */
1336 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1337 InvalidateRect( descr
->self
, &rect
, TRUE
);
1338 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
1339 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1342 InvalidateRect( descr
->self
, NULL
, TRUE
);
1346 /***********************************************************************
1347 * LISTBOX_SetHorizontalExtent
1349 static LRESULT
LISTBOX_SetHorizontalExtent( LB_DESCR
*descr
, INT extent
)
1351 if (descr
->style
& LBS_MULTICOLUMN
)
1353 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1354 TRACE("[%p]: new horz extent = %d\n", descr
->self
, extent
);
1355 descr
->horz_extent
= extent
;
1356 if (descr
->style
& WS_HSCROLL
) {
1358 info
.cbSize
= sizeof(info
);
1360 info
.nMax
= descr
->horz_extent
? descr
->horz_extent
- 1 : 0;
1361 info
.fMask
= SIF_RANGE
;
1362 if (descr
->style
& LBS_DISABLENOSCROLL
)
1363 info
.fMask
|= SIF_DISABLENOSCROLL
;
1364 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
1366 if (descr
->horz_pos
> extent
- descr
->width
)
1367 LISTBOX_SetHorizontalPos( descr
, extent
- descr
->width
);
1372 /***********************************************************************
1373 * LISTBOX_SetColumnWidth
1375 static LRESULT
LISTBOX_SetColumnWidth( LB_DESCR
*descr
, INT column_width
)
1379 TRACE("[%p]: new column width = %d\n", descr
->self
, column_width
);
1381 GetClientRect(descr
->self
, &rect
);
1382 descr
->width
= rect
.right
- rect
.left
;
1383 descr
->height
= rect
.bottom
- rect
.top
;
1384 descr
->column_width
= column_width
;
1386 LISTBOX_UpdatePage(descr
);
1387 LISTBOX_UpdateScroll(descr
);
1392 /***********************************************************************
1395 * Returns the item height.
1397 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1401 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1406 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1408 ERR("unable to get DC.\n" );
1411 if (font
) oldFont
= SelectObject( hdc
, font
);
1412 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1413 if (oldFont
) SelectObject( hdc
, oldFont
);
1414 ReleaseDC( descr
->self
, hdc
);
1416 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1417 if (!IS_OWNERDRAW(descr
))
1418 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1423 /***********************************************************************
1424 * LISTBOX_MakeItemVisible
1426 * Make sure that a given item is partially or fully visible.
1428 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1432 TRACE("current top item %d, index %d, fully %d\n", descr
->top_item
, index
, fully
);
1434 if (index
<= descr
->top_item
) top
= index
;
1435 else if (descr
->style
& LBS_MULTICOLUMN
)
1437 INT cols
= descr
->width
;
1438 if (!fully
) cols
+= descr
->column_width
- 1;
1439 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1441 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1442 top
= index
- descr
->page_size
* (cols
- 1);
1444 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1446 INT height
= fully
? get_item_height(descr
, index
) : 1;
1447 for (top
= index
; top
> descr
->top_item
; top
--)
1448 if ((height
+= get_item_height(descr
, top
- 1)) > descr
->height
) break;
1452 if (index
< descr
->top_item
+ descr
->page_size
) return;
1453 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1454 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1455 top
= index
- descr
->page_size
+ 1;
1457 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1460 /***********************************************************************
1461 * LISTBOX_SetCaretIndex
1464 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1467 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1469 BOOL focus_changed
= descr
->focus_item
!= index
;
1471 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1473 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1474 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1478 LISTBOX_DrawFocusRect( descr
, FALSE
);
1479 descr
->focus_item
= index
;
1482 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1485 LISTBOX_DrawFocusRect( descr
, TRUE
);
1491 /***********************************************************************
1492 * LISTBOX_SelectItemRange
1494 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1496 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1501 /* A few sanity checks */
1503 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1504 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1506 if (!descr
->nb_items
) return LB_OKAY
;
1508 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1509 if (first
< 0) first
= 0;
1510 if (last
< first
) return LB_OKAY
;
1512 if (on
) /* Turn selection on */
1514 for (i
= first
; i
<= last
; i
++)
1516 if (is_item_selected(descr
, i
)) continue;
1517 set_item_selected_state(descr
, i
, TRUE
);
1518 LISTBOX_InvalidateItemRect(descr
, i
);
1521 else /* Turn selection off */
1523 for (i
= first
; i
<= last
; i
++)
1525 if (!is_item_selected(descr
, i
)) continue;
1526 set_item_selected_state(descr
, i
, FALSE
);
1527 LISTBOX_InvalidateItemRect(descr
, i
);
1533 /***********************************************************************
1534 * LISTBOX_SetSelection
1536 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1537 BOOL on
, BOOL send_notify
)
1539 TRACE( "cur_sel=%d index=%d notify=%s\n",
1540 descr
->selected_item
, index
, send_notify
? "YES" : "NO" );
1542 if (descr
->style
& LBS_NOSEL
)
1544 descr
->selected_item
= index
;
1547 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1548 if (descr
->style
& LBS_MULTIPLESEL
)
1550 if (index
== -1) /* Select all items */
1551 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1552 else /* Only one item */
1553 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1557 INT oldsel
= descr
->selected_item
;
1558 if (index
== oldsel
) return LB_OKAY
;
1559 if (oldsel
!= -1) set_item_selected_state(descr
, oldsel
, FALSE
);
1560 if (index
!= -1) set_item_selected_state(descr
, index
, TRUE
);
1561 descr
->selected_item
= index
;
1562 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1563 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1564 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1565 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1567 if( descr
->lphc
) /* set selection change flag for parent combo */
1568 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1574 /***********************************************************************
1577 * Change the caret position and extend the selection to the new caret.
1579 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1581 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1583 if ((index
< 0) || (index
>= descr
->nb_items
))
1586 /* Important, repaint needs to be done in this order if
1587 you want to mimic Windows behavior:
1588 1. Remove the focus and paint the item
1589 2. Remove the selection and paint the item(s)
1590 3. Set the selection and repaint the item(s)
1591 4. Set the focus to 'index' and repaint the item */
1593 /* 1. remove the focus and repaint the item */
1594 LISTBOX_DrawFocusRect( descr
, FALSE
);
1596 /* 2. then turn off the previous selection */
1597 /* 3. repaint the new selected item */
1598 if (descr
->style
& LBS_EXTENDEDSEL
)
1600 if (descr
->anchor_item
!= -1)
1602 INT first
= min( index
, descr
->anchor_item
);
1603 INT last
= max( index
, descr
->anchor_item
);
1605 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1606 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1607 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1610 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1612 /* Set selection to new caret item */
1613 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1616 /* 4. repaint the new item with the focus */
1617 descr
->focus_item
= index
;
1618 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1619 LISTBOX_DrawFocusRect( descr
, TRUE
);
1623 /***********************************************************************
1624 * LISTBOX_InsertItem
1626 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1627 LPWSTR str
, ULONG_PTR data
)
1629 INT oldfocus
= descr
->focus_item
;
1631 if (index
== -1) index
= descr
->nb_items
;
1632 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1633 if (!resize_storage(descr
, descr
->nb_items
+ 1)) return LB_ERR
;
1635 insert_item_data(descr
, index
);
1637 set_item_string(descr
, index
, str
);
1638 set_item_data(descr
, index
, HAS_STRINGS(descr
) ? 0 : data
);
1639 set_item_height(descr
, index
, 0);
1640 set_item_selected_state(descr
, index
, FALSE
);
1642 /* Get item height */
1644 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1646 MEASUREITEMSTRUCT mis
;
1647 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1649 mis
.CtlType
= ODT_LISTBOX
;
1652 mis
.itemData
= data
;
1653 mis
.itemHeight
= descr
->item_height
;
1654 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1655 set_item_height(descr
, index
, mis
.itemHeight
? mis
.itemHeight
: 1);
1656 TRACE("[%p]: measure item %d (%s) = %d\n",
1657 descr
->self
, index
, str
? debugstr_w(str
) : "", get_item_height(descr
, index
));
1660 /* Repaint the items */
1662 LISTBOX_UpdateScroll( descr
);
1663 LISTBOX_InvalidateItems( descr
, index
);
1665 /* Move selection and focused item */
1666 /* If listbox was empty, set focus to the first item */
1667 if (descr
->nb_items
== 1)
1668 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1669 /* single select don't change selection index in win31 */
1670 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1672 descr
->selected_item
++;
1673 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1677 if (index
<= descr
->selected_item
)
1679 descr
->selected_item
++;
1680 descr
->focus_item
= oldfocus
; /* focus not changed */
1687 /***********************************************************************
1688 * LISTBOX_InsertString
1690 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
, LPCWSTR str
)
1692 LPWSTR new_str
= NULL
;
1695 if (HAS_STRINGS(descr
))
1697 if (!str
) str
= L
"";
1698 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (lstrlenW(str
) + 1) * sizeof(WCHAR
) )))
1700 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1703 lstrcpyW(new_str
, str
);
1706 if (index
== -1) index
= descr
->nb_items
;
1707 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, (ULONG_PTR
)str
)) != 0)
1709 HeapFree( GetProcessHeap(), 0, new_str
);
1713 TRACE("[%p]: added item %d %s\n",
1714 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1719 /***********************************************************************
1720 * LISTBOX_DeleteItem
1722 * Delete the content of an item. 'index' must be a valid index.
1724 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1726 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1727 * while Win95 sends it for all items with user data.
1728 * It's probably better to send it too often than not
1729 * often enough, so this is what we do here.
1731 if (IS_OWNERDRAW(descr
) || get_item_data(descr
, index
))
1733 DELETEITEMSTRUCT dis
;
1734 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1736 dis
.CtlType
= ODT_LISTBOX
;
1739 dis
.hwndItem
= descr
->self
;
1740 dis
.itemData
= get_item_data(descr
, index
);
1741 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1743 HeapFree( GetProcessHeap(), 0, get_item_string(descr
, index
) );
1747 /***********************************************************************
1748 * LISTBOX_RemoveItem
1750 * Remove an item from the listbox and delete its content.
1752 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1754 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1756 /* We need to invalidate the original rect instead of the updated one. */
1757 LISTBOX_InvalidateItems( descr
, index
);
1759 if (descr
->nb_items
== 1)
1761 SendMessageW(descr
->self
, LB_RESETCONTENT
, 0, 0);
1765 LISTBOX_DeleteItem( descr
, index
);
1766 remove_item_data(descr
, index
);
1768 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1769 resize_storage(descr
, descr
->nb_items
);
1771 /* Repaint the items */
1773 LISTBOX_UpdateScroll( descr
);
1774 /* if we removed the scrollbar, reset the top of the list
1775 (correct for owner-drawn ???) */
1776 if (descr
->nb_items
== descr
->page_size
)
1777 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1779 /* Move selection and focused item */
1780 if (!IS_MULTISELECT(descr
))
1782 if (index
== descr
->selected_item
)
1783 descr
->selected_item
= -1;
1784 else if (index
< descr
->selected_item
)
1786 descr
->selected_item
--;
1787 if (ISWIN31
) /* win 31 do not change the selected item number */
1788 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1792 if (descr
->focus_item
>= descr
->nb_items
)
1794 descr
->focus_item
= descr
->nb_items
- 1;
1795 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1801 /***********************************************************************
1802 * LISTBOX_ResetContent
1804 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1808 if (!(descr
->style
& LBS_NODATA
))
1809 for (i
= descr
->nb_items
- 1; i
>= 0; i
--) LISTBOX_DeleteItem(descr
, i
);
1810 HeapFree( GetProcessHeap(), 0, descr
->u
.items
);
1811 descr
->nb_items
= 0;
1812 descr
->top_item
= 0;
1813 descr
->selected_item
= -1;
1814 descr
->focus_item
= 0;
1815 descr
->anchor_item
= -1;
1816 descr
->items_size
= 0;
1817 descr
->u
.items
= NULL
;
1821 /***********************************************************************
1824 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, UINT count
)
1826 UINT orig_num
= descr
->nb_items
;
1828 if (!(descr
->style
& LBS_NODATA
)) return LB_ERR
;
1830 if (!resize_storage(descr
, count
))
1832 descr
->nb_items
= count
;
1836 LISTBOX_UpdateScroll(descr
);
1837 if (count
< orig_num
)
1839 descr
->anchor_item
= min(descr
->anchor_item
, count
- 1);
1840 if (descr
->selected_item
>= count
)
1841 descr
->selected_item
= -1;
1843 /* If we removed the scrollbar, reset the top of the list */
1844 if (count
<= descr
->page_size
&& orig_num
> descr
->page_size
)
1845 LISTBOX_SetTopItem(descr
, 0, TRUE
);
1847 descr
->focus_item
= min(descr
->focus_item
, count
- 1);
1850 /* If it was empty before growing, set focus to the first item */
1851 else if (orig_num
== 0) LISTBOX_SetCaretIndex(descr
, 0, FALSE
);
1853 else SendMessageW(descr
->self
, LB_RESETCONTENT
, 0, 0);
1855 InvalidateRect( descr
->self
, NULL
, TRUE
);
1860 /***********************************************************************
1863 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1864 LPCWSTR filespec
, BOOL long_names
)
1867 LRESULT ret
= LB_OKAY
;
1868 WIN32_FIND_DATAW entry
;
1870 LRESULT maxinsert
= LB_ERR
;
1872 /* don't scan directory if we just want drives exclusively */
1873 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1874 /* scan directory */
1875 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1877 int le
= GetLastError();
1878 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1885 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1887 if (!(attrib
& DDL_DIRECTORY
) ||
1888 !lstrcmpW( entry
.cFileName
, L
"." )) continue;
1890 if (!long_names
&& entry
.cAlternateFileName
[0])
1891 lstrcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1893 lstrcpyW( buffer
+ 1, entry
.cFileName
);
1894 lstrcatW(buffer
, L
"]");
1896 else /* not a directory */
1898 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1899 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1901 if ((attrib
& DDL_EXCLUSIVE
) &&
1902 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1905 if (!long_names
&& entry
.cAlternateFileName
[0])
1906 lstrcpyW( buffer
, entry
.cAlternateFileName
);
1908 lstrcpyW( buffer
, entry
.cFileName
);
1910 if (!long_names
) CharLowerW( buffer
);
1911 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1912 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1914 if (ret
<= maxinsert
) maxinsert
++; else maxinsert
= ret
;
1915 } while (FindNextFileW( handle
, &entry
));
1916 FindClose( handle
);
1924 if (attrib
& DDL_DRIVES
)
1926 WCHAR buffer
[] = L
"[-a-]";
1927 WCHAR root
[] = L
"A:\\";
1929 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1931 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1932 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1941 /***********************************************************************
1942 * LISTBOX_HandleVScroll
1944 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1948 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1952 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1955 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1958 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1959 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1962 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1963 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1965 case SB_THUMBPOSITION
:
1966 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1969 info
.cbSize
= sizeof(info
);
1970 info
.fMask
= SIF_TRACKPOS
;
1971 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1972 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1975 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1978 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1985 /***********************************************************************
1986 * LISTBOX_HandleHScroll
1988 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1993 if (descr
->style
& LBS_MULTICOLUMN
)
1998 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
2002 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
2006 page
= descr
->width
/ descr
->column_width
;
2007 if (page
< 1) page
= 1;
2008 LISTBOX_SetTopItem( descr
,
2009 descr
->top_item
- page
* descr
->page_size
, TRUE
);
2012 page
= descr
->width
/ descr
->column_width
;
2013 if (page
< 1) page
= 1;
2014 LISTBOX_SetTopItem( descr
,
2015 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
2017 case SB_THUMBPOSITION
:
2018 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
2021 info
.cbSize
= sizeof(info
);
2022 info
.fMask
= SIF_TRACKPOS
;
2023 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
2024 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
2028 LISTBOX_SetTopItem( descr
, 0, TRUE
);
2031 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
2035 else if (descr
->horz_extent
)
2040 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
2043 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
2046 LISTBOX_SetHorizontalPos( descr
,
2047 descr
->horz_pos
- descr
->width
);
2050 LISTBOX_SetHorizontalPos( descr
,
2051 descr
->horz_pos
+ descr
->width
);
2053 case SB_THUMBPOSITION
:
2054 LISTBOX_SetHorizontalPos( descr
, pos
);
2057 info
.cbSize
= sizeof(info
);
2058 info
.fMask
= SIF_TRACKPOS
;
2059 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
2060 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
2063 LISTBOX_SetHorizontalPos( descr
, 0 );
2066 LISTBOX_SetHorizontalPos( descr
,
2067 descr
->horz_extent
- descr
->width
);
2074 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
2076 INT pulScrollLines
= 3;
2078 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
2080 /* if scrolling changes direction, ignore left overs */
2081 if ((delta
< 0 && descr
->wheel_remain
< 0) ||
2082 (delta
> 0 && descr
->wheel_remain
> 0))
2083 descr
->wheel_remain
+= delta
;
2085 descr
->wheel_remain
= delta
;
2087 if (descr
->wheel_remain
&& pulScrollLines
)
2090 if (descr
->style
& LBS_MULTICOLUMN
)
2092 pulScrollLines
= min(descr
->width
/ descr
->column_width
, pulScrollLines
);
2093 pulScrollLines
= max(1, pulScrollLines
);
2094 cLineScroll
= pulScrollLines
* descr
->wheel_remain
/ WHEEL_DELTA
;
2095 descr
->wheel_remain
-= WHEEL_DELTA
* cLineScroll
/ pulScrollLines
;
2096 cLineScroll
*= descr
->page_size
;
2100 pulScrollLines
= min(descr
->page_size
, pulScrollLines
);
2101 cLineScroll
= pulScrollLines
* descr
->wheel_remain
/ WHEEL_DELTA
;
2102 descr
->wheel_remain
-= WHEEL_DELTA
* cLineScroll
/ pulScrollLines
;
2104 LISTBOX_SetTopItem( descr
, descr
->top_item
- cLineScroll
, TRUE
);
2109 /***********************************************************************
2110 * LISTBOX_HandleLButtonDown
2112 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
2114 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2116 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2117 descr
->self
, x
, y
, index
, descr
->focus_item
);
2119 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2121 if (!descr
->in_focus
)
2123 if( !descr
->lphc
) SetFocus( descr
->self
);
2124 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2127 if (index
== -1) return 0;
2131 if (descr
->style
& LBS_NOTIFY
)
2132 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2133 MAKELPARAM( x
, y
) );
2136 descr
->captured
= TRUE
;
2137 SetCapture( descr
->self
);
2139 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2141 /* we should perhaps make sure that all items are deselected
2142 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2143 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2144 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2147 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2148 if (keys
& MK_CONTROL
)
2150 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2151 LISTBOX_SetSelection( descr
, index
,
2152 !is_item_selected(descr
, index
),
2153 (descr
->style
& LBS_NOTIFY
) != 0);
2157 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2159 if (descr
->style
& LBS_EXTENDEDSEL
)
2161 LISTBOX_SetSelection( descr
, index
,
2162 is_item_selected(descr
, index
),
2163 (descr
->style
& LBS_NOTIFY
) != 0 );
2167 LISTBOX_SetSelection( descr
, index
,
2168 !is_item_selected(descr
, index
),
2169 (descr
->style
& LBS_NOTIFY
) != 0 );
2175 descr
->anchor_item
= index
;
2176 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2177 LISTBOX_SetSelection( descr
, index
,
2178 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2183 if (GetWindowLongW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2190 if (DragDetect( descr
->self
, pt
))
2191 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2198 /*************************************************************************
2199 * LISTBOX_HandleLButtonDownCombo [Internal]
2201 * Process LButtonDown message for the ComboListBox
2204 * pWnd [I] The windows internal structure
2205 * pDescr [I] The ListBox internal structure
2206 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2207 * x [I] X Mouse Coordinate
2208 * y [I] Y Mouse Coordinate
2211 * 0 since we are processing the WM_LBUTTONDOWN Message
2214 * This function is only to be used when a ListBox is a ComboListBox
2217 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2219 RECT clientRect
, screenRect
;
2225 GetClientRect(descr
->self
, &clientRect
);
2227 if(PtInRect(&clientRect
, mousePos
))
2229 /* MousePos is in client, resume normal processing */
2230 if (msg
== WM_LBUTTONDOWN
)
2232 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2233 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2235 else if (descr
->style
& LBS_NOTIFY
)
2236 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2240 POINT screenMousePos
;
2241 HWND hWndOldCapture
;
2243 /* Check the Non-Client Area */
2244 screenMousePos
= mousePos
;
2245 hWndOldCapture
= GetCapture();
2247 GetWindowRect(descr
->self
, &screenRect
);
2248 ClientToScreen(descr
->self
, &screenMousePos
);
2250 if(!PtInRect(&screenRect
, screenMousePos
))
2252 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2253 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2254 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2258 /* Check to see the NC is a scrollbar */
2260 LONG style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2261 /* Check Vertical scroll bar */
2262 if (style
& WS_VSCROLL
)
2264 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2265 if (PtInRect( &clientRect
, mousePos
))
2266 nHitTestType
= HTVSCROLL
;
2268 /* Check horizontal scroll bar */
2269 if (style
& WS_HSCROLL
)
2271 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2272 if (PtInRect( &clientRect
, mousePos
))
2273 nHitTestType
= HTHSCROLL
;
2275 /* Windows sends this message when a scrollbar is clicked
2278 if(nHitTestType
!= 0)
2280 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2281 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2283 /* Resume the Capture after scrolling is complete
2285 if(hWndOldCapture
!= 0)
2286 SetCapture(hWndOldCapture
);
2292 /***********************************************************************
2293 * LISTBOX_HandleLButtonUp
2295 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2297 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2298 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2299 LISTBOX_Timer
= LB_TIMER_NONE
;
2300 if (descr
->captured
)
2302 descr
->captured
= FALSE
;
2303 if (GetCapture() == descr
->self
) ReleaseCapture();
2304 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2305 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2311 /***********************************************************************
2312 * LISTBOX_HandleTimer
2314 * Handle scrolling upon a timer event.
2315 * Return TRUE if scrolling should continue.
2317 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2322 if (descr
->top_item
) index
= descr
->top_item
- 1;
2326 if (descr
->top_item
) index
-= descr
->page_size
;
2329 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2330 if (index
== descr
->focus_item
) index
++;
2331 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2333 case LB_TIMER_RIGHT
:
2334 if (index
+ descr
->page_size
< descr
->nb_items
)
2335 index
+= descr
->page_size
;
2340 if (index
== descr
->focus_item
) return FALSE
;
2341 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2346 /***********************************************************************
2347 * LISTBOX_HandleSystemTimer
2349 * WM_SYSTIMER handler.
2351 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2353 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2355 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2356 LISTBOX_Timer
= LB_TIMER_NONE
;
2362 /***********************************************************************
2363 * LISTBOX_HandleMouseMove
2365 * WM_MOUSEMOVE handler.
2367 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2371 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2373 if (!descr
->captured
) return;
2375 if (descr
->style
& LBS_MULTICOLUMN
)
2378 else if (y
>= descr
->item_height
* descr
->page_size
)
2379 y
= descr
->item_height
* descr
->page_size
- 1;
2383 dir
= LB_TIMER_LEFT
;
2386 else if (x
>= descr
->width
)
2388 dir
= LB_TIMER_RIGHT
;
2389 x
= descr
->width
- 1;
2394 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2395 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2398 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2399 if (index
== -1) index
= descr
->focus_item
;
2400 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2402 /* Start/stop the system timer */
2404 if (dir
!= LB_TIMER_NONE
)
2405 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2406 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2407 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2408 LISTBOX_Timer
= dir
;
2412 /***********************************************************************
2413 * LISTBOX_HandleKeyDown
2415 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2418 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2419 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2420 bForceSelection
= FALSE
; /* only for single select list */
2422 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2424 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2425 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2426 (LPARAM
)descr
->self
);
2427 if (caret
== -2) return 0;
2429 if (caret
== -1) switch(key
)
2432 if (descr
->style
& LBS_MULTICOLUMN
)
2434 bForceSelection
= FALSE
;
2435 if (descr
->focus_item
>= descr
->page_size
)
2436 caret
= descr
->focus_item
- descr
->page_size
;
2441 caret
= descr
->focus_item
- 1;
2442 if (caret
< 0) caret
= 0;
2445 if (descr
->style
& LBS_MULTICOLUMN
)
2447 bForceSelection
= FALSE
;
2448 caret
= min(descr
->focus_item
+ descr
->page_size
, descr
->nb_items
- 1);
2453 caret
= descr
->focus_item
+ 1;
2454 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2458 if (descr
->style
& LBS_MULTICOLUMN
)
2460 INT page
= descr
->width
/ descr
->column_width
;
2461 if (page
< 1) page
= 1;
2462 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2464 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2465 if (caret
< 0) caret
= 0;
2468 if (descr
->style
& LBS_MULTICOLUMN
)
2470 INT page
= descr
->width
/ descr
->column_width
;
2471 if (page
< 1) page
= 1;
2472 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2474 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2475 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2481 caret
= descr
->nb_items
- 1;
2484 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2485 else if (descr
->style
& LBS_MULTIPLESEL
)
2487 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2488 !is_item_selected(descr
, descr
->focus_item
),
2489 (descr
->style
& LBS_NOTIFY
) != 0 );
2493 bForceSelection
= FALSE
;
2495 if (bForceSelection
) /* focused item is used instead of key */
2496 caret
= descr
->focus_item
;
2499 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2500 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2501 !IS_MULTISELECT(descr
))
2502 descr
->anchor_item
= caret
;
2503 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2505 if (descr
->style
& LBS_MULTIPLESEL
)
2506 descr
->selected_item
= caret
;
2508 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2509 if (descr
->style
& LBS_NOTIFY
)
2511 if (descr
->lphc
&& IsWindowVisible( descr
->self
))
2513 /* make sure that combo parent doesn't hide us */
2514 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2516 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2523 /***********************************************************************
2524 * LISTBOX_HandleChar
2526 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2534 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2536 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2537 MAKEWPARAM(charW
, descr
->focus_item
),
2538 (LPARAM
)descr
->self
);
2539 if (caret
== -2) return 0;
2542 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2545 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2546 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2547 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2548 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2549 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2555 /***********************************************************************
2558 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2561 MEASUREITEMSTRUCT mis
;
2564 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2567 GetClientRect( hwnd
, &rect
);
2569 descr
->owner
= GetParent( descr
->self
);
2570 descr
->style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2571 descr
->width
= rect
.right
- rect
.left
;
2572 descr
->height
= rect
.bottom
- rect
.top
;
2573 descr
->u
.items
= NULL
;
2574 descr
->items_size
= 0;
2575 descr
->nb_items
= 0;
2576 descr
->top_item
= 0;
2577 descr
->selected_item
= -1;
2578 descr
->focus_item
= 0;
2579 descr
->anchor_item
= -1;
2580 descr
->item_height
= 1;
2581 descr
->page_size
= 1;
2582 descr
->column_width
= 150;
2583 descr
->horz_extent
= 0;
2584 descr
->horz_pos
= 0;
2587 descr
->wheel_remain
= 0;
2588 descr
->caret_on
= !lphc
;
2589 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2590 descr
->in_focus
= FALSE
;
2591 descr
->captured
= FALSE
;
2593 descr
->locale
= GetUserDefaultLCID();
2598 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2599 descr
->owner
= lphc
->self
;
2602 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2604 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2606 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2607 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2608 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2609 if ((descr
->style
& (LBS_OWNERDRAWFIXED
| LBS_HASSTRINGS
| LBS_SORT
)) != LBS_OWNERDRAWFIXED
)
2610 descr
->style
&= ~LBS_NODATA
;
2611 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2613 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2615 descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2617 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2619 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2620 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2624 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2625 mis
.CtlType
= ODT_LISTBOX
;
2630 mis
.itemHeight
= descr
->item_height
;
2631 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2632 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2636 OpenThemeData( descr
->self
, WC_LISTBOXW
);
2638 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2643 /***********************************************************************
2646 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2648 HTHEME theme
= GetWindowTheme( descr
->self
);
2649 CloseThemeData( theme
);
2650 LISTBOX_ResetContent( descr
);
2651 SetWindowLongPtrW( descr
->self
, 0, 0 );
2652 HeapFree( GetProcessHeap(), 0, descr
);
2657 /***********************************************************************
2658 * ListBoxWndProc_common
2660 static LRESULT CALLBACK
LISTBOX_WindowProc( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
2662 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2663 HEADCOMBO
*lphc
= NULL
;
2669 if (!IsWindow(hwnd
)) return 0;
2671 if (msg
== WM_CREATE
)
2673 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2674 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= lpcs
->lpCreateParams
;
2675 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2676 TRACE("creating hwnd %p descr %p\n", hwnd
, (void *)GetWindowLongPtrW( hwnd
, 0 ) );
2679 /* Ignore all other messages before we get a WM_CREATE */
2680 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
2682 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2684 TRACE("[%p]: msg %#x wp %08lx lp %08lx\n", descr
->self
, msg
, wParam
, lParam
);
2688 case LB_RESETCONTENT
:
2689 LISTBOX_ResetContent( descr
);
2690 LISTBOX_UpdateScroll( descr
);
2691 InvalidateRect( descr
->self
, NULL
, TRUE
);
2696 const WCHAR
*textW
= (const WCHAR
*)lParam
;
2697 INT index
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2698 return LISTBOX_InsertString( descr
, index
, textW
);
2701 case LB_INSERTSTRING
:
2702 return LISTBOX_InsertString( descr
, wParam
, (const WCHAR
*)lParam
);
2706 const WCHAR
*textW
= (const WCHAR
*)lParam
;
2707 INT index
= LISTBOX_FindFileStrPos( descr
, textW
);
2708 return LISTBOX_InsertString( descr
, index
, textW
);
2711 case LB_DELETESTRING
:
2712 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2713 return descr
->nb_items
;
2716 SetLastError(ERROR_INVALID_INDEX
);
2720 case LB_GETITEMDATA
:
2721 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2723 SetLastError(ERROR_INVALID_INDEX
);
2726 return get_item_data(descr
, wParam
);
2728 case LB_SETITEMDATA
:
2729 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2731 SetLastError(ERROR_INVALID_INDEX
);
2734 set_item_data(descr
, wParam
, lParam
);
2735 /* undocumented: returns TRUE, not LB_OKAY (0) */
2739 return descr
->nb_items
;
2742 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, TRUE
);
2745 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2747 SetLastError(ERROR_INVALID_INDEX
);
2750 if (!HAS_STRINGS(descr
)) return sizeof(ULONG_PTR
);
2751 return lstrlenW(get_item_string(descr
, wParam
));
2754 if (descr
->nb_items
== 0)
2756 if (!IS_MULTISELECT(descr
))
2757 return descr
->selected_item
;
2758 if (descr
->selected_item
!= -1)
2759 return descr
->selected_item
;
2760 return descr
->focus_item
;
2761 /* otherwise, if the user tries to move the selection with the */
2762 /* arrow keys, we will give the application something to choke on */
2763 case LB_GETTOPINDEX
:
2764 return descr
->top_item
;
2766 case LB_GETITEMHEIGHT
:
2767 return LISTBOX_GetItemHeight( descr
, wParam
);
2769 case LB_SETITEMHEIGHT
:
2770 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2772 case LB_ITEMFROMPOINT
:
2779 /* The hiword of the return value is not a client area
2780 hittest as suggested by MSDN, but rather a hittest on
2781 the returned listbox item. */
2783 if(descr
->nb_items
== 0)
2784 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2786 pt
.x
= (short)LOWORD(lParam
);
2787 pt
.y
= (short)HIWORD(lParam
);
2789 SetRect(&rect
, 0, 0, descr
->width
, descr
->height
);
2791 if(!PtInRect(&rect
, pt
))
2793 pt
.x
= min(pt
.x
, rect
.right
- 1);
2794 pt
.x
= max(pt
.x
, 0);
2795 pt
.y
= min(pt
.y
, rect
.bottom
- 1);
2796 pt
.y
= max(pt
.y
, 0);
2800 index
= LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
);
2804 index
= descr
->nb_items
- 1;
2807 return MAKELONG(index
, hit
? 0 : 1);
2810 case LB_SETCARETINDEX
:
2811 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2812 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2819 case LB_GETCARETINDEX
:
2820 return descr
->focus_item
;
2822 case LB_SETTOPINDEX
:
2823 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2825 case LB_SETCOLUMNWIDTH
:
2826 return LISTBOX_SetColumnWidth( descr
, wParam
);
2828 case LB_GETITEMRECT
:
2829 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2832 return LISTBOX_FindString( descr
, wParam
, (const WCHAR
*)lParam
, FALSE
);
2834 case LB_FINDSTRINGEXACT
:
2835 return LISTBOX_FindString( descr
, wParam
, (const WCHAR
*)lParam
, TRUE
);
2837 case LB_SELECTSTRING
:
2839 const WCHAR
*textW
= (const WCHAR
*)lParam
;
2842 if (HAS_STRINGS(descr
))
2843 TRACE("LB_SELECTSTRING: %s\n", debugstr_w(textW
));
2845 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2846 if (index
!= LB_ERR
)
2848 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2849 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2855 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2857 return is_item_selected(descr
, wParam
);
2860 ret
= LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2861 if (ret
!= LB_ERR
&& wParam
)
2863 descr
->anchor_item
= lParam
;
2865 LISTBOX_SetCaretIndex( descr
, lParam
, TRUE
);
2870 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2871 LISTBOX_SetCaretIndex( descr
, wParam
, TRUE
);
2872 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2873 if (ret
!= LB_ERR
) ret
= descr
->selected_item
;
2876 case LB_GETSELCOUNT
:
2877 return LISTBOX_GetSelCount( descr
);
2879 case LB_GETSELITEMS
:
2880 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2882 case LB_SELITEMRANGE
:
2883 if (LOWORD(lParam
) <= HIWORD(lParam
))
2884 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2885 HIWORD(lParam
), wParam
);
2887 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2888 LOWORD(lParam
), wParam
);
2890 case LB_SELITEMRANGEEX
:
2891 if ((INT
)lParam
>= (INT
)wParam
)
2892 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2894 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
2896 case LB_GETHORIZONTALEXTENT
:
2897 return descr
->horz_extent
;
2899 case LB_SETHORIZONTALEXTENT
:
2900 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
2902 case LB_GETANCHORINDEX
:
2903 return descr
->anchor_item
;
2905 case LB_SETANCHORINDEX
:
2906 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2908 SetLastError(ERROR_INVALID_INDEX
);
2911 descr
->anchor_item
= (INT
)wParam
;
2915 return LISTBOX_Directory( descr
, wParam
, (const WCHAR
*)lParam
, msg
== LB_DIR
);
2918 return descr
->locale
;
2923 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
2925 ret
= descr
->locale
;
2926 descr
->locale
= (LCID
)wParam
;
2930 case LB_INITSTORAGE
:
2931 return LISTBOX_InitStorage( descr
, wParam
);
2934 return LISTBOX_SetCount( descr
, (INT
)wParam
);
2936 case LB_SETTABSTOPS
:
2937 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
);
2940 if (descr
->caret_on
)
2942 descr
->caret_on
= TRUE
;
2943 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2944 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2948 if (!descr
->caret_on
)
2950 descr
->caret_on
= FALSE
;
2951 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2952 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2955 case LB_GETLISTBOXINFO
:
2956 return descr
->page_size
;
2959 return LISTBOX_Destroy( descr
);
2962 InvalidateRect( descr
->self
, NULL
, TRUE
);
2966 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
2970 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2972 case WM_PRINTCLIENT
:
2976 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
2977 ret
= LISTBOX_Paint( descr
, hdc
);
2978 if( !wParam
) EndPaint( descr
->self
, &ps
);
2983 return LISTBOX_NCPaint( descr
, (HRGN
)wParam
);
2986 LISTBOX_UpdateSize( descr
);
2989 return (LRESULT
)descr
->font
;
2991 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
2992 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
2995 descr
->in_focus
= TRUE
;
2996 descr
->caret_on
= TRUE
;
2997 if (descr
->focus_item
!= -1)
2998 LISTBOX_DrawFocusRect( descr
, TRUE
);
2999 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
3002 LISTBOX_HandleLButtonUp( descr
); /* Release capture if we have it */
3003 descr
->in_focus
= FALSE
;
3004 descr
->wheel_remain
= 0;
3005 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
3006 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3007 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
3010 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3012 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3014 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
3015 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
3016 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
3017 case WM_LBUTTONDOWN
:
3019 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3020 (INT16
)LOWORD(lParam
),
3021 (INT16
)HIWORD(lParam
) );
3022 return LISTBOX_HandleLButtonDown( descr
, wParam
,
3023 (INT16
)LOWORD(lParam
),
3024 (INT16
)HIWORD(lParam
) );
3025 case WM_LBUTTONDBLCLK
:
3027 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3028 (INT16
)LOWORD(lParam
),
3029 (INT16
)HIWORD(lParam
) );
3030 if (descr
->style
& LBS_NOTIFY
)
3031 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
3034 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
3036 BOOL captured
= descr
->captured
;
3040 mousePos
.x
= (INT16
)LOWORD(lParam
);
3041 mousePos
.y
= (INT16
)HIWORD(lParam
);
3044 * If we are in a dropdown combobox, we simulate that
3045 * the mouse is captured to show the tracking of the item.
3047 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
3048 descr
->captured
= TRUE
;
3050 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
3052 descr
->captured
= captured
;
3054 else if (GetCapture() == descr
->self
)
3056 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
3057 (INT16
)HIWORD(lParam
) );
3067 * If the mouse button "up" is not in the listbox,
3068 * we make sure there is no selection by re-selecting the
3069 * item that was selected when the listbox was made visible.
3071 mousePos
.x
= (INT16
)LOWORD(lParam
);
3072 mousePos
.y
= (INT16
)HIWORD(lParam
);
3074 GetClientRect(descr
->self
, &clientRect
);
3077 * When the user clicks outside the combobox and the focus
3078 * is lost, the owning combobox will send a fake buttonup with
3079 * 0xFFFFFFF as the mouse location, we must also revert the
3080 * selection to the original selection.
3082 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
3083 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
3085 return LISTBOX_HandleLButtonUp( descr
);
3087 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3089 /* for some reason Windows makes it possible to
3090 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3092 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3093 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3094 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3096 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3100 return LISTBOX_HandleKeyDown( descr
, wParam
);
3102 return LISTBOX_HandleChar( descr
, wParam
);
3105 return LISTBOX_HandleSystemTimer( descr
);
3107 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3110 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3111 wParam
, (LPARAM
)descr
->self
);
3112 TRACE("hbrush = %p\n", hbrush
);
3114 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3117 GetClientRect(descr
->self
, &rect
);
3118 FillRect((HDC
)wParam
, &rect
, hbrush
);
3123 if( lphc
) return 0;
3124 return SendMessageW( descr
->owner
, msg
, wParam
, lParam
);
3127 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3135 case WM_THEMECHANGED
:
3136 theme
= GetWindowTheme( hwnd
);
3137 CloseThemeData( theme
);
3138 OpenThemeData( hwnd
, WC_LISTBOXW
);
3139 InvalidateRect( hwnd
, NULL
, TRUE
);
3143 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3144 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3145 hwnd
, msg
, wParam
, lParam
);
3148 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
3151 void LISTBOX_Register(void)
3155 memset(&wndClass
, 0, sizeof(wndClass
));
3156 wndClass
.style
= CS_PARENTDC
| CS_DBLCLKS
| CS_GLOBALCLASS
;
3157 wndClass
.lpfnWndProc
= LISTBOX_WindowProc
;
3158 wndClass
.cbClsExtra
= 0;
3159 wndClass
.cbWndExtra
= sizeof(LB_DESCR
*);
3160 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
3161 wndClass
.hbrBackground
= NULL
;
3162 wndClass
.lpszClassName
= WC_LISTBOXW
;
3163 RegisterClassW(&wndClass
);
3166 void COMBOLBOX_Register(void)
3170 memset(&wndClass
, 0, sizeof(wndClass
));
3171 wndClass
.style
= CS_SAVEBITS
| CS_DBLCLKS
| CS_DROPSHADOW
| CS_GLOBALCLASS
;
3172 wndClass
.lpfnWndProc
= LISTBOX_WindowProc
;
3173 wndClass
.cbClsExtra
= 0;
3174 wndClass
.cbWndExtra
= sizeof(LB_DESCR
*);
3175 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
3176 wndClass
.hbrBackground
= NULL
;
3177 wndClass
.lpszClassName
= L
"ComboLBox";
3178 RegisterClassW(&wndClass
);