4 * Copyright 1996 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "user_private.h"
32 #include "wine/exception.h"
33 #include "wine/debug.h"
34 #include "wine/heap.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(listbox
);
38 /* Items array granularity (must be power of 2) */
39 #define LB_ARRAY_GRANULARITY 16
41 /* Scrolling timeout in ms */
42 #define LB_SCROLL_TIMEOUT 50
44 /* Listbox system timer id */
47 /* flag listbox changed while setredraw false - internal style */
48 #define LBS_DISPLAYCHANGED 0x80000000
53 LPWSTR str
; /* Item text */
54 BOOL selected
; /* Is item selected? */
55 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
56 ULONG_PTR data
; /* User data */
59 /* Listbox structure */
62 HWND self
; /* Our own window handle */
63 HWND owner
; /* Owner window to send notifications to */
64 UINT style
; /* Window style */
65 INT width
; /* Window width */
66 INT height
; /* Window height */
69 LB_ITEMDATA
*items
; /* Array of items */
70 BYTE
*nodata_items
; /* For multi-selection LBS_NODATA */
72 INT nb_items
; /* Number of items */
73 UINT items_size
; /* Total number of allocated items in the array */
74 INT top_item
; /* Top visible item */
75 INT selected_item
; /* Selected item */
76 INT focus_item
; /* Item that has the focus */
77 INT anchor_item
; /* Anchor item for extended selection */
78 INT item_height
; /* Default item height */
79 INT page_size
; /* Items per listbox page */
80 INT column_width
; /* Column width for multi-column listboxes */
81 INT horz_extent
; /* Horizontal extent */
82 INT horz_pos
; /* Horizontal position */
83 INT nb_tabs
; /* Number of tabs in array */
84 INT
*tabs
; /* Array of tabs */
85 INT avg_char_width
; /* Average width of characters */
86 INT wheel_remain
; /* Left over scroll amount */
87 BOOL caret_on
; /* Is caret on? */
88 BOOL captured
; /* Is mouse captured? */
90 HFONT font
; /* Current font */
91 LCID locale
; /* Current locale for string comparisons */
92 HEADCOMBO
*lphc
; /* ComboLBox */
96 #define IS_OWNERDRAW(descr) \
97 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
99 #define HAS_STRINGS(descr) \
100 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
103 #define IS_MULTISELECT(descr) \
104 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
105 !((descr)->style & LBS_NOSEL))
107 #define SEND_NOTIFICATION(descr,code) \
108 (SendMessageW( (descr)->owner, WM_COMMAND, \
109 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
111 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
113 /* Current timer status */
123 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
125 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
);
128 For listboxes without LBS_NODATA, an array of LB_ITEMDATA is allocated
129 to store the states of each item into descr->u.items.
131 For single-selection LBS_NODATA listboxes, no storage is allocated,
132 and thus descr->u.nodata_items will always be NULL.
134 For multi-selection LBS_NODATA listboxes, one byte per item is stored
135 for the item's selection state into descr->u.nodata_items.
137 static size_t get_sizeof_item( const LB_DESCR
*descr
)
139 return (descr
->style
& LBS_NODATA
) ? sizeof(BYTE
) : sizeof(LB_ITEMDATA
);
142 static BOOL
resize_storage(LB_DESCR
*descr
, UINT items_size
)
146 if (items_size
> descr
->items_size
||
147 items_size
+ LB_ARRAY_GRANULARITY
* 2 < descr
->items_size
)
149 items_size
= (items_size
+ LB_ARRAY_GRANULARITY
- 1) & ~(LB_ARRAY_GRANULARITY
- 1);
150 if ((descr
->style
& (LBS_NODATA
| LBS_MULTIPLESEL
| LBS_EXTENDEDSEL
)) != LBS_NODATA
)
152 items
= heap_realloc(descr
->u
.items
, items_size
* get_sizeof_item(descr
));
155 SEND_NOTIFICATION(descr
, LBN_ERRSPACE
);
158 descr
->u
.items
= items
;
160 descr
->items_size
= items_size
;
163 if ((descr
->style
& LBS_NODATA
) && descr
->u
.nodata_items
&& items_size
> descr
->nb_items
)
165 memset(descr
->u
.nodata_items
+ descr
->nb_items
, 0,
166 (items_size
- descr
->nb_items
) * get_sizeof_item(descr
));
171 static ULONG_PTR
get_item_data( const LB_DESCR
*descr
, UINT index
)
173 return (descr
->style
& LBS_NODATA
) ? 0 : descr
->u
.items
[index
].data
;
176 static void set_item_data( LB_DESCR
*descr
, UINT index
, ULONG_PTR data
)
178 if (!(descr
->style
& LBS_NODATA
)) descr
->u
.items
[index
].data
= data
;
181 static WCHAR
*get_item_string( const LB_DESCR
*descr
, UINT index
)
183 return HAS_STRINGS(descr
) ? descr
->u
.items
[index
].str
: NULL
;
186 static void set_item_string( const LB_DESCR
*descr
, UINT index
, WCHAR
*string
)
188 if (!(descr
->style
& LBS_NODATA
)) descr
->u
.items
[index
].str
= string
;
191 static UINT
get_item_height( const LB_DESCR
*descr
, UINT index
)
193 return (descr
->style
& LBS_NODATA
) ? 0 : descr
->u
.items
[index
].height
;
196 static void set_item_height( LB_DESCR
*descr
, UINT index
, UINT height
)
198 if (!(descr
->style
& LBS_NODATA
)) descr
->u
.items
[index
].height
= height
;
201 static BOOL
is_item_selected( const LB_DESCR
*descr
, UINT index
)
203 if (!(descr
->style
& (LBS_MULTIPLESEL
| LBS_EXTENDEDSEL
)))
204 return index
== descr
->selected_item
;
205 if (descr
->style
& LBS_NODATA
)
206 return descr
->u
.nodata_items
[index
];
208 return descr
->u
.items
[index
].selected
;
211 static void set_item_selected_state(LB_DESCR
*descr
, UINT index
, BOOL state
)
213 if (descr
->style
& (LBS_MULTIPLESEL
| LBS_EXTENDEDSEL
))
215 if (descr
->style
& LBS_NODATA
)
216 descr
->u
.nodata_items
[index
] = state
;
218 descr
->u
.items
[index
].selected
= state
;
222 static void insert_item_data(LB_DESCR
*descr
, UINT index
)
224 size_t size
= get_sizeof_item(descr
);
225 BYTE
*p
= descr
->u
.nodata_items
+ index
* size
;
227 if (!descr
->u
.items
) return;
229 if (index
< descr
->nb_items
)
230 memmove(p
+ size
, p
, (descr
->nb_items
- index
) * size
);
233 static void remove_item_data(LB_DESCR
*descr
, UINT index
)
235 size_t size
= get_sizeof_item(descr
);
236 BYTE
*p
= descr
->u
.nodata_items
+ index
* size
;
238 if (!descr
->u
.items
) return;
240 if (index
< descr
->nb_items
)
241 memmove(p
, p
+ size
, (descr
->nb_items
- index
) * size
);
244 /*********************************************************************
245 * listbox class descriptor
247 const struct builtin_class_descr LISTBOX_builtin_class
=
249 L
"ListBox", /* name */
250 CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
251 WINPROC_LISTBOX
, /* proc */
252 sizeof(LB_DESCR
*), /* extra */
253 IDC_ARROW
, /* cursor */
258 /*********************************************************************
259 * combolbox class descriptor
261 const struct builtin_class_descr COMBOLBOX_builtin_class
=
263 L
"ComboLBox", /* name */
264 CS_DBLCLKS
| CS_SAVEBITS
, /* style */
265 WINPROC_LISTBOX
, /* proc */
266 sizeof(LB_DESCR
*), /* extra */
267 IDC_ARROW
, /* cursor */
272 /***********************************************************************
273 * LISTBOX_GetCurrentPageSize
275 * Return the current page size
277 static INT
LISTBOX_GetCurrentPageSize( const LB_DESCR
*descr
)
280 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
281 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
283 if ((height
+= get_item_height(descr
, i
)) > descr
->height
) break;
285 if (i
== descr
->top_item
) return 1;
286 else return i
- descr
->top_item
;
290 /***********************************************************************
291 * LISTBOX_GetMaxTopIndex
293 * Return the maximum possible index for the top of the listbox.
295 static INT
LISTBOX_GetMaxTopIndex( const LB_DESCR
*descr
)
299 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
301 page
= descr
->height
;
302 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
303 if ((page
-= get_item_height(descr
, max
)) < 0) break;
304 if (max
< descr
->nb_items
- 1) max
++;
306 else if (descr
->style
& LBS_MULTICOLUMN
)
308 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
309 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
310 max
= (max
- page
) * descr
->page_size
;
314 max
= descr
->nb_items
- descr
->page_size
;
316 if (max
< 0) max
= 0;
321 /***********************************************************************
322 * LISTBOX_UpdateScroll
324 * Update the scrollbars. Should be called whenever the content
325 * of the listbox changes.
327 static void LISTBOX_UpdateScroll( LB_DESCR
*descr
)
331 /* Check the listbox scroll bar flags individually before we call
332 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
333 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
334 scroll bar when we do not need one.
335 if (!(descr->style & WS_VSCROLL)) return;
338 /* It is important that we check descr->style, and not wnd->dwStyle,
339 for WS_VSCROLL, as the former is exactly the one passed in
340 argument to CreateWindow.
341 In Windows (and from now on in Wine :) a listbox created
342 with such a style (no WS_SCROLL) does not update
343 the scrollbar with listbox-related data, thus letting
344 the programmer use it for his/her own purposes. */
346 if (descr
->style
& LBS_NOREDRAW
) return;
347 info
.cbSize
= sizeof(info
);
349 if (descr
->style
& LBS_MULTICOLUMN
)
352 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
353 info
.nPos
= descr
->top_item
/ descr
->page_size
;
354 info
.nPage
= descr
->width
/ descr
->column_width
;
355 if (info
.nPage
< 1) info
.nPage
= 1;
356 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
357 if (descr
->style
& LBS_DISABLENOSCROLL
)
358 info
.fMask
|= SIF_DISABLENOSCROLL
;
359 if (descr
->style
& WS_HSCROLL
)
360 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
362 info
.fMask
= SIF_RANGE
;
363 if (descr
->style
& WS_VSCROLL
)
364 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
369 info
.nMax
= descr
->nb_items
- 1;
370 info
.nPos
= descr
->top_item
;
371 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
372 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
373 if (descr
->style
& LBS_DISABLENOSCROLL
)
374 info
.fMask
|= SIF_DISABLENOSCROLL
;
375 if (descr
->style
& WS_VSCROLL
)
376 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
378 if ((descr
->style
& WS_HSCROLL
) && descr
->horz_extent
)
380 info
.nPos
= descr
->horz_pos
;
381 info
.nPage
= descr
->width
;
382 info
.fMask
= SIF_POS
| SIF_PAGE
;
383 if (descr
->style
& LBS_DISABLENOSCROLL
)
384 info
.fMask
|= SIF_DISABLENOSCROLL
;
385 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
389 if (descr
->style
& LBS_DISABLENOSCROLL
)
393 info
.fMask
= SIF_RANGE
| SIF_DISABLENOSCROLL
;
394 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
398 ShowScrollBar( descr
->self
, SB_HORZ
, FALSE
);
405 /***********************************************************************
408 * Set the top item of the listbox, scrolling up or down if necessary.
410 static LRESULT
LISTBOX_SetTopItem( LB_DESCR
*descr
, INT index
, BOOL scroll
)
412 INT max
= LISTBOX_GetMaxTopIndex( descr
);
414 TRACE("setting top item %d, scroll %d\n", index
, scroll
);
416 if (index
> max
) index
= max
;
417 if (index
< 0) index
= 0;
418 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
419 if (descr
->top_item
== index
) return LB_OKAY
;
423 if (descr
->style
& LBS_MULTICOLUMN
)
424 dx
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
425 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
428 if (index
> descr
->top_item
)
430 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
431 dy
-= get_item_height(descr
, i
);
435 for (i
= index
; i
< descr
->top_item
; i
++)
436 dy
+= get_item_height(descr
, i
);
440 dy
= (descr
->top_item
- index
) * descr
->item_height
;
442 ScrollWindowEx( descr
->self
, dx
, dy
, NULL
, NULL
, 0, NULL
,
443 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
446 InvalidateRect( descr
->self
, NULL
, TRUE
);
447 descr
->top_item
= index
;
448 LISTBOX_UpdateScroll( descr
);
453 /***********************************************************************
456 * Update the page size. Should be called when the size of
457 * the client area or the item height changes.
459 static void LISTBOX_UpdatePage( LB_DESCR
*descr
)
463 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
465 if (page_size
== descr
->page_size
) return;
466 descr
->page_size
= page_size
;
467 if (descr
->style
& LBS_MULTICOLUMN
)
468 InvalidateRect( descr
->self
, NULL
, TRUE
);
469 LISTBOX_SetTopItem( descr
, descr
->top_item
, FALSE
);
473 /***********************************************************************
476 * Update the size of the listbox. Should be called when the size of
477 * the client area changes.
479 static void LISTBOX_UpdateSize( LB_DESCR
*descr
)
483 GetClientRect( descr
->self
, &rect
);
484 descr
->width
= rect
.right
- rect
.left
;
485 descr
->height
= rect
.bottom
- rect
.top
;
486 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
491 GetWindowRect( descr
->self
, &rect
);
492 if(descr
->item_height
!= 0)
493 remaining
= descr
->height
% descr
->item_height
;
496 if ((descr
->height
> descr
->item_height
) && remaining
)
498 TRACE("[%p]: changing height %d -> %d\n",
499 descr
->self
, descr
->height
, descr
->height
- remaining
);
500 SetWindowPos( descr
->self
, 0, 0, 0, rect
.right
- rect
.left
,
501 rect
.bottom
- rect
.top
- remaining
,
502 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
506 TRACE("[%p]: new size = %d,%d\n", descr
->self
, descr
->width
, descr
->height
);
507 LISTBOX_UpdatePage( descr
);
508 LISTBOX_UpdateScroll( descr
);
510 /* Invalidate the focused item so it will be repainted correctly */
511 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
513 InvalidateRect( descr
->self
, &rect
, FALSE
);
518 /***********************************************************************
519 * LISTBOX_GetItemRect
521 * Get the rectangle enclosing an item, in listbox client coordinates.
522 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
524 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
)
526 /* Index <= 0 is legal even on empty listboxes */
527 if (index
&& (index
>= descr
->nb_items
))
530 SetLastError(ERROR_INVALID_INDEX
);
533 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
534 if (descr
->style
& LBS_MULTICOLUMN
)
536 INT col
= (index
/ descr
->page_size
) -
537 (descr
->top_item
/ descr
->page_size
);
538 rect
->left
+= col
* descr
->column_width
;
539 rect
->right
= rect
->left
+ descr
->column_width
;
540 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
541 rect
->bottom
= rect
->top
+ descr
->item_height
;
543 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
546 rect
->right
+= descr
->horz_pos
;
547 if ((index
>= 0) && (index
< descr
->nb_items
))
549 if (index
< descr
->top_item
)
551 for (i
= descr
->top_item
-1; i
>= index
; i
--)
552 rect
->top
-= get_item_height(descr
, i
);
556 for (i
= descr
->top_item
; i
< index
; i
++)
557 rect
->top
+= get_item_height(descr
, i
);
559 rect
->bottom
= rect
->top
+ get_item_height(descr
, index
);
565 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
566 rect
->bottom
= rect
->top
+ descr
->item_height
;
567 rect
->right
+= descr
->horz_pos
;
570 TRACE("item %d, rect %s\n", index
, wine_dbgstr_rect(rect
));
572 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
573 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
577 /***********************************************************************
578 * LISTBOX_GetItemFromPoint
580 * Return the item nearest from point (x,y) (in client coordinates).
582 static INT
LISTBOX_GetItemFromPoint( const LB_DESCR
*descr
, INT x
, INT y
)
584 INT index
= descr
->top_item
;
586 if (!descr
->nb_items
) return -1; /* No items */
587 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
592 while (index
< descr
->nb_items
)
594 if ((pos
+= get_item_height(descr
, index
)) > y
) break;
603 if ((pos
-= get_item_height(descr
, index
)) <= y
) break;
607 else if (descr
->style
& LBS_MULTICOLUMN
)
609 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
610 if (y
>= 0) index
+= y
/ descr
->item_height
;
611 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
612 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
616 index
+= (y
/ descr
->item_height
);
618 if (index
< 0) return 0;
619 if (index
>= descr
->nb_items
) return -1;
624 /***********************************************************************
629 static void LISTBOX_PaintItem( LB_DESCR
*descr
, HDC hdc
, const RECT
*rect
,
630 INT index
, UINT action
, BOOL ignoreFocus
)
632 BOOL selected
= FALSE
, focused
;
633 WCHAR
*item_str
= NULL
;
635 if (index
< descr
->nb_items
)
637 item_str
= get_item_string(descr
, index
);
638 selected
= is_item_selected(descr
, index
);
641 focused
= !ignoreFocus
&& descr
->focus_item
== index
&& descr
->caret_on
&& descr
->in_focus
;
643 if (IS_OWNERDRAW(descr
))
649 if (index
>= descr
->nb_items
)
651 if (action
== ODA_FOCUS
)
652 DrawFocusRect( hdc
, rect
);
654 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
658 /* some programs mess with the clipping region when
659 drawing the item, *and* restore the previous region
660 after they are done, so a region has better to exist
661 else everything ends clipped */
662 GetClientRect(descr
->self
, &r
);
663 hrgn
= set_control_clipping( hdc
, &r
);
665 dis
.CtlType
= ODT_LISTBOX
;
666 dis
.CtlID
= GetWindowLongPtrW( descr
->self
, GWLP_ID
);
667 dis
.hwndItem
= descr
->self
;
668 dis
.itemAction
= action
;
673 dis
.itemState
|= ODS_SELECTED
;
675 dis
.itemState
|= ODS_FOCUS
;
676 if (!IsWindowEnabled(descr
->self
)) dis
.itemState
|= ODS_DISABLED
;
677 dis
.itemData
= get_item_data(descr
, index
);
679 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
680 descr
->self
, index
, debugstr_w(item_str
), action
,
681 dis
.itemState
, wine_dbgstr_rect(rect
) );
682 SendMessageW(descr
->owner
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
683 SelectClipRgn( hdc
, hrgn
);
684 if (hrgn
) DeleteObject( hrgn
);
688 COLORREF oldText
= 0, oldBk
= 0;
690 if (action
== ODA_FOCUS
)
692 DrawFocusRect( hdc
, rect
);
697 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
698 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
701 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
702 descr
->self
, index
, debugstr_w(item_str
), action
,
703 wine_dbgstr_rect(rect
) );
705 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
706 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
707 else if (!(descr
->style
& LBS_USETABSTOPS
))
708 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
709 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item_str
,
710 lstrlenW(item_str
), NULL
);
713 /* Output empty string to paint background in the full width. */
714 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
715 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
716 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
717 item_str
, lstrlenW(item_str
),
718 descr
->nb_tabs
, descr
->tabs
, 0);
722 SetBkColor( hdc
, oldBk
);
723 SetTextColor( hdc
, oldText
);
726 DrawFocusRect( hdc
, rect
);
731 /***********************************************************************
734 * Change the redraw flag.
736 static void LISTBOX_SetRedraw( LB_DESCR
*descr
, BOOL on
)
740 if (!(descr
->style
& LBS_NOREDRAW
)) return;
741 descr
->style
&= ~LBS_NOREDRAW
;
742 if (descr
->style
& LBS_DISPLAYCHANGED
)
743 { /* page was changed while setredraw false, refresh automatically */
744 InvalidateRect(descr
->self
, NULL
, TRUE
);
745 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
746 { /* reset top of page if less than number of items/page */
747 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
748 if (descr
->top_item
< 0) descr
->top_item
= 0;
750 descr
->style
&= ~LBS_DISPLAYCHANGED
;
752 LISTBOX_UpdateScroll( descr
);
754 else descr
->style
|= LBS_NOREDRAW
;
758 /***********************************************************************
759 * LISTBOX_RepaintItem
761 * Repaint a single item synchronously.
763 static void LISTBOX_RepaintItem( LB_DESCR
*descr
, INT index
, UINT action
)
768 HBRUSH hbrush
, oldBrush
= 0;
770 /* Do not repaint the item if the item is not visible */
771 if (!IsWindowVisible(descr
->self
)) return;
772 if (descr
->style
& LBS_NOREDRAW
)
774 descr
->style
|= LBS_DISPLAYCHANGED
;
777 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
778 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
779 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
780 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
781 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
782 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
783 if (!IsWindowEnabled(descr
->self
))
784 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
785 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
786 LISTBOX_PaintItem( descr
, hdc
, &rect
, index
, action
, TRUE
);
787 if (oldFont
) SelectObject( hdc
, oldFont
);
788 if (oldBrush
) SelectObject( hdc
, oldBrush
);
789 ReleaseDC( descr
->self
, hdc
);
793 /***********************************************************************
794 * LISTBOX_DrawFocusRect
796 static void LISTBOX_DrawFocusRect( LB_DESCR
*descr
, BOOL on
)
802 /* Do not repaint the item if the item is not visible */
803 if (!IsWindowVisible(descr
->self
)) return;
805 if (descr
->focus_item
== -1) return;
806 if (!descr
->caret_on
|| !descr
->in_focus
) return;
808 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) != 1) return;
809 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
810 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
811 if (!IsWindowEnabled(descr
->self
))
812 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
813 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
814 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, !on
);
815 if (oldFont
) SelectObject( hdc
, oldFont
);
816 ReleaseDC( descr
->self
, hdc
);
820 /***********************************************************************
821 * LISTBOX_InitStorage
823 static LRESULT
LISTBOX_InitStorage( LB_DESCR
*descr
, INT nb_items
)
825 UINT new_size
= descr
->nb_items
+ nb_items
;
827 if (new_size
> descr
->items_size
&& !resize_storage(descr
, new_size
))
829 return descr
->items_size
;
833 /***********************************************************************
834 * LISTBOX_SetTabStops
836 static BOOL
LISTBOX_SetTabStops( LB_DESCR
*descr
, INT count
, LPINT tabs
)
840 if (!(descr
->style
& LBS_USETABSTOPS
))
842 SetLastError(ERROR_LB_WITHOUT_TABSTOPS
);
846 HeapFree( GetProcessHeap(), 0, descr
->tabs
);
847 if (!(descr
->nb_tabs
= count
))
852 if (!(descr
->tabs
= HeapAlloc( GetProcessHeap(), 0,
853 descr
->nb_tabs
* sizeof(INT
) )))
855 memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
857 /* convert into "dialog units"*/
858 for (i
= 0; i
< descr
->nb_tabs
; i
++)
859 descr
->tabs
[i
] = MulDiv(descr
->tabs
[i
], descr
->avg_char_width
, 4);
865 /***********************************************************************
868 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPWSTR buffer
, BOOL unicode
)
872 if ((index
< 0) || (index
>= descr
->nb_items
))
874 SetLastError(ERROR_INVALID_INDEX
);
878 if (HAS_STRINGS(descr
))
880 WCHAR
*str
= get_item_string(descr
, index
);
887 return WideCharToMultiByte( CP_ACP
, 0, str
, len
, NULL
, 0, NULL
, NULL
);
890 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(str
));
892 __TRY
/* hide a Delphi bug that passes a read-only buffer */
896 lstrcpyW(buffer
, str
);
897 len
= lstrlenW(buffer
);
901 len
= WideCharToMultiByte(CP_ACP
, 0, str
, -1, (LPSTR
)buffer
,
902 0x7FFFFFFF, NULL
, NULL
) - 1;
907 WARN( "got an invalid buffer (Delphi bug?)\n" );
908 SetLastError( ERROR_INVALID_PARAMETER
);
915 *((ULONG_PTR
*)buffer
) = get_item_data(descr
, index
);
916 len
= sizeof(ULONG_PTR
);
921 static inline INT
LISTBOX_lstrcmpiW( LCID lcid
, LPCWSTR str1
, LPCWSTR str2
)
923 INT ret
= CompareStringW( lcid
, NORM_IGNORECASE
, str1
, -1, str2
, -1 );
924 if (ret
== CSTR_LESS_THAN
)
926 if (ret
== CSTR_EQUAL
)
928 if (ret
== CSTR_GREATER_THAN
)
933 /***********************************************************************
934 * LISTBOX_FindStringPos
936 * Find the nearest string located before a given string in sort order.
937 * If 'exact' is TRUE, return an error if we don't get an exact match.
939 static INT
LISTBOX_FindStringPos( LB_DESCR
*descr
, LPCWSTR str
, BOOL exact
)
941 INT index
, min
, max
, res
;
943 if (!descr
->nb_items
|| !(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
946 max
= descr
->nb_items
- 1;
949 index
= (min
+ max
) / 2;
950 if (HAS_STRINGS(descr
))
951 res
= LISTBOX_lstrcmpiW( descr
->locale
, get_item_string(descr
, index
), str
);
954 COMPAREITEMSTRUCT cis
;
955 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
957 cis
.CtlType
= ODT_LISTBOX
;
959 cis
.hwndItem
= descr
->self
;
960 /* note that some application (MetaStock) expects the second item
961 * to be in the listbox */
963 cis
.itemData1
= get_item_data(descr
, index
);
965 cis
.itemData2
= (ULONG_PTR
)str
;
966 cis
.dwLocaleId
= descr
->locale
;
967 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
969 if (!res
) return index
;
970 if (res
> 0) max
= index
- 1;
971 else min
= index
+ 1;
973 return exact
? -1 : min
;
977 /***********************************************************************
978 * LISTBOX_FindFileStrPos
980 * Find the nearest string located before a given string in directory
981 * sort order (i.e. first files, then directories, then drives).
983 static INT
LISTBOX_FindFileStrPos( LB_DESCR
*descr
, LPCWSTR str
)
987 if (!HAS_STRINGS(descr
))
988 return LISTBOX_FindStringPos( descr
, str
, FALSE
);
990 max
= descr
->nb_items
;
993 INT index
= (min
+ max
) / 2;
994 LPCWSTR p
= get_item_string(descr
, index
);
995 if (*p
== '[') /* drive or directory */
997 if (*str
!= '[') res
= -1;
998 else if (p
[1] == '-') /* drive */
1000 if (str
[1] == '-') res
= str
[2] - p
[2];
1003 else /* directory */
1005 if (str
[1] == '-') res
= 1;
1006 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
1011 if (*str
== '[') res
= 1;
1012 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
1014 if (!res
) return index
;
1015 if (res
< 0) max
= index
;
1016 else min
= index
+ 1;
1022 /***********************************************************************
1023 * LISTBOX_FindString
1025 * Find the item beginning with a given string.
1027 static INT
LISTBOX_FindString( LB_DESCR
*descr
, INT start
, LPCWSTR str
, BOOL exact
)
1031 if (descr
->style
& LBS_NODATA
)
1033 SetLastError(ERROR_INVALID_PARAMETER
);
1038 if (start
>= descr
->nb_items
) start
= 0;
1039 if (HAS_STRINGS(descr
))
1041 if (!str
|| ! str
[0] ) return LB_ERR
;
1044 for (i
= 0, index
= start
; i
< descr
->nb_items
; i
++, index
++)
1046 if (index
== descr
->nb_items
) index
= 0;
1047 if (!LISTBOX_lstrcmpiW(descr
->locale
, str
, get_item_string(descr
, index
)))
1053 /* Special case for drives and directories: ignore prefix */
1054 INT len
= lstrlenW(str
);
1057 for (i
= 0, index
= start
; i
< descr
->nb_items
; i
++, index
++)
1059 if (index
== descr
->nb_items
) index
= 0;
1060 item_str
= get_item_string(descr
, index
);
1062 if (!wcsnicmp(str
, item_str
, len
)) return index
;
1063 if (item_str
[0] == '[')
1065 if (!wcsnicmp(str
, item_str
+ 1, len
)) return index
;
1066 if (item_str
[1] == '-' && !wcsnicmp(str
, item_str
+ 2, len
)) return index
;
1073 if (exact
&& (descr
->style
& LBS_SORT
))
1074 /* If sorted, use a WM_COMPAREITEM binary search */
1075 return LISTBOX_FindStringPos( descr
, str
, TRUE
);
1077 /* Otherwise use a linear search */
1078 for (i
= 0, index
= start
; i
< descr
->nb_items
; i
++, index
++)
1080 if (index
== descr
->nb_items
) index
= 0;
1081 if (get_item_data(descr
, index
) == (ULONG_PTR
)str
) return index
;
1088 /***********************************************************************
1089 * LISTBOX_GetSelCount
1091 static LRESULT
LISTBOX_GetSelCount( const LB_DESCR
*descr
)
1095 if (!(descr
->style
& LBS_MULTIPLESEL
) ||
1096 (descr
->style
& LBS_NOSEL
))
1098 for (i
= count
= 0; i
< descr
->nb_items
; i
++)
1099 if (is_item_selected(descr
, i
)) count
++;
1104 /***********************************************************************
1105 * LISTBOX_GetSelItems
1107 static LRESULT
LISTBOX_GetSelItems( const LB_DESCR
*descr
, INT max
, LPINT array
)
1111 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1112 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++)
1113 if (is_item_selected(descr
, i
)) array
[count
++] = i
;
1118 /***********************************************************************
1121 static LRESULT
LISTBOX_Paint( LB_DESCR
*descr
, HDC hdc
)
1123 INT i
, col_pos
= descr
->page_size
- 1;
1125 RECT focusRect
= {-1, -1, -1, -1};
1127 HBRUSH hbrush
, oldBrush
= 0;
1129 if (descr
->style
& LBS_NOREDRAW
) return 0;
1131 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1132 if (descr
->style
& LBS_MULTICOLUMN
)
1133 rect
.right
= rect
.left
+ descr
->column_width
;
1134 else if (descr
->horz_pos
)
1136 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1137 rect
.right
+= descr
->horz_pos
;
1140 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1141 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
1142 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
1143 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1144 if (!IsWindowEnabled(descr
->self
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1146 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1149 /* Special case for empty listbox: paint focus rect */
1150 rect
.bottom
= rect
.top
+ descr
->item_height
;
1151 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1152 &rect
, NULL
, 0, NULL
);
1153 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1154 rect
.top
= rect
.bottom
;
1157 /* Paint all the item, regarding the selection
1158 Focus state will be painted after */
1160 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1162 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1163 rect
.bottom
= rect
.top
+ descr
->item_height
;
1165 rect
.bottom
= rect
.top
+ get_item_height(descr
, i
);
1167 /* keep the focus rect, to paint the focus item after */
1168 if (i
== descr
->focus_item
)
1171 LISTBOX_PaintItem( descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1172 rect
.top
= rect
.bottom
;
1174 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1176 if (!IS_OWNERDRAW(descr
))
1178 /* Clear the bottom of the column */
1179 if (rect
.top
< descr
->height
)
1181 rect
.bottom
= descr
->height
;
1182 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1183 &rect
, NULL
, 0, NULL
);
1187 /* Go to the next column */
1188 rect
.left
+= descr
->column_width
;
1189 rect
.right
+= descr
->column_width
;
1191 col_pos
= descr
->page_size
- 1;
1192 if (rect
.left
>= descr
->width
) break;
1197 if (rect
.top
>= descr
->height
) break;
1201 /* Paint the focus item now */
1202 if (focusRect
.top
!= focusRect
.bottom
&&
1203 descr
->caret_on
&& descr
->in_focus
)
1204 LISTBOX_PaintItem( descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1206 if (!IS_OWNERDRAW(descr
))
1208 /* Clear the remainder of the client area */
1209 if (rect
.top
< descr
->height
)
1211 rect
.bottom
= descr
->height
;
1212 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1213 &rect
, NULL
, 0, NULL
);
1215 if (rect
.right
< descr
->width
)
1217 rect
.left
= rect
.right
;
1218 rect
.right
= descr
->width
;
1220 rect
.bottom
= descr
->height
;
1221 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1222 &rect
, NULL
, 0, NULL
);
1225 if (oldFont
) SelectObject( hdc
, oldFont
);
1226 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1231 /***********************************************************************
1232 * LISTBOX_InvalidateItems
1234 * Invalidate all items from a given item. If the specified item is not
1235 * visible, nothing happens.
1237 static void LISTBOX_InvalidateItems( LB_DESCR
*descr
, INT index
)
1241 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1243 if (descr
->style
& LBS_NOREDRAW
)
1245 descr
->style
|= LBS_DISPLAYCHANGED
;
1248 rect
.bottom
= descr
->height
;
1249 InvalidateRect( descr
->self
, &rect
, TRUE
);
1250 if (descr
->style
& LBS_MULTICOLUMN
)
1252 /* Repaint the other columns */
1253 rect
.left
= rect
.right
;
1254 rect
.right
= descr
->width
;
1256 InvalidateRect( descr
->self
, &rect
, TRUE
);
1261 static void LISTBOX_InvalidateItemRect( LB_DESCR
*descr
, INT index
)
1265 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1266 InvalidateRect( descr
->self
, &rect
, TRUE
);
1269 /***********************************************************************
1270 * LISTBOX_GetItemHeight
1272 static LRESULT
LISTBOX_GetItemHeight( const LB_DESCR
*descr
, INT index
)
1274 if (descr
->style
& LBS_OWNERDRAWVARIABLE
&& descr
->nb_items
> 0)
1276 if ((index
< 0) || (index
>= descr
->nb_items
))
1278 SetLastError(ERROR_INVALID_INDEX
);
1281 return get_item_height(descr
, index
);
1283 else return descr
->item_height
;
1287 /***********************************************************************
1288 * LISTBOX_SetItemHeight
1290 static LRESULT
LISTBOX_SetItemHeight( LB_DESCR
*descr
, INT index
, INT height
, BOOL repaint
)
1292 if (height
> MAXBYTE
)
1295 if (!height
) height
= 1;
1297 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1299 if ((index
< 0) || (index
>= descr
->nb_items
))
1301 SetLastError(ERROR_INVALID_INDEX
);
1304 TRACE("[%p]: item %d height = %d\n", descr
->self
, index
, height
);
1305 set_item_height(descr
, index
, height
);
1306 LISTBOX_UpdateScroll( descr
);
1308 LISTBOX_InvalidateItems( descr
, index
);
1310 else if (height
!= descr
->item_height
)
1312 TRACE("[%p]: new height = %d\n", descr
->self
, height
);
1313 descr
->item_height
= height
;
1314 LISTBOX_UpdatePage( descr
);
1315 LISTBOX_UpdateScroll( descr
);
1317 InvalidateRect( descr
->self
, 0, TRUE
);
1323 /***********************************************************************
1324 * LISTBOX_SetHorizontalPos
1326 static void LISTBOX_SetHorizontalPos( LB_DESCR
*descr
, INT pos
)
1330 if (pos
> descr
->horz_extent
- descr
->width
)
1331 pos
= descr
->horz_extent
- descr
->width
;
1332 if (pos
< 0) pos
= 0;
1333 if (!(diff
= descr
->horz_pos
- pos
)) return;
1334 TRACE("[%p]: new horz pos = %d\n", descr
->self
, pos
);
1335 descr
->horz_pos
= pos
;
1336 LISTBOX_UpdateScroll( descr
);
1337 if (abs(diff
) < descr
->width
)
1340 /* Invalidate the focused item so it will be repainted correctly */
1341 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1342 InvalidateRect( descr
->self
, &rect
, TRUE
);
1343 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
1344 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1347 InvalidateRect( descr
->self
, NULL
, TRUE
);
1351 /***********************************************************************
1352 * LISTBOX_SetHorizontalExtent
1354 static LRESULT
LISTBOX_SetHorizontalExtent( LB_DESCR
*descr
, INT extent
)
1356 if (descr
->style
& LBS_MULTICOLUMN
)
1358 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1359 TRACE("[%p]: new horz extent = %d\n", descr
->self
, extent
);
1360 descr
->horz_extent
= extent
;
1361 if (descr
->style
& WS_HSCROLL
) {
1363 info
.cbSize
= sizeof(info
);
1365 info
.nMax
= descr
->horz_extent
? descr
->horz_extent
- 1 : 0;
1366 info
.fMask
= SIF_RANGE
;
1367 if (descr
->style
& LBS_DISABLENOSCROLL
)
1368 info
.fMask
|= SIF_DISABLENOSCROLL
;
1369 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
1371 if (descr
->horz_pos
> extent
- descr
->width
)
1372 LISTBOX_SetHorizontalPos( descr
, extent
- descr
->width
);
1377 /***********************************************************************
1378 * LISTBOX_SetColumnWidth
1380 static LRESULT
LISTBOX_SetColumnWidth( LB_DESCR
*descr
, INT column_width
)
1384 TRACE("[%p]: new column width = %d\n", descr
->self
, column_width
);
1386 GetClientRect(descr
->self
, &rect
);
1387 descr
->width
= rect
.right
- rect
.left
;
1388 descr
->height
= rect
.bottom
- rect
.top
;
1389 descr
->column_width
= column_width
;
1391 LISTBOX_UpdatePage(descr
);
1392 LISTBOX_UpdateScroll(descr
);
1397 /***********************************************************************
1400 * Returns the item height.
1402 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1406 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1411 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1413 ERR("unable to get DC.\n" );
1416 if (font
) oldFont
= SelectObject( hdc
, font
);
1417 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1418 if (oldFont
) SelectObject( hdc
, oldFont
);
1419 ReleaseDC( descr
->self
, hdc
);
1421 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1422 if (!IS_OWNERDRAW(descr
))
1423 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1428 /***********************************************************************
1429 * LISTBOX_MakeItemVisible
1431 * Make sure that a given item is partially or fully visible.
1433 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1437 TRACE("current top item %d, index %d, fully %d\n", descr
->top_item
, index
, fully
);
1439 if (index
<= descr
->top_item
) top
= index
;
1440 else if (descr
->style
& LBS_MULTICOLUMN
)
1442 INT cols
= descr
->width
;
1443 if (!fully
) cols
+= descr
->column_width
- 1;
1444 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1446 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1447 top
= index
- descr
->page_size
* (cols
- 1);
1449 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1451 INT height
= fully
? get_item_height(descr
, index
) : 1;
1452 for (top
= index
; top
> descr
->top_item
; top
--)
1453 if ((height
+= get_item_height(descr
, top
- 1)) > descr
->height
) break;
1457 if (index
< descr
->top_item
+ descr
->page_size
) return;
1458 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1459 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1460 top
= index
- descr
->page_size
+ 1;
1462 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1465 /***********************************************************************
1466 * LISTBOX_SetCaretIndex
1469 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1472 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1474 BOOL focus_changed
= descr
->focus_item
!= index
;
1476 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1478 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1479 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1483 LISTBOX_DrawFocusRect( descr
, FALSE
);
1484 descr
->focus_item
= index
;
1487 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1490 LISTBOX_DrawFocusRect( descr
, TRUE
);
1496 /***********************************************************************
1497 * LISTBOX_SelectItemRange
1499 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1501 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1506 /* A few sanity checks */
1508 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1509 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1511 if (!descr
->nb_items
) return LB_OKAY
;
1513 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1514 if (first
< 0) first
= 0;
1515 if (last
< first
) return LB_OKAY
;
1517 if (on
) /* Turn selection on */
1519 for (i
= first
; i
<= last
; i
++)
1521 if (is_item_selected(descr
, i
)) continue;
1522 set_item_selected_state(descr
, i
, TRUE
);
1523 LISTBOX_InvalidateItemRect(descr
, i
);
1526 else /* Turn selection off */
1528 for (i
= first
; i
<= last
; i
++)
1530 if (!is_item_selected(descr
, i
)) continue;
1531 set_item_selected_state(descr
, i
, FALSE
);
1532 LISTBOX_InvalidateItemRect(descr
, i
);
1538 /***********************************************************************
1539 * LISTBOX_SetSelection
1541 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1542 BOOL on
, BOOL send_notify
)
1544 TRACE( "cur_sel=%d index=%d notify=%s\n",
1545 descr
->selected_item
, index
, send_notify
? "YES" : "NO" );
1547 if (descr
->style
& LBS_NOSEL
)
1549 descr
->selected_item
= index
;
1552 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1553 if (descr
->style
& LBS_MULTIPLESEL
)
1555 if (index
== -1) /* Select all items */
1556 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1557 else /* Only one item */
1558 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1562 INT oldsel
= descr
->selected_item
;
1563 if (index
== oldsel
) return LB_OKAY
;
1564 if (oldsel
!= -1) set_item_selected_state(descr
, oldsel
, FALSE
);
1565 if (index
!= -1) set_item_selected_state(descr
, index
, TRUE
);
1566 descr
->selected_item
= index
;
1567 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1568 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1569 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1570 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1572 if( descr
->lphc
) /* set selection change flag for parent combo */
1573 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1579 /***********************************************************************
1582 * Change the caret position and extend the selection to the new caret.
1584 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1586 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1588 if ((index
< 0) || (index
>= descr
->nb_items
))
1591 /* Important, repaint needs to be done in this order if
1592 you want to mimic Windows behavior:
1593 1. Remove the focus and paint the item
1594 2. Remove the selection and paint the item(s)
1595 3. Set the selection and repaint the item(s)
1596 4. Set the focus to 'index' and repaint the item */
1598 /* 1. remove the focus and repaint the item */
1599 LISTBOX_DrawFocusRect( descr
, FALSE
);
1601 /* 2. then turn off the previous selection */
1602 /* 3. repaint the new selected item */
1603 if (descr
->style
& LBS_EXTENDEDSEL
)
1605 if (descr
->anchor_item
!= -1)
1607 INT first
= min( index
, descr
->anchor_item
);
1608 INT last
= max( index
, descr
->anchor_item
);
1610 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1611 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1612 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1615 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1617 /* Set selection to new caret item */
1618 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1621 /* 4. repaint the new item with the focus */
1622 descr
->focus_item
= index
;
1623 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1624 LISTBOX_DrawFocusRect( descr
, TRUE
);
1628 /***********************************************************************
1629 * LISTBOX_InsertItem
1631 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1632 LPWSTR str
, ULONG_PTR data
)
1634 INT oldfocus
= descr
->focus_item
;
1636 if (index
== -1) index
= descr
->nb_items
;
1637 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1638 if (!resize_storage(descr
, descr
->nb_items
+ 1)) return LB_ERR
;
1640 insert_item_data(descr
, index
);
1642 set_item_string(descr
, index
, str
);
1643 set_item_data(descr
, index
, HAS_STRINGS(descr
) ? 0 : data
);
1644 set_item_height(descr
, index
, 0);
1645 set_item_selected_state(descr
, index
, FALSE
);
1647 /* Get item height */
1649 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1651 MEASUREITEMSTRUCT mis
;
1652 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1654 mis
.CtlType
= ODT_LISTBOX
;
1657 mis
.itemData
= str
? (ULONG_PTR
)str
: data
;
1658 mis
.itemHeight
= descr
->item_height
;
1659 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1660 set_item_height(descr
, index
, mis
.itemHeight
? mis
.itemHeight
: 1);
1661 TRACE("[%p]: measure item %d (%s) = %d\n",
1662 descr
->self
, index
, str
? debugstr_w(str
) : "", get_item_height(descr
, index
));
1665 /* Repaint the items */
1667 LISTBOX_UpdateScroll( descr
);
1668 LISTBOX_InvalidateItems( descr
, index
);
1670 /* Move selection and focused item */
1671 /* If listbox was empty, set focus to the first item */
1672 if (descr
->nb_items
== 1)
1673 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1674 /* single select don't change selection index in win31 */
1675 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1677 descr
->selected_item
++;
1678 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1682 if (index
<= descr
->selected_item
)
1684 descr
->selected_item
++;
1685 descr
->focus_item
= oldfocus
; /* focus not changed */
1692 /***********************************************************************
1693 * LISTBOX_InsertString
1695 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
, LPCWSTR str
)
1697 LPWSTR new_str
= NULL
;
1700 if (HAS_STRINGS(descr
))
1702 if (!str
) str
= L
"";
1703 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (lstrlenW(str
) + 1) * sizeof(WCHAR
) )))
1705 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1708 lstrcpyW(new_str
, str
);
1711 if (index
== -1) index
= descr
->nb_items
;
1712 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, (ULONG_PTR
)str
)) != 0)
1714 HeapFree( GetProcessHeap(), 0, new_str
);
1718 TRACE("[%p]: added item %d %s\n",
1719 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1724 /***********************************************************************
1725 * LISTBOX_DeleteItem
1727 * Delete the content of an item. 'index' must be a valid index.
1729 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1731 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1732 * while Win95 sends it for all items with user data.
1733 * It's probably better to send it too often than not
1734 * often enough, so this is what we do here.
1736 if (IS_OWNERDRAW(descr
) || get_item_data(descr
, index
))
1738 DELETEITEMSTRUCT dis
;
1739 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1741 dis
.CtlType
= ODT_LISTBOX
;
1744 dis
.hwndItem
= descr
->self
;
1745 dis
.itemData
= get_item_data(descr
, index
);
1746 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1748 HeapFree( GetProcessHeap(), 0, get_item_string(descr
, index
) );
1752 /***********************************************************************
1753 * LISTBOX_RemoveItem
1755 * Remove an item from the listbox and delete its content.
1757 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1759 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1761 /* We need to invalidate the original rect instead of the updated one. */
1762 LISTBOX_InvalidateItems( descr
, index
);
1764 if (descr
->nb_items
== 1)
1766 SendMessageW(descr
->self
, LB_RESETCONTENT
, 0, 0);
1770 LISTBOX_DeleteItem( descr
, index
);
1771 remove_item_data(descr
, index
);
1773 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1774 resize_storage(descr
, descr
->nb_items
);
1776 /* Repaint the items */
1778 LISTBOX_UpdateScroll( descr
);
1779 /* if we removed the scrollbar, reset the top of the list
1780 (correct for owner-drawn ???) */
1781 if (descr
->nb_items
== descr
->page_size
)
1782 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1784 /* Move selection and focused item */
1785 if (!IS_MULTISELECT(descr
))
1787 if (index
== descr
->selected_item
)
1788 descr
->selected_item
= -1;
1789 else if (index
< descr
->selected_item
)
1791 descr
->selected_item
--;
1792 if (ISWIN31
) /* win 31 do not change the selected item number */
1793 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1797 if (descr
->focus_item
>= descr
->nb_items
)
1799 descr
->focus_item
= descr
->nb_items
- 1;
1800 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1806 /***********************************************************************
1807 * LISTBOX_ResetContent
1809 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1813 if (!(descr
->style
& LBS_NODATA
))
1814 for (i
= descr
->nb_items
- 1; i
>= 0; i
--) LISTBOX_DeleteItem(descr
, i
);
1815 HeapFree( GetProcessHeap(), 0, descr
->u
.items
);
1816 descr
->nb_items
= 0;
1817 descr
->top_item
= 0;
1818 descr
->selected_item
= -1;
1819 descr
->focus_item
= 0;
1820 descr
->anchor_item
= -1;
1821 descr
->items_size
= 0;
1822 descr
->u
.items
= NULL
;
1826 /***********************************************************************
1829 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, UINT count
)
1831 UINT orig_num
= descr
->nb_items
;
1833 if (!(descr
->style
& LBS_NODATA
))
1835 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1839 if (!resize_storage(descr
, count
))
1841 descr
->nb_items
= count
;
1845 LISTBOX_UpdateScroll(descr
);
1846 if (count
< orig_num
)
1848 descr
->anchor_item
= min(descr
->anchor_item
, count
- 1);
1849 if (descr
->selected_item
>= count
)
1850 descr
->selected_item
= -1;
1852 /* If we removed the scrollbar, reset the top of the list */
1853 if (count
<= descr
->page_size
&& orig_num
> descr
->page_size
)
1854 LISTBOX_SetTopItem(descr
, 0, TRUE
);
1856 descr
->focus_item
= min(descr
->focus_item
, count
- 1);
1859 /* If it was empty before growing, set focus to the first item */
1860 else if (orig_num
== 0) LISTBOX_SetCaretIndex(descr
, 0, FALSE
);
1862 else SendMessageW(descr
->self
, LB_RESETCONTENT
, 0, 0);
1864 InvalidateRect( descr
->self
, NULL
, TRUE
);
1869 /***********************************************************************
1872 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1873 LPCWSTR filespec
, BOOL long_names
)
1876 LRESULT ret
= LB_OKAY
;
1877 WIN32_FIND_DATAW entry
;
1879 LRESULT maxinsert
= LB_ERR
;
1881 /* don't scan directory if we just want drives exclusively */
1882 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1883 /* scan directory */
1884 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1886 int le
= GetLastError();
1887 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1894 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1896 if (!(attrib
& DDL_DIRECTORY
) ||
1897 !wcscmp( entry
.cFileName
, L
"." )) continue;
1899 if (!long_names
&& entry
.cAlternateFileName
[0])
1900 lstrcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1902 lstrcpyW( buffer
+ 1, entry
.cFileName
);
1903 lstrcatW(buffer
, L
"]");
1905 else /* not a directory */
1907 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1908 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1910 if ((attrib
& DDL_EXCLUSIVE
) &&
1911 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1914 if (!long_names
&& entry
.cAlternateFileName
[0])
1915 lstrcpyW( buffer
, entry
.cAlternateFileName
);
1917 lstrcpyW( buffer
, entry
.cFileName
);
1919 if (!long_names
) CharLowerW( buffer
);
1920 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1921 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1923 if (ret
<= maxinsert
) maxinsert
++; else maxinsert
= ret
;
1924 } while (FindNextFileW( handle
, &entry
));
1925 FindClose( handle
);
1933 if (attrib
& DDL_DRIVES
)
1935 WCHAR buffer
[] = L
"[-a-]";
1936 WCHAR root
[] = L
"A:\\";
1938 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1940 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1941 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1950 /***********************************************************************
1951 * LISTBOX_HandleVScroll
1953 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1957 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1961 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1964 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1967 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1968 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1971 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1972 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1974 case SB_THUMBPOSITION
:
1975 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1978 info
.cbSize
= sizeof(info
);
1979 info
.fMask
= SIF_TRACKPOS
;
1980 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1981 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1984 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1987 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1994 /***********************************************************************
1995 * LISTBOX_HandleHScroll
1997 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
2002 if (descr
->style
& LBS_MULTICOLUMN
)
2007 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
2011 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
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
);
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
);
2026 case SB_THUMBPOSITION
:
2027 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
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
,
2037 LISTBOX_SetTopItem( descr
, 0, TRUE
);
2040 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
2044 else if (descr
->horz_extent
)
2049 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
2052 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
2055 LISTBOX_SetHorizontalPos( descr
,
2056 descr
->horz_pos
- descr
->width
);
2059 LISTBOX_SetHorizontalPos( descr
,
2060 descr
->horz_pos
+ descr
->width
);
2062 case SB_THUMBPOSITION
:
2063 LISTBOX_SetHorizontalPos( descr
, pos
);
2066 info
.cbSize
= sizeof(info
);
2067 info
.fMask
= SIF_TRACKPOS
;
2068 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
2069 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
2072 LISTBOX_SetHorizontalPos( descr
, 0 );
2075 LISTBOX_SetHorizontalPos( descr
,
2076 descr
->horz_extent
- descr
->width
);
2083 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
2085 INT pulScrollLines
= 3;
2087 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
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
;
2094 descr
->wheel_remain
= delta
;
2096 if (descr
->wheel_remain
&& pulScrollLines
)
2099 if (descr
->style
& LBS_MULTICOLUMN
)
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
;
2109 pulScrollLines
= min(descr
->page_size
, pulScrollLines
);
2110 cLineScroll
= pulScrollLines
* descr
->wheel_remain
/ WHEEL_DELTA
;
2111 descr
->wheel_remain
-= WHEEL_DELTA
* cLineScroll
/ pulScrollLines
;
2113 LISTBOX_SetTopItem( descr
, descr
->top_item
- cLineScroll
, TRUE
);
2118 /***********************************************************************
2119 * LISTBOX_HandleLButtonDown
2121 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
2123 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2125 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2126 descr
->self
, x
, y
, index
, descr
->focus_item
);
2128 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2130 if (!descr
->in_focus
)
2132 if( !descr
->lphc
) SetFocus( descr
->self
);
2133 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2136 if (index
== -1) return 0;
2140 if (descr
->style
& LBS_NOTIFY
)
2141 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2142 MAKELPARAM( x
, y
) );
2145 descr
->captured
= TRUE
;
2146 SetCapture( descr
->self
);
2148 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
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);
2156 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2157 if (keys
& MK_CONTROL
)
2159 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2160 LISTBOX_SetSelection( descr
, index
,
2161 !is_item_selected(descr
, index
),
2162 (descr
->style
& LBS_NOTIFY
) != 0);
2166 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2168 if (descr
->style
& LBS_EXTENDEDSEL
)
2170 LISTBOX_SetSelection( descr
, index
,
2171 is_item_selected(descr
, index
),
2172 (descr
->style
& LBS_NOTIFY
) != 0 );
2176 LISTBOX_SetSelection( descr
, index
,
2177 !is_item_selected(descr
, index
),
2178 (descr
->style
& LBS_NOTIFY
) != 0 );
2184 descr
->anchor_item
= index
;
2185 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2186 LISTBOX_SetSelection( descr
, index
,
2187 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2192 if (GetWindowLongW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2199 if (DragDetect( descr
->self
, pt
))
2200 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2207 /*************************************************************************
2208 * LISTBOX_HandleLButtonDownCombo [Internal]
2210 * Process LButtonDown message for the ComboListBox
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
2220 * 0 since we are processing the WM_LBUTTONDOWN Message
2223 * This function is only to be used when a ListBox is a ComboListBox
2226 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2228 RECT clientRect
, screenRect
;
2234 GetClientRect(descr
->self
, &clientRect
);
2236 if(PtInRect(&clientRect
, mousePos
))
2238 /* MousePos is in client, resume normal processing */
2239 if (msg
== WM_LBUTTONDOWN
)
2241 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2242 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2244 else if (descr
->style
& LBS_NOTIFY
)
2245 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2249 POINT screenMousePos
;
2250 HWND hWndOldCapture
;
2252 /* Check the Non-Client Area */
2253 screenMousePos
= mousePos
;
2254 hWndOldCapture
= GetCapture();
2256 GetWindowRect(descr
->self
, &screenRect
);
2257 ClientToScreen(descr
->self
, &screenMousePos
);
2259 if(!PtInRect(&screenRect
, screenMousePos
))
2261 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2262 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2263 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2267 /* Check to see the NC is a scrollbar */
2269 LONG style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2270 /* Check Vertical scroll bar */
2271 if (style
& WS_VSCROLL
)
2273 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2274 if (PtInRect( &clientRect
, mousePos
))
2275 nHitTestType
= HTVSCROLL
;
2277 /* Check horizontal scroll bar */
2278 if (style
& WS_HSCROLL
)
2280 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2281 if (PtInRect( &clientRect
, mousePos
))
2282 nHitTestType
= HTHSCROLL
;
2284 /* Windows sends this message when a scrollbar is clicked
2287 if(nHitTestType
!= 0)
2289 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2290 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2292 /* Resume the Capture after scrolling is complete
2294 if(hWndOldCapture
!= 0)
2295 SetCapture(hWndOldCapture
);
2301 /***********************************************************************
2302 * LISTBOX_HandleLButtonUp
2304 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2306 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2307 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2308 LISTBOX_Timer
= LB_TIMER_NONE
;
2309 if (descr
->captured
)
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
);
2320 /***********************************************************************
2321 * LISTBOX_HandleTimer
2323 * Handle scrolling upon a timer event.
2324 * Return TRUE if scrolling should continue.
2326 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2331 if (descr
->top_item
) index
= descr
->top_item
- 1;
2335 if (descr
->top_item
) index
-= descr
->page_size
;
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;
2342 case LB_TIMER_RIGHT
:
2343 if (index
+ descr
->page_size
< descr
->nb_items
)
2344 index
+= descr
->page_size
;
2349 if (index
== descr
->focus_item
) return FALSE
;
2350 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2355 /***********************************************************************
2356 * LISTBOX_HandleSystemTimer
2358 * WM_SYSTIMER handler.
2360 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2362 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2364 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2365 LISTBOX_Timer
= LB_TIMER_NONE
;
2371 /***********************************************************************
2372 * LISTBOX_HandleMouseMove
2374 * WM_MOUSEMOVE handler.
2376 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2380 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2382 if (!descr
->captured
) return;
2384 if (descr
->style
& LBS_MULTICOLUMN
)
2387 else if (y
>= descr
->item_height
* descr
->page_size
)
2388 y
= descr
->item_height
* descr
->page_size
- 1;
2392 dir
= LB_TIMER_LEFT
;
2395 else if (x
>= descr
->width
)
2397 dir
= LB_TIMER_RIGHT
;
2398 x
= descr
->width
- 1;
2403 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2404 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
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
;
2411 /* Start/stop the system timer */
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
;
2421 /***********************************************************************
2422 * LISTBOX_HandleKeyDown
2424 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
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 */
2431 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2433 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2434 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2435 (LPARAM
)descr
->self
);
2436 if (caret
== -2) return 0;
2438 if (caret
== -1) switch(key
)
2441 if (descr
->style
& LBS_MULTICOLUMN
)
2443 bForceSelection
= FALSE
;
2444 if (descr
->focus_item
>= descr
->page_size
)
2445 caret
= descr
->focus_item
- descr
->page_size
;
2450 caret
= descr
->focus_item
- 1;
2451 if (caret
< 0) caret
= 0;
2454 if (descr
->style
& LBS_MULTICOLUMN
)
2456 bForceSelection
= FALSE
;
2457 caret
= min(descr
->focus_item
+ descr
->page_size
, descr
->nb_items
- 1);
2462 caret
= descr
->focus_item
+ 1;
2463 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2467 if (descr
->style
& LBS_MULTICOLUMN
)
2469 INT page
= descr
->width
/ descr
->column_width
;
2470 if (page
< 1) page
= 1;
2471 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2473 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2474 if (caret
< 0) caret
= 0;
2477 if (descr
->style
& LBS_MULTICOLUMN
)
2479 INT page
= descr
->width
/ descr
->column_width
;
2480 if (page
< 1) page
= 1;
2481 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2483 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2484 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2490 caret
= descr
->nb_items
- 1;
2493 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2494 else if (descr
->style
& LBS_MULTIPLESEL
)
2496 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2497 !is_item_selected(descr
, descr
->focus_item
),
2498 (descr
->style
& LBS_NOTIFY
) != 0 );
2502 bForceSelection
= FALSE
;
2504 if (bForceSelection
) /* focused item is used instead of key */
2505 caret
= descr
->focus_item
;
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
);
2514 if (descr
->style
& LBS_MULTIPLESEL
)
2515 descr
->selected_item
= caret
;
2517 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2518 if (descr
->style
& LBS_NOTIFY
)
2520 if (descr
->lphc
&& IsWindowVisible( descr
->self
))
2522 /* make sure that combo parent doesn't hide us */
2523 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2525 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2532 /***********************************************************************
2533 * LISTBOX_HandleChar
2535 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2543 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2545 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2546 MAKEWPARAM(charW
, descr
->focus_item
),
2547 (LPARAM
)descr
->self
);
2548 if (caret
== -2) return 0;
2550 if (caret
== -1 && !(descr
->style
& LBS_NODATA
))
2551 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
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
);
2564 /***********************************************************************
2567 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2570 MEASUREITEMSTRUCT mis
;
2573 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2576 GetClientRect( hwnd
, &rect
);
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;
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
;
2602 descr
->locale
= GetUserDefaultLCID();
2607 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2608 descr
->owner
= lphc
->self
;
2611 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2613 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
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 );
2622 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2624 descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2626 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2628 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2629 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2633 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2634 mis
.CtlType
= ODT_LISTBOX
;
2639 mis
.itemHeight
= descr
->item_height
;
2640 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2641 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2645 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2650 /***********************************************************************
2653 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2655 LISTBOX_ResetContent( descr
);
2656 SetWindowLongPtrW( descr
->self
, 0, 0 );
2657 HeapFree( GetProcessHeap(), 0, descr
);
2662 /***********************************************************************
2663 * ListBoxWndProc_common
2665 LRESULT
ListBoxWndProc_common( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2667 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2668 HEADCOMBO
*lphc
= NULL
;
2673 if (!IsWindow(hwnd
)) return 0;
2675 if (msg
== WM_CREATE
)
2677 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2678 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= lpcs
->lpCreateParams
;
2679 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2680 TRACE("creating hwnd %p descr %p\n", hwnd
, (void *)GetWindowLongPtrW( hwnd
, 0 ) );
2683 /* Ignore all other messages before we get a WM_CREATE */
2684 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2685 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2687 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2689 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2690 descr
->self
, SPY_GetMsgName(msg
, descr
->self
), wParam
, lParam
);
2694 case LB_RESETCONTENT
:
2695 LISTBOX_ResetContent( descr
);
2696 LISTBOX_UpdateScroll( descr
);
2697 InvalidateRect( descr
->self
, NULL
, TRUE
);
2704 if(unicode
|| !HAS_STRINGS(descr
))
2705 textW
= (LPWSTR
)lParam
;
2708 LPSTR textA
= (LPSTR
)lParam
;
2709 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2710 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2711 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2715 wParam
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2716 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2717 if (!unicode
&& HAS_STRINGS(descr
))
2718 HeapFree(GetProcessHeap(), 0, textW
);
2722 case LB_INSERTSTRING
:
2726 if(unicode
|| !HAS_STRINGS(descr
))
2727 textW
= (LPWSTR
)lParam
;
2730 LPSTR textA
= (LPSTR
)lParam
;
2731 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2732 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2733 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2737 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2738 if(!unicode
&& HAS_STRINGS(descr
))
2739 HeapFree(GetProcessHeap(), 0, textW
);
2747 if(unicode
|| !HAS_STRINGS(descr
))
2748 textW
= (LPWSTR
)lParam
;
2751 LPSTR textA
= (LPSTR
)lParam
;
2752 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2753 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2754 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2758 wParam
= LISTBOX_FindFileStrPos( descr
, textW
);
2759 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2760 if(!unicode
&& HAS_STRINGS(descr
))
2761 HeapFree(GetProcessHeap(), 0, textW
);
2765 case LB_DELETESTRING
:
2766 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2767 return descr
->nb_items
;
2770 SetLastError(ERROR_INVALID_INDEX
);
2774 case LB_GETITEMDATA
:
2775 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2777 SetLastError(ERROR_INVALID_INDEX
);
2780 return get_item_data(descr
, wParam
);
2782 case LB_SETITEMDATA
:
2783 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2785 SetLastError(ERROR_INVALID_INDEX
);
2788 set_item_data(descr
, wParam
, lParam
);
2789 /* undocumented: returns TRUE, not LB_OKAY (0) */
2793 return descr
->nb_items
;
2796 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, unicode
);
2799 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2801 SetLastError(ERROR_INVALID_INDEX
);
2804 if (!HAS_STRINGS(descr
)) return sizeof(ULONG_PTR
);
2805 if (unicode
) return lstrlenW(get_item_string(descr
, wParam
));
2806 return WideCharToMultiByte( CP_ACP
, 0, get_item_string(descr
, wParam
),
2807 lstrlenW(get_item_string(descr
, wParam
)), NULL
, 0, NULL
, NULL
);
2810 if (descr
->nb_items
== 0)
2812 if (!IS_MULTISELECT(descr
))
2813 return descr
->selected_item
;
2814 if (descr
->selected_item
!= -1)
2815 return descr
->selected_item
;
2816 return descr
->focus_item
;
2817 /* otherwise, if the user tries to move the selection with the */
2818 /* arrow keys, we will give the application something to choke on */
2819 case LB_GETTOPINDEX
:
2820 return descr
->top_item
;
2822 case LB_GETITEMHEIGHT
:
2823 return LISTBOX_GetItemHeight( descr
, wParam
);
2825 case LB_SETITEMHEIGHT
:
2826 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2828 case LB_ITEMFROMPOINT
:
2835 /* The hiword of the return value is not a client area
2836 hittest as suggested by MSDN, but rather a hittest on
2837 the returned listbox item. */
2839 if(descr
->nb_items
== 0)
2840 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2842 pt
.x
= (short)LOWORD(lParam
);
2843 pt
.y
= (short)HIWORD(lParam
);
2845 SetRect(&rect
, 0, 0, descr
->width
, descr
->height
);
2847 if(!PtInRect(&rect
, pt
))
2849 pt
.x
= min(pt
.x
, rect
.right
- 1);
2850 pt
.x
= max(pt
.x
, 0);
2851 pt
.y
= min(pt
.y
, rect
.bottom
- 1);
2852 pt
.y
= max(pt
.y
, 0);
2856 index
= LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
);
2860 index
= descr
->nb_items
- 1;
2863 return MAKELONG(index
, hit
? 0 : 1);
2866 case LB_SETCARETINDEX
:
2867 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2868 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2875 case LB_GETCARETINDEX
:
2876 return descr
->focus_item
;
2878 case LB_SETTOPINDEX
:
2879 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2881 case LB_SETCOLUMNWIDTH
:
2882 return LISTBOX_SetColumnWidth( descr
, wParam
);
2884 case LB_GETITEMRECT
:
2885 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2891 if(unicode
|| !HAS_STRINGS(descr
))
2892 textW
= (LPWSTR
)lParam
;
2895 LPSTR textA
= (LPSTR
)lParam
;
2896 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2897 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2898 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2900 ret
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2901 if(!unicode
&& HAS_STRINGS(descr
))
2902 HeapFree(GetProcessHeap(), 0, textW
);
2906 case LB_FINDSTRINGEXACT
:
2910 if(unicode
|| !HAS_STRINGS(descr
))
2911 textW
= (LPWSTR
)lParam
;
2914 LPSTR textA
= (LPSTR
)lParam
;
2915 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2916 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2917 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2919 ret
= LISTBOX_FindString( descr
, wParam
, textW
, TRUE
);
2920 if(!unicode
&& HAS_STRINGS(descr
))
2921 HeapFree(GetProcessHeap(), 0, textW
);
2925 case LB_SELECTSTRING
:
2930 if(HAS_STRINGS(descr
))
2931 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2932 debugstr_a((LPSTR
)lParam
));
2933 if(unicode
|| !HAS_STRINGS(descr
))
2934 textW
= (LPWSTR
)lParam
;
2937 LPSTR textA
= (LPSTR
)lParam
;
2938 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2939 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2940 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2942 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2943 if(!unicode
&& HAS_STRINGS(descr
))
2944 HeapFree(GetProcessHeap(), 0, textW
);
2945 if (index
!= LB_ERR
)
2947 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2948 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2954 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2956 return is_item_selected(descr
, wParam
);
2959 ret
= LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2960 if (ret
!= LB_ERR
&& wParam
)
2962 descr
->anchor_item
= lParam
;
2964 LISTBOX_SetCaretIndex( descr
, lParam
, TRUE
);
2969 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2970 LISTBOX_SetCaretIndex( descr
, wParam
, TRUE
);
2971 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2972 if (ret
!= LB_ERR
) ret
= descr
->selected_item
;
2975 case LB_GETSELCOUNT
:
2976 return LISTBOX_GetSelCount( descr
);
2978 case LB_GETSELITEMS
:
2979 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2981 case LB_SELITEMRANGE
:
2982 if (LOWORD(lParam
) <= HIWORD(lParam
))
2983 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2984 HIWORD(lParam
), wParam
);
2986 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2987 LOWORD(lParam
), wParam
);
2989 case LB_SELITEMRANGEEX
:
2990 if ((INT
)lParam
>= (INT
)wParam
)
2991 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2993 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
2995 case LB_GETHORIZONTALEXTENT
:
2996 return descr
->horz_extent
;
2998 case LB_SETHORIZONTALEXTENT
:
2999 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
3001 case LB_GETANCHORINDEX
:
3002 return descr
->anchor_item
;
3004 case LB_SETANCHORINDEX
:
3005 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
3007 SetLastError(ERROR_INVALID_INDEX
);
3010 descr
->anchor_item
= (INT
)wParam
;
3018 textW
= (LPWSTR
)lParam
;
3021 LPSTR textA
= (LPSTR
)lParam
;
3022 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
3023 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
3024 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
3026 ret
= LISTBOX_Directory( descr
, wParam
, textW
, msg
== LB_DIR
);
3028 HeapFree(GetProcessHeap(), 0, textW
);
3033 return descr
->locale
;
3038 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
3040 ret
= descr
->locale
;
3041 descr
->locale
= (LCID
)wParam
;
3045 case LB_INITSTORAGE
:
3046 return LISTBOX_InitStorage( descr
, wParam
);
3049 return LISTBOX_SetCount( descr
, (INT
)wParam
);
3051 case LB_SETTABSTOPS
:
3052 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
);
3055 if (descr
->caret_on
)
3057 descr
->caret_on
= TRUE
;
3058 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3059 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3063 if (!descr
->caret_on
)
3065 descr
->caret_on
= FALSE
;
3066 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3067 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3070 case LB_GETLISTBOXINFO
:
3071 return descr
->page_size
;
3074 return LISTBOX_Destroy( descr
);
3077 InvalidateRect( descr
->self
, NULL
, TRUE
);
3081 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
3085 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
3087 case WM_PRINTCLIENT
:
3091 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
3092 ret
= LISTBOX_Paint( descr
, hdc
);
3093 if( !wParam
) EndPaint( descr
->self
, &ps
);
3097 LISTBOX_UpdateSize( descr
);
3100 return (LRESULT
)descr
->font
;
3102 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
3103 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
3106 descr
->in_focus
= TRUE
;
3107 descr
->caret_on
= TRUE
;
3108 if (descr
->focus_item
!= -1)
3109 LISTBOX_DrawFocusRect( descr
, TRUE
);
3110 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
3113 LISTBOX_HandleLButtonUp( descr
); /* Release capture if we have it */
3114 descr
->in_focus
= FALSE
;
3115 descr
->wheel_remain
= 0;
3116 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
3117 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3118 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
3121 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3123 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3125 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
3126 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
3127 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
3128 case WM_LBUTTONDOWN
:
3130 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3131 (INT16
)LOWORD(lParam
),
3132 (INT16
)HIWORD(lParam
) );
3133 return LISTBOX_HandleLButtonDown( descr
, wParam
,
3134 (INT16
)LOWORD(lParam
),
3135 (INT16
)HIWORD(lParam
) );
3136 case WM_LBUTTONDBLCLK
:
3138 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3139 (INT16
)LOWORD(lParam
),
3140 (INT16
)HIWORD(lParam
) );
3141 if (descr
->style
& LBS_NOTIFY
)
3142 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
3145 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
3147 BOOL captured
= descr
->captured
;
3151 mousePos
.x
= (INT16
)LOWORD(lParam
);
3152 mousePos
.y
= (INT16
)HIWORD(lParam
);
3155 * If we are in a dropdown combobox, we simulate that
3156 * the mouse is captured to show the tracking of the item.
3158 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
3159 descr
->captured
= TRUE
;
3161 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
3163 descr
->captured
= captured
;
3165 else if (GetCapture() == descr
->self
)
3167 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
3168 (INT16
)HIWORD(lParam
) );
3178 * If the mouse button "up" is not in the listbox,
3179 * we make sure there is no selection by re-selecting the
3180 * item that was selected when the listbox was made visible.
3182 mousePos
.x
= (INT16
)LOWORD(lParam
);
3183 mousePos
.y
= (INT16
)HIWORD(lParam
);
3185 GetClientRect(descr
->self
, &clientRect
);
3188 * When the user clicks outside the combobox and the focus
3189 * is lost, the owning combobox will send a fake buttonup with
3190 * 0xFFFFFFF as the mouse location, we must also revert the
3191 * selection to the original selection.
3193 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
3194 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
3196 return LISTBOX_HandleLButtonUp( descr
);
3198 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3200 /* for some reason Windows makes it possible to
3201 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3203 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3204 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3205 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3207 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3211 return LISTBOX_HandleKeyDown( descr
, wParam
);
3216 charW
= (WCHAR
)wParam
;
3219 CHAR charA
= (CHAR
)wParam
;
3220 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
3222 return LISTBOX_HandleChar( descr
, charW
);
3225 return LISTBOX_HandleSystemTimer( descr
);
3227 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3230 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3231 wParam
, (LPARAM
)descr
->self
);
3232 TRACE("hbrush = %p\n", hbrush
);
3234 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3237 GetClientRect(descr
->self
, &rect
);
3238 FillRect((HDC
)wParam
, &rect
, hbrush
);
3243 if( lphc
) return 0;
3244 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3245 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3248 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3257 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3258 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3259 hwnd
, msg
, wParam
, lParam
);
3262 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3263 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3266 DWORD WINAPI
GetListBoxInfo(HWND hwnd
)
3268 TRACE("%p\n", hwnd
);
3269 return SendMessageW(hwnd
, LB_GETLISTBOXINFO
, 0, 0);