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
29 #include "wine/unicode.h"
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 static const WCHAR listboxW
[] = {'L','i','s','t','B','o','x',0};
248 const struct builtin_class_descr LISTBOX_builtin_class
=
251 CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
252 WINPROC_LISTBOX
, /* proc */
253 sizeof(LB_DESCR
*), /* extra */
254 IDC_ARROW
, /* cursor */
259 /*********************************************************************
260 * combolbox class descriptor
262 static const WCHAR combolboxW
[] = {'C','o','m','b','o','L','B','o','x',0};
263 const struct builtin_class_descr COMBOLBOX_builtin_class
=
265 combolboxW
, /* name */
266 CS_DBLCLKS
| CS_SAVEBITS
, /* style */
267 WINPROC_LISTBOX
, /* proc */
268 sizeof(LB_DESCR
*), /* extra */
269 IDC_ARROW
, /* cursor */
274 /***********************************************************************
275 * LISTBOX_GetCurrentPageSize
277 * Return the current page size
279 static INT
LISTBOX_GetCurrentPageSize( const LB_DESCR
*descr
)
282 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
283 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
285 if ((height
+= get_item_height(descr
, i
)) > descr
->height
) break;
287 if (i
== descr
->top_item
) return 1;
288 else return i
- descr
->top_item
;
292 /***********************************************************************
293 * LISTBOX_GetMaxTopIndex
295 * Return the maximum possible index for the top of the listbox.
297 static INT
LISTBOX_GetMaxTopIndex( const LB_DESCR
*descr
)
301 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
303 page
= descr
->height
;
304 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
305 if ((page
-= get_item_height(descr
, max
)) < 0) break;
306 if (max
< descr
->nb_items
- 1) max
++;
308 else if (descr
->style
& LBS_MULTICOLUMN
)
310 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
311 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
312 max
= (max
- page
) * descr
->page_size
;
316 max
= descr
->nb_items
- descr
->page_size
;
318 if (max
< 0) max
= 0;
323 /***********************************************************************
324 * LISTBOX_UpdateScroll
326 * Update the scrollbars. Should be called whenever the content
327 * of the listbox changes.
329 static void LISTBOX_UpdateScroll( LB_DESCR
*descr
)
333 /* Check the listbox scroll bar flags individually before we call
334 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
335 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
336 scroll bar when we do not need one.
337 if (!(descr->style & WS_VSCROLL)) return;
340 /* It is important that we check descr->style, and not wnd->dwStyle,
341 for WS_VSCROLL, as the former is exactly the one passed in
342 argument to CreateWindow.
343 In Windows (and from now on in Wine :) a listbox created
344 with such a style (no WS_SCROLL) does not update
345 the scrollbar with listbox-related data, thus letting
346 the programmer use it for his/her own purposes. */
348 if (descr
->style
& LBS_NOREDRAW
) return;
349 info
.cbSize
= sizeof(info
);
351 if (descr
->style
& LBS_MULTICOLUMN
)
354 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
355 info
.nPos
= descr
->top_item
/ descr
->page_size
;
356 info
.nPage
= descr
->width
/ descr
->column_width
;
357 if (info
.nPage
< 1) info
.nPage
= 1;
358 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
359 if (descr
->style
& LBS_DISABLENOSCROLL
)
360 info
.fMask
|= SIF_DISABLENOSCROLL
;
361 if (descr
->style
& WS_HSCROLL
)
362 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
364 info
.fMask
= SIF_RANGE
;
365 if (descr
->style
& WS_VSCROLL
)
366 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
371 info
.nMax
= descr
->nb_items
- 1;
372 info
.nPos
= descr
->top_item
;
373 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
374 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
375 if (descr
->style
& LBS_DISABLENOSCROLL
)
376 info
.fMask
|= SIF_DISABLENOSCROLL
;
377 if (descr
->style
& WS_VSCROLL
)
378 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
380 if ((descr
->style
& WS_HSCROLL
) && descr
->horz_extent
)
382 info
.nPos
= descr
->horz_pos
;
383 info
.nPage
= descr
->width
;
384 info
.fMask
= SIF_POS
| SIF_PAGE
;
385 if (descr
->style
& LBS_DISABLENOSCROLL
)
386 info
.fMask
|= SIF_DISABLENOSCROLL
;
387 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
391 if (descr
->style
& LBS_DISABLENOSCROLL
)
395 info
.fMask
= SIF_RANGE
| SIF_DISABLENOSCROLL
;
396 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
400 ShowScrollBar( descr
->self
, SB_HORZ
, FALSE
);
407 /***********************************************************************
410 * Set the top item of the listbox, scrolling up or down if necessary.
412 static LRESULT
LISTBOX_SetTopItem( LB_DESCR
*descr
, INT index
, BOOL scroll
)
414 INT max
= LISTBOX_GetMaxTopIndex( descr
);
416 TRACE("setting top item %d, scroll %d\n", index
, scroll
);
418 if (index
> max
) index
= max
;
419 if (index
< 0) index
= 0;
420 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
421 if (descr
->top_item
== index
) return LB_OKAY
;
425 if (descr
->style
& LBS_MULTICOLUMN
)
426 dx
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
427 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
430 if (index
> descr
->top_item
)
432 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
433 dy
-= get_item_height(descr
, i
);
437 for (i
= index
; i
< descr
->top_item
; i
++)
438 dy
+= get_item_height(descr
, i
);
442 dy
= (descr
->top_item
- index
) * descr
->item_height
;
444 ScrollWindowEx( descr
->self
, dx
, dy
, NULL
, NULL
, 0, NULL
,
445 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
448 InvalidateRect( descr
->self
, NULL
, TRUE
);
449 descr
->top_item
= index
;
450 LISTBOX_UpdateScroll( descr
);
455 /***********************************************************************
458 * Update the page size. Should be called when the size of
459 * the client area or the item height changes.
461 static void LISTBOX_UpdatePage( LB_DESCR
*descr
)
465 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
467 if (page_size
== descr
->page_size
) return;
468 descr
->page_size
= page_size
;
469 if (descr
->style
& LBS_MULTICOLUMN
)
470 InvalidateRect( descr
->self
, NULL
, TRUE
);
471 LISTBOX_SetTopItem( descr
, descr
->top_item
, FALSE
);
475 /***********************************************************************
478 * Update the size of the listbox. Should be called when the size of
479 * the client area changes.
481 static void LISTBOX_UpdateSize( LB_DESCR
*descr
)
485 GetClientRect( descr
->self
, &rect
);
486 descr
->width
= rect
.right
- rect
.left
;
487 descr
->height
= rect
.bottom
- rect
.top
;
488 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
493 GetWindowRect( descr
->self
, &rect
);
494 if(descr
->item_height
!= 0)
495 remaining
= descr
->height
% descr
->item_height
;
498 if ((descr
->height
> descr
->item_height
) && remaining
)
500 TRACE("[%p]: changing height %d -> %d\n",
501 descr
->self
, descr
->height
, descr
->height
- remaining
);
502 SetWindowPos( descr
->self
, 0, 0, 0, rect
.right
- rect
.left
,
503 rect
.bottom
- rect
.top
- remaining
,
504 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
508 TRACE("[%p]: new size = %d,%d\n", descr
->self
, descr
->width
, descr
->height
);
509 LISTBOX_UpdatePage( descr
);
510 LISTBOX_UpdateScroll( descr
);
512 /* Invalidate the focused item so it will be repainted correctly */
513 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
515 InvalidateRect( descr
->self
, &rect
, FALSE
);
520 /***********************************************************************
521 * LISTBOX_GetItemRect
523 * Get the rectangle enclosing an item, in listbox client coordinates.
524 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
526 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
)
528 /* Index <= 0 is legal even on empty listboxes */
529 if (index
&& (index
>= descr
->nb_items
))
532 SetLastError(ERROR_INVALID_INDEX
);
535 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
536 if (descr
->style
& LBS_MULTICOLUMN
)
538 INT col
= (index
/ descr
->page_size
) -
539 (descr
->top_item
/ descr
->page_size
);
540 rect
->left
+= col
* descr
->column_width
;
541 rect
->right
= rect
->left
+ descr
->column_width
;
542 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
543 rect
->bottom
= rect
->top
+ descr
->item_height
;
545 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
548 rect
->right
+= descr
->horz_pos
;
549 if ((index
>= 0) && (index
< descr
->nb_items
))
551 if (index
< descr
->top_item
)
553 for (i
= descr
->top_item
-1; i
>= index
; i
--)
554 rect
->top
-= get_item_height(descr
, i
);
558 for (i
= descr
->top_item
; i
< index
; i
++)
559 rect
->top
+= get_item_height(descr
, i
);
561 rect
->bottom
= rect
->top
+ get_item_height(descr
, index
);
567 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
568 rect
->bottom
= rect
->top
+ descr
->item_height
;
569 rect
->right
+= descr
->horz_pos
;
572 TRACE("item %d, rect %s\n", index
, wine_dbgstr_rect(rect
));
574 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
575 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
579 /***********************************************************************
580 * LISTBOX_GetItemFromPoint
582 * Return the item nearest from point (x,y) (in client coordinates).
584 static INT
LISTBOX_GetItemFromPoint( const LB_DESCR
*descr
, INT x
, INT y
)
586 INT index
= descr
->top_item
;
588 if (!descr
->nb_items
) return -1; /* No items */
589 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
594 while (index
< descr
->nb_items
)
596 if ((pos
+= get_item_height(descr
, index
)) > y
) break;
605 if ((pos
-= get_item_height(descr
, index
)) <= y
) break;
609 else if (descr
->style
& LBS_MULTICOLUMN
)
611 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
612 if (y
>= 0) index
+= y
/ descr
->item_height
;
613 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
614 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
618 index
+= (y
/ descr
->item_height
);
620 if (index
< 0) return 0;
621 if (index
>= descr
->nb_items
) return -1;
626 /***********************************************************************
631 static void LISTBOX_PaintItem( LB_DESCR
*descr
, HDC hdc
, const RECT
*rect
,
632 INT index
, UINT action
, BOOL ignoreFocus
)
634 BOOL selected
= FALSE
, focused
;
635 WCHAR
*item_str
= NULL
;
637 if (index
< descr
->nb_items
)
639 item_str
= get_item_string(descr
, index
);
640 selected
= is_item_selected(descr
, index
);
643 focused
= !ignoreFocus
&& descr
->focus_item
== index
&& descr
->caret_on
&& descr
->in_focus
;
645 if (IS_OWNERDRAW(descr
))
651 if (index
>= descr
->nb_items
)
653 if (action
== ODA_FOCUS
)
654 DrawFocusRect( hdc
, rect
);
656 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
660 /* some programs mess with the clipping region when
661 drawing the item, *and* restore the previous region
662 after they are done, so a region has better to exist
663 else everything ends clipped */
664 GetClientRect(descr
->self
, &r
);
665 hrgn
= set_control_clipping( hdc
, &r
);
667 dis
.CtlType
= ODT_LISTBOX
;
668 dis
.CtlID
= GetWindowLongPtrW( descr
->self
, GWLP_ID
);
669 dis
.hwndItem
= descr
->self
;
670 dis
.itemAction
= action
;
675 dis
.itemState
|= ODS_SELECTED
;
677 dis
.itemState
|= ODS_FOCUS
;
678 if (!IsWindowEnabled(descr
->self
)) dis
.itemState
|= ODS_DISABLED
;
679 dis
.itemData
= get_item_data(descr
, index
);
681 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
682 descr
->self
, index
, debugstr_w(item_str
), action
,
683 dis
.itemState
, wine_dbgstr_rect(rect
) );
684 SendMessageW(descr
->owner
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
685 SelectClipRgn( hdc
, hrgn
);
686 if (hrgn
) DeleteObject( hrgn
);
690 COLORREF oldText
= 0, oldBk
= 0;
692 if (action
== ODA_FOCUS
)
694 DrawFocusRect( hdc
, rect
);
699 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
700 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
703 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
704 descr
->self
, index
, debugstr_w(item_str
), action
,
705 wine_dbgstr_rect(rect
) );
707 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
708 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
709 else if (!(descr
->style
& LBS_USETABSTOPS
))
710 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
711 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item_str
,
712 strlenW(item_str
), NULL
);
715 /* Output empty string to paint background in the full width. */
716 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
717 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
718 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
719 item_str
, strlenW(item_str
),
720 descr
->nb_tabs
, descr
->tabs
, 0);
724 SetBkColor( hdc
, oldBk
);
725 SetTextColor( hdc
, oldText
);
728 DrawFocusRect( hdc
, rect
);
733 /***********************************************************************
736 * Change the redraw flag.
738 static void LISTBOX_SetRedraw( LB_DESCR
*descr
, BOOL on
)
742 if (!(descr
->style
& LBS_NOREDRAW
)) return;
743 descr
->style
&= ~LBS_NOREDRAW
;
744 if (descr
->style
& LBS_DISPLAYCHANGED
)
745 { /* page was changed while setredraw false, refresh automatically */
746 InvalidateRect(descr
->self
, NULL
, TRUE
);
747 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
748 { /* reset top of page if less than number of items/page */
749 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
750 if (descr
->top_item
< 0) descr
->top_item
= 0;
752 descr
->style
&= ~LBS_DISPLAYCHANGED
;
754 LISTBOX_UpdateScroll( descr
);
756 else descr
->style
|= LBS_NOREDRAW
;
760 /***********************************************************************
761 * LISTBOX_RepaintItem
763 * Repaint a single item synchronously.
765 static void LISTBOX_RepaintItem( LB_DESCR
*descr
, INT index
, UINT action
)
770 HBRUSH hbrush
, oldBrush
= 0;
772 /* Do not repaint the item if the item is not visible */
773 if (!IsWindowVisible(descr
->self
)) return;
774 if (descr
->style
& LBS_NOREDRAW
)
776 descr
->style
|= LBS_DISPLAYCHANGED
;
779 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
780 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
781 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
782 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
783 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
784 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
785 if (!IsWindowEnabled(descr
->self
))
786 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
787 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
788 LISTBOX_PaintItem( descr
, hdc
, &rect
, index
, action
, TRUE
);
789 if (oldFont
) SelectObject( hdc
, oldFont
);
790 if (oldBrush
) SelectObject( hdc
, oldBrush
);
791 ReleaseDC( descr
->self
, hdc
);
795 /***********************************************************************
796 * LISTBOX_DrawFocusRect
798 static void LISTBOX_DrawFocusRect( LB_DESCR
*descr
, BOOL on
)
804 /* Do not repaint the item if the item is not visible */
805 if (!IsWindowVisible(descr
->self
)) return;
807 if (descr
->focus_item
== -1) return;
808 if (!descr
->caret_on
|| !descr
->in_focus
) return;
810 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) != 1) return;
811 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
812 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
813 if (!IsWindowEnabled(descr
->self
))
814 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
815 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
816 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, !on
);
817 if (oldFont
) SelectObject( hdc
, oldFont
);
818 ReleaseDC( descr
->self
, hdc
);
822 /***********************************************************************
823 * LISTBOX_InitStorage
825 static LRESULT
LISTBOX_InitStorage( LB_DESCR
*descr
, INT nb_items
)
827 UINT new_size
= descr
->nb_items
+ nb_items
;
829 if (new_size
> descr
->items_size
&& !resize_storage(descr
, new_size
))
831 return descr
->items_size
;
835 /***********************************************************************
836 * LISTBOX_SetTabStops
838 static BOOL
LISTBOX_SetTabStops( LB_DESCR
*descr
, INT count
, LPINT tabs
)
842 if (!(descr
->style
& LBS_USETABSTOPS
))
844 SetLastError(ERROR_LB_WITHOUT_TABSTOPS
);
848 HeapFree( GetProcessHeap(), 0, descr
->tabs
);
849 if (!(descr
->nb_tabs
= count
))
854 if (!(descr
->tabs
= HeapAlloc( GetProcessHeap(), 0,
855 descr
->nb_tabs
* sizeof(INT
) )))
857 memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
859 /* convert into "dialog units"*/
860 for (i
= 0; i
< descr
->nb_tabs
; i
++)
861 descr
->tabs
[i
] = MulDiv(descr
->tabs
[i
], descr
->avg_char_width
, 4);
867 /***********************************************************************
870 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPWSTR buffer
, BOOL unicode
)
874 if ((index
< 0) || (index
>= descr
->nb_items
))
876 SetLastError(ERROR_INVALID_INDEX
);
880 if (HAS_STRINGS(descr
))
882 WCHAR
*str
= get_item_string(descr
, index
);
889 return WideCharToMultiByte( CP_ACP
, 0, str
, len
, NULL
, 0, NULL
, NULL
);
892 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(str
));
894 __TRY
/* hide a Delphi bug that passes a read-only buffer */
898 strcpyW(buffer
, str
);
899 len
= strlenW(buffer
);
903 len
= WideCharToMultiByte(CP_ACP
, 0, str
, -1, (LPSTR
)buffer
,
904 0x7FFFFFFF, NULL
, NULL
) - 1;
909 WARN( "got an invalid buffer (Delphi bug?)\n" );
910 SetLastError( ERROR_INVALID_PARAMETER
);
917 *((ULONG_PTR
*)buffer
) = get_item_data(descr
, index
);
918 len
= sizeof(ULONG_PTR
);
923 static inline INT
LISTBOX_lstrcmpiW( LCID lcid
, LPCWSTR str1
, LPCWSTR str2
)
925 INT ret
= CompareStringW( lcid
, NORM_IGNORECASE
, str1
, -1, str2
, -1 );
926 if (ret
== CSTR_LESS_THAN
)
928 if (ret
== CSTR_EQUAL
)
930 if (ret
== CSTR_GREATER_THAN
)
935 /***********************************************************************
936 * LISTBOX_FindStringPos
938 * Find the nearest string located before a given string in sort order.
939 * If 'exact' is TRUE, return an error if we don't get an exact match.
941 static INT
LISTBOX_FindStringPos( LB_DESCR
*descr
, LPCWSTR str
, BOOL exact
)
943 INT index
, min
, max
, res
;
945 if (!descr
->nb_items
|| !(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
948 max
= descr
->nb_items
- 1;
951 index
= (min
+ max
) / 2;
952 if (HAS_STRINGS(descr
))
953 res
= LISTBOX_lstrcmpiW( descr
->locale
, get_item_string(descr
, index
), str
);
956 COMPAREITEMSTRUCT cis
;
957 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
959 cis
.CtlType
= ODT_LISTBOX
;
961 cis
.hwndItem
= descr
->self
;
962 /* note that some application (MetaStock) expects the second item
963 * to be in the listbox */
965 cis
.itemData1
= get_item_data(descr
, index
);
967 cis
.itemData2
= (ULONG_PTR
)str
;
968 cis
.dwLocaleId
= descr
->locale
;
969 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
971 if (!res
) return index
;
972 if (res
> 0) max
= index
- 1;
973 else min
= index
+ 1;
975 return exact
? -1 : min
;
979 /***********************************************************************
980 * LISTBOX_FindFileStrPos
982 * Find the nearest string located before a given string in directory
983 * sort order (i.e. first files, then directories, then drives).
985 static INT
LISTBOX_FindFileStrPos( LB_DESCR
*descr
, LPCWSTR str
)
989 if (!HAS_STRINGS(descr
))
990 return LISTBOX_FindStringPos( descr
, str
, FALSE
);
992 max
= descr
->nb_items
;
995 INT index
= (min
+ max
) / 2;
996 LPCWSTR p
= get_item_string(descr
, index
);
997 if (*p
== '[') /* drive or directory */
999 if (*str
!= '[') res
= -1;
1000 else if (p
[1] == '-') /* drive */
1002 if (str
[1] == '-') res
= str
[2] - p
[2];
1005 else /* directory */
1007 if (str
[1] == '-') res
= 1;
1008 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
1013 if (*str
== '[') res
= 1;
1014 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
1016 if (!res
) return index
;
1017 if (res
< 0) max
= index
;
1018 else min
= index
+ 1;
1024 /***********************************************************************
1025 * LISTBOX_FindString
1027 * Find the item beginning with a given string.
1029 static INT
LISTBOX_FindString( LB_DESCR
*descr
, INT start
, LPCWSTR str
, BOOL exact
)
1033 if (descr
->style
& LBS_NODATA
)
1035 SetLastError(ERROR_INVALID_PARAMETER
);
1040 if (start
>= descr
->nb_items
) start
= 0;
1041 if (HAS_STRINGS(descr
))
1043 if (!str
|| ! str
[0] ) return LB_ERR
;
1046 for (i
= 0, index
= start
; i
< descr
->nb_items
; i
++, index
++)
1048 if (index
== descr
->nb_items
) index
= 0;
1049 if (!LISTBOX_lstrcmpiW(descr
->locale
, str
, get_item_string(descr
, index
)))
1055 /* Special case for drives and directories: ignore prefix */
1056 INT len
= strlenW(str
);
1059 for (i
= 0, index
= start
; i
< descr
->nb_items
; i
++, index
++)
1061 if (index
== descr
->nb_items
) index
= 0;
1062 item_str
= get_item_string(descr
, index
);
1064 if (!strncmpiW(str
, item_str
, len
)) return index
;
1065 if (item_str
[0] == '[')
1067 if (!strncmpiW(str
, item_str
+ 1, len
)) return index
;
1068 if (item_str
[1] == '-' && !strncmpiW(str
, item_str
+ 2, len
)) return index
;
1075 if (exact
&& (descr
->style
& LBS_SORT
))
1076 /* If sorted, use a WM_COMPAREITEM binary search */
1077 return LISTBOX_FindStringPos( descr
, str
, TRUE
);
1079 /* Otherwise use a linear search */
1080 for (i
= 0, index
= start
; i
< descr
->nb_items
; i
++, index
++)
1082 if (index
== descr
->nb_items
) index
= 0;
1083 if (get_item_data(descr
, index
) == (ULONG_PTR
)str
) return index
;
1090 /***********************************************************************
1091 * LISTBOX_GetSelCount
1093 static LRESULT
LISTBOX_GetSelCount( const LB_DESCR
*descr
)
1097 if (!(descr
->style
& LBS_MULTIPLESEL
) ||
1098 (descr
->style
& LBS_NOSEL
))
1100 for (i
= count
= 0; i
< descr
->nb_items
; i
++)
1101 if (is_item_selected(descr
, i
)) count
++;
1106 /***********************************************************************
1107 * LISTBOX_GetSelItems
1109 static LRESULT
LISTBOX_GetSelItems( const LB_DESCR
*descr
, INT max
, LPINT array
)
1113 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1114 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++)
1115 if (is_item_selected(descr
, i
)) array
[count
++] = i
;
1120 /***********************************************************************
1123 static LRESULT
LISTBOX_Paint( LB_DESCR
*descr
, HDC hdc
)
1125 INT i
, col_pos
= descr
->page_size
- 1;
1127 RECT focusRect
= {-1, -1, -1, -1};
1129 HBRUSH hbrush
, oldBrush
= 0;
1131 if (descr
->style
& LBS_NOREDRAW
) return 0;
1133 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1134 if (descr
->style
& LBS_MULTICOLUMN
)
1135 rect
.right
= rect
.left
+ descr
->column_width
;
1136 else if (descr
->horz_pos
)
1138 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1139 rect
.right
+= descr
->horz_pos
;
1142 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1143 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
1144 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
1145 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1146 if (!IsWindowEnabled(descr
->self
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1148 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1151 /* Special case for empty listbox: paint focus rect */
1152 rect
.bottom
= rect
.top
+ descr
->item_height
;
1153 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1154 &rect
, NULL
, 0, NULL
);
1155 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1156 rect
.top
= rect
.bottom
;
1159 /* Paint all the item, regarding the selection
1160 Focus state will be painted after */
1162 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1164 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1165 rect
.bottom
= rect
.top
+ descr
->item_height
;
1167 rect
.bottom
= rect
.top
+ get_item_height(descr
, i
);
1169 /* keep the focus rect, to paint the focus item after */
1170 if (i
== descr
->focus_item
)
1173 LISTBOX_PaintItem( descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1174 rect
.top
= rect
.bottom
;
1176 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1178 if (!IS_OWNERDRAW(descr
))
1180 /* Clear the bottom of the column */
1181 if (rect
.top
< descr
->height
)
1183 rect
.bottom
= descr
->height
;
1184 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1185 &rect
, NULL
, 0, NULL
);
1189 /* Go to the next column */
1190 rect
.left
+= descr
->column_width
;
1191 rect
.right
+= descr
->column_width
;
1193 col_pos
= descr
->page_size
- 1;
1194 if (rect
.left
>= descr
->width
) break;
1199 if (rect
.top
>= descr
->height
) break;
1203 /* Paint the focus item now */
1204 if (focusRect
.top
!= focusRect
.bottom
&&
1205 descr
->caret_on
&& descr
->in_focus
)
1206 LISTBOX_PaintItem( descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1208 if (!IS_OWNERDRAW(descr
))
1210 /* Clear the remainder of the client area */
1211 if (rect
.top
< descr
->height
)
1213 rect
.bottom
= descr
->height
;
1214 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1215 &rect
, NULL
, 0, NULL
);
1217 if (rect
.right
< descr
->width
)
1219 rect
.left
= rect
.right
;
1220 rect
.right
= descr
->width
;
1222 rect
.bottom
= descr
->height
;
1223 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1224 &rect
, NULL
, 0, NULL
);
1227 if (oldFont
) SelectObject( hdc
, oldFont
);
1228 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1233 /***********************************************************************
1234 * LISTBOX_InvalidateItems
1236 * Invalidate all items from a given item. If the specified item is not
1237 * visible, nothing happens.
1239 static void LISTBOX_InvalidateItems( LB_DESCR
*descr
, INT index
)
1243 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1245 if (descr
->style
& LBS_NOREDRAW
)
1247 descr
->style
|= LBS_DISPLAYCHANGED
;
1250 rect
.bottom
= descr
->height
;
1251 InvalidateRect( descr
->self
, &rect
, TRUE
);
1252 if (descr
->style
& LBS_MULTICOLUMN
)
1254 /* Repaint the other columns */
1255 rect
.left
= rect
.right
;
1256 rect
.right
= descr
->width
;
1258 InvalidateRect( descr
->self
, &rect
, TRUE
);
1263 static void LISTBOX_InvalidateItemRect( LB_DESCR
*descr
, INT index
)
1267 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1268 InvalidateRect( descr
->self
, &rect
, TRUE
);
1271 /***********************************************************************
1272 * LISTBOX_GetItemHeight
1274 static LRESULT
LISTBOX_GetItemHeight( const LB_DESCR
*descr
, INT index
)
1276 if (descr
->style
& LBS_OWNERDRAWVARIABLE
&& descr
->nb_items
> 0)
1278 if ((index
< 0) || (index
>= descr
->nb_items
))
1280 SetLastError(ERROR_INVALID_INDEX
);
1283 return get_item_height(descr
, index
);
1285 else return descr
->item_height
;
1289 /***********************************************************************
1290 * LISTBOX_SetItemHeight
1292 static LRESULT
LISTBOX_SetItemHeight( LB_DESCR
*descr
, INT index
, INT height
, BOOL repaint
)
1294 if (height
> MAXBYTE
)
1297 if (!height
) height
= 1;
1299 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1301 if ((index
< 0) || (index
>= descr
->nb_items
))
1303 SetLastError(ERROR_INVALID_INDEX
);
1306 TRACE("[%p]: item %d height = %d\n", descr
->self
, index
, height
);
1307 set_item_height(descr
, index
, height
);
1308 LISTBOX_UpdateScroll( descr
);
1310 LISTBOX_InvalidateItems( descr
, index
);
1312 else if (height
!= descr
->item_height
)
1314 TRACE("[%p]: new height = %d\n", descr
->self
, height
);
1315 descr
->item_height
= height
;
1316 LISTBOX_UpdatePage( descr
);
1317 LISTBOX_UpdateScroll( descr
);
1319 InvalidateRect( descr
->self
, 0, TRUE
);
1325 /***********************************************************************
1326 * LISTBOX_SetHorizontalPos
1328 static void LISTBOX_SetHorizontalPos( LB_DESCR
*descr
, INT pos
)
1332 if (pos
> descr
->horz_extent
- descr
->width
)
1333 pos
= descr
->horz_extent
- descr
->width
;
1334 if (pos
< 0) pos
= 0;
1335 if (!(diff
= descr
->horz_pos
- pos
)) return;
1336 TRACE("[%p]: new horz pos = %d\n", descr
->self
, pos
);
1337 descr
->horz_pos
= pos
;
1338 LISTBOX_UpdateScroll( descr
);
1339 if (abs(diff
) < descr
->width
)
1342 /* Invalidate the focused item so it will be repainted correctly */
1343 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1344 InvalidateRect( descr
->self
, &rect
, TRUE
);
1345 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
1346 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1349 InvalidateRect( descr
->self
, NULL
, TRUE
);
1353 /***********************************************************************
1354 * LISTBOX_SetHorizontalExtent
1356 static LRESULT
LISTBOX_SetHorizontalExtent( LB_DESCR
*descr
, INT extent
)
1358 if (descr
->style
& LBS_MULTICOLUMN
)
1360 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1361 TRACE("[%p]: new horz extent = %d\n", descr
->self
, extent
);
1362 descr
->horz_extent
= extent
;
1363 if (descr
->style
& WS_HSCROLL
) {
1365 info
.cbSize
= sizeof(info
);
1367 info
.nMax
= descr
->horz_extent
? descr
->horz_extent
- 1 : 0;
1368 info
.fMask
= SIF_RANGE
;
1369 if (descr
->style
& LBS_DISABLENOSCROLL
)
1370 info
.fMask
|= SIF_DISABLENOSCROLL
;
1371 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
1373 if (descr
->horz_pos
> extent
- descr
->width
)
1374 LISTBOX_SetHorizontalPos( descr
, extent
- descr
->width
);
1379 /***********************************************************************
1380 * LISTBOX_SetColumnWidth
1382 static LRESULT
LISTBOX_SetColumnWidth( LB_DESCR
*descr
, INT column_width
)
1386 TRACE("[%p]: new column width = %d\n", descr
->self
, column_width
);
1388 GetClientRect(descr
->self
, &rect
);
1389 descr
->width
= rect
.right
- rect
.left
;
1390 descr
->height
= rect
.bottom
- rect
.top
;
1391 descr
->column_width
= column_width
;
1393 LISTBOX_UpdatePage(descr
);
1394 LISTBOX_UpdateScroll(descr
);
1399 /***********************************************************************
1402 * Returns the item height.
1404 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1408 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1413 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1415 ERR("unable to get DC.\n" );
1418 if (font
) oldFont
= SelectObject( hdc
, font
);
1419 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1420 if (oldFont
) SelectObject( hdc
, oldFont
);
1421 ReleaseDC( descr
->self
, hdc
);
1423 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1424 if (!IS_OWNERDRAW(descr
))
1425 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1430 /***********************************************************************
1431 * LISTBOX_MakeItemVisible
1433 * Make sure that a given item is partially or fully visible.
1435 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1439 TRACE("current top item %d, index %d, fully %d\n", descr
->top_item
, index
, fully
);
1441 if (index
<= descr
->top_item
) top
= index
;
1442 else if (descr
->style
& LBS_MULTICOLUMN
)
1444 INT cols
= descr
->width
;
1445 if (!fully
) cols
+= descr
->column_width
- 1;
1446 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1448 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1449 top
= index
- descr
->page_size
* (cols
- 1);
1451 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1453 INT height
= fully
? get_item_height(descr
, index
) : 1;
1454 for (top
= index
; top
> descr
->top_item
; top
--)
1455 if ((height
+= get_item_height(descr
, top
- 1)) > descr
->height
) break;
1459 if (index
< descr
->top_item
+ descr
->page_size
) return;
1460 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1461 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1462 top
= index
- descr
->page_size
+ 1;
1464 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1467 /***********************************************************************
1468 * LISTBOX_SetCaretIndex
1471 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1474 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1476 BOOL focus_changed
= descr
->focus_item
!= index
;
1478 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1480 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1481 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1485 LISTBOX_DrawFocusRect( descr
, FALSE
);
1486 descr
->focus_item
= index
;
1489 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1492 LISTBOX_DrawFocusRect( descr
, TRUE
);
1498 /***********************************************************************
1499 * LISTBOX_SelectItemRange
1501 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1503 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1508 /* A few sanity checks */
1510 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1511 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1513 if (!descr
->nb_items
) return LB_OKAY
;
1515 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1516 if (first
< 0) first
= 0;
1517 if (last
< first
) return LB_OKAY
;
1519 if (on
) /* Turn selection on */
1521 for (i
= first
; i
<= last
; i
++)
1523 if (is_item_selected(descr
, i
)) continue;
1524 set_item_selected_state(descr
, i
, TRUE
);
1525 LISTBOX_InvalidateItemRect(descr
, i
);
1528 else /* Turn selection off */
1530 for (i
= first
; i
<= last
; i
++)
1532 if (!is_item_selected(descr
, i
)) continue;
1533 set_item_selected_state(descr
, i
, FALSE
);
1534 LISTBOX_InvalidateItemRect(descr
, i
);
1540 /***********************************************************************
1541 * LISTBOX_SetSelection
1543 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1544 BOOL on
, BOOL send_notify
)
1546 TRACE( "cur_sel=%d index=%d notify=%s\n",
1547 descr
->selected_item
, index
, send_notify
? "YES" : "NO" );
1549 if (descr
->style
& LBS_NOSEL
)
1551 descr
->selected_item
= index
;
1554 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1555 if (descr
->style
& LBS_MULTIPLESEL
)
1557 if (index
== -1) /* Select all items */
1558 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1559 else /* Only one item */
1560 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1564 INT oldsel
= descr
->selected_item
;
1565 if (index
== oldsel
) return LB_OKAY
;
1566 if (oldsel
!= -1) set_item_selected_state(descr
, oldsel
, FALSE
);
1567 if (index
!= -1) set_item_selected_state(descr
, index
, TRUE
);
1568 descr
->selected_item
= index
;
1569 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1570 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1571 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1572 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1574 if( descr
->lphc
) /* set selection change flag for parent combo */
1575 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1581 /***********************************************************************
1584 * Change the caret position and extend the selection to the new caret.
1586 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1588 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1590 if ((index
< 0) || (index
>= descr
->nb_items
))
1593 /* Important, repaint needs to be done in this order if
1594 you want to mimic Windows behavior:
1595 1. Remove the focus and paint the item
1596 2. Remove the selection and paint the item(s)
1597 3. Set the selection and repaint the item(s)
1598 4. Set the focus to 'index' and repaint the item */
1600 /* 1. remove the focus and repaint the item */
1601 LISTBOX_DrawFocusRect( descr
, FALSE
);
1603 /* 2. then turn off the previous selection */
1604 /* 3. repaint the new selected item */
1605 if (descr
->style
& LBS_EXTENDEDSEL
)
1607 if (descr
->anchor_item
!= -1)
1609 INT first
= min( index
, descr
->anchor_item
);
1610 INT last
= max( index
, descr
->anchor_item
);
1612 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1613 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1614 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1617 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1619 /* Set selection to new caret item */
1620 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1623 /* 4. repaint the new item with the focus */
1624 descr
->focus_item
= index
;
1625 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1626 LISTBOX_DrawFocusRect( descr
, TRUE
);
1630 /***********************************************************************
1631 * LISTBOX_InsertItem
1633 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1634 LPWSTR str
, ULONG_PTR data
)
1636 INT oldfocus
= descr
->focus_item
;
1638 if (index
== -1) index
= descr
->nb_items
;
1639 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1640 if (!resize_storage(descr
, descr
->nb_items
+ 1)) return LB_ERR
;
1642 insert_item_data(descr
, index
);
1644 set_item_string(descr
, index
, str
);
1645 set_item_data(descr
, index
, HAS_STRINGS(descr
) ? 0 : data
);
1646 set_item_height(descr
, index
, 0);
1647 set_item_selected_state(descr
, index
, FALSE
);
1649 /* Get item height */
1651 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1653 MEASUREITEMSTRUCT mis
;
1654 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1656 mis
.CtlType
= ODT_LISTBOX
;
1659 mis
.itemData
= str
? (ULONG_PTR
)str
: data
;
1660 mis
.itemHeight
= descr
->item_height
;
1661 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1662 set_item_height(descr
, index
, mis
.itemHeight
? mis
.itemHeight
: 1);
1663 TRACE("[%p]: measure item %d (%s) = %d\n",
1664 descr
->self
, index
, str
? debugstr_w(str
) : "", get_item_height(descr
, index
));
1667 /* Repaint the items */
1669 LISTBOX_UpdateScroll( descr
);
1670 LISTBOX_InvalidateItems( descr
, index
);
1672 /* Move selection and focused item */
1673 /* If listbox was empty, set focus to the first item */
1674 if (descr
->nb_items
== 1)
1675 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1676 /* single select don't change selection index in win31 */
1677 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1679 descr
->selected_item
++;
1680 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1684 if (index
<= descr
->selected_item
)
1686 descr
->selected_item
++;
1687 descr
->focus_item
= oldfocus
; /* focus not changed */
1694 /***********************************************************************
1695 * LISTBOX_InsertString
1697 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
, LPCWSTR str
)
1699 LPWSTR new_str
= NULL
;
1702 if (HAS_STRINGS(descr
))
1704 static const WCHAR empty_stringW
[] = { 0 };
1705 if (!str
) str
= empty_stringW
;
1706 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1708 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1711 strcpyW(new_str
, str
);
1714 if (index
== -1) index
= descr
->nb_items
;
1715 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, (ULONG_PTR
)str
)) != 0)
1717 HeapFree( GetProcessHeap(), 0, new_str
);
1721 TRACE("[%p]: added item %d %s\n",
1722 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1727 /***********************************************************************
1728 * LISTBOX_DeleteItem
1730 * Delete the content of an item. 'index' must be a valid index.
1732 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1734 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1735 * while Win95 sends it for all items with user data.
1736 * It's probably better to send it too often than not
1737 * often enough, so this is what we do here.
1739 if (IS_OWNERDRAW(descr
) || get_item_data(descr
, index
))
1741 DELETEITEMSTRUCT dis
;
1742 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1744 dis
.CtlType
= ODT_LISTBOX
;
1747 dis
.hwndItem
= descr
->self
;
1748 dis
.itemData
= get_item_data(descr
, index
);
1749 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1751 HeapFree( GetProcessHeap(), 0, get_item_string(descr
, index
) );
1755 /***********************************************************************
1756 * LISTBOX_RemoveItem
1758 * Remove an item from the listbox and delete its content.
1760 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1762 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1764 /* We need to invalidate the original rect instead of the updated one. */
1765 LISTBOX_InvalidateItems( descr
, index
);
1767 if (descr
->nb_items
== 1)
1769 SendMessageW(descr
->self
, LB_RESETCONTENT
, 0, 0);
1773 LISTBOX_DeleteItem( descr
, index
);
1774 remove_item_data(descr
, index
);
1776 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1777 resize_storage(descr
, descr
->nb_items
);
1779 /* Repaint the items */
1781 LISTBOX_UpdateScroll( descr
);
1782 /* if we removed the scrollbar, reset the top of the list
1783 (correct for owner-drawn ???) */
1784 if (descr
->nb_items
== descr
->page_size
)
1785 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1787 /* Move selection and focused item */
1788 if (!IS_MULTISELECT(descr
))
1790 if (index
== descr
->selected_item
)
1791 descr
->selected_item
= -1;
1792 else if (index
< descr
->selected_item
)
1794 descr
->selected_item
--;
1795 if (ISWIN31
) /* win 31 do not change the selected item number */
1796 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1800 if (descr
->focus_item
>= descr
->nb_items
)
1802 descr
->focus_item
= descr
->nb_items
- 1;
1803 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1809 /***********************************************************************
1810 * LISTBOX_ResetContent
1812 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1816 if (!(descr
->style
& LBS_NODATA
))
1817 for (i
= descr
->nb_items
- 1; i
>= 0; i
--) LISTBOX_DeleteItem(descr
, i
);
1818 HeapFree( GetProcessHeap(), 0, descr
->u
.items
);
1819 descr
->nb_items
= 0;
1820 descr
->top_item
= 0;
1821 descr
->selected_item
= -1;
1822 descr
->focus_item
= 0;
1823 descr
->anchor_item
= -1;
1824 descr
->items_size
= 0;
1825 descr
->u
.items
= NULL
;
1829 /***********************************************************************
1832 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, UINT count
)
1834 UINT orig_num
= descr
->nb_items
;
1836 if (!(descr
->style
& LBS_NODATA
))
1838 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1842 if (!resize_storage(descr
, count
))
1844 descr
->nb_items
= count
;
1848 LISTBOX_UpdateScroll(descr
);
1849 if (count
< orig_num
)
1851 descr
->anchor_item
= min(descr
->anchor_item
, count
- 1);
1852 if (descr
->selected_item
>= count
)
1853 descr
->selected_item
= -1;
1855 /* If we removed the scrollbar, reset the top of the list */
1856 if (count
<= descr
->page_size
&& orig_num
> descr
->page_size
)
1857 LISTBOX_SetTopItem(descr
, 0, TRUE
);
1859 descr
->focus_item
= min(descr
->focus_item
, count
- 1);
1862 /* If it was empty before growing, set focus to the first item */
1863 else if (orig_num
== 0) LISTBOX_SetCaretIndex(descr
, 0, FALSE
);
1865 else SendMessageW(descr
->self
, LB_RESETCONTENT
, 0, 0);
1867 InvalidateRect( descr
->self
, NULL
, TRUE
);
1872 /***********************************************************************
1875 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1876 LPCWSTR filespec
, BOOL long_names
)
1879 LRESULT ret
= LB_OKAY
;
1880 WIN32_FIND_DATAW entry
;
1882 LRESULT maxinsert
= LB_ERR
;
1884 /* don't scan directory if we just want drives exclusively */
1885 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1886 /* scan directory */
1887 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1889 int le
= GetLastError();
1890 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1897 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1899 static const WCHAR bracketW
[] = { ']',0 };
1900 static const WCHAR dotW
[] = { '.',0 };
1901 if (!(attrib
& DDL_DIRECTORY
) ||
1902 !strcmpW( entry
.cFileName
, dotW
)) continue;
1904 if (!long_names
&& entry
.cAlternateFileName
[0])
1905 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1907 strcpyW( buffer
+ 1, entry
.cFileName
);
1908 strcatW(buffer
, bracketW
);
1910 else /* not a directory */
1912 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1913 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1915 if ((attrib
& DDL_EXCLUSIVE
) &&
1916 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1919 if (!long_names
&& entry
.cAlternateFileName
[0])
1920 strcpyW( buffer
, entry
.cAlternateFileName
);
1922 strcpyW( buffer
, entry
.cFileName
);
1924 if (!long_names
) CharLowerW( buffer
);
1925 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1926 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1928 if (ret
<= maxinsert
) maxinsert
++; else maxinsert
= ret
;
1929 } while (FindNextFileW( handle
, &entry
));
1930 FindClose( handle
);
1938 if (attrib
& DDL_DRIVES
)
1940 WCHAR buffer
[] = {'[','-','a','-',']',0};
1941 WCHAR root
[] = {'A',':','\\',0};
1943 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1945 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1946 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1955 /***********************************************************************
1956 * LISTBOX_HandleVScroll
1958 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1962 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1966 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1969 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1972 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1973 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1976 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1977 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1979 case SB_THUMBPOSITION
:
1980 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1983 info
.cbSize
= sizeof(info
);
1984 info
.fMask
= SIF_TRACKPOS
;
1985 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1986 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1989 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1992 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1999 /***********************************************************************
2000 * LISTBOX_HandleHScroll
2002 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
2007 if (descr
->style
& LBS_MULTICOLUMN
)
2012 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
2016 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
2020 page
= descr
->width
/ descr
->column_width
;
2021 if (page
< 1) page
= 1;
2022 LISTBOX_SetTopItem( descr
,
2023 descr
->top_item
- page
* descr
->page_size
, TRUE
);
2026 page
= descr
->width
/ descr
->column_width
;
2027 if (page
< 1) page
= 1;
2028 LISTBOX_SetTopItem( descr
,
2029 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
2031 case SB_THUMBPOSITION
:
2032 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
2035 info
.cbSize
= sizeof(info
);
2036 info
.fMask
= SIF_TRACKPOS
;
2037 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
2038 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
2042 LISTBOX_SetTopItem( descr
, 0, TRUE
);
2045 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
2049 else if (descr
->horz_extent
)
2054 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
2057 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
2060 LISTBOX_SetHorizontalPos( descr
,
2061 descr
->horz_pos
- descr
->width
);
2064 LISTBOX_SetHorizontalPos( descr
,
2065 descr
->horz_pos
+ descr
->width
);
2067 case SB_THUMBPOSITION
:
2068 LISTBOX_SetHorizontalPos( descr
, pos
);
2071 info
.cbSize
= sizeof(info
);
2072 info
.fMask
= SIF_TRACKPOS
;
2073 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
2074 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
2077 LISTBOX_SetHorizontalPos( descr
, 0 );
2080 LISTBOX_SetHorizontalPos( descr
,
2081 descr
->horz_extent
- descr
->width
);
2088 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
2090 INT pulScrollLines
= 3;
2092 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
2094 /* if scrolling changes direction, ignore left overs */
2095 if ((delta
< 0 && descr
->wheel_remain
< 0) ||
2096 (delta
> 0 && descr
->wheel_remain
> 0))
2097 descr
->wheel_remain
+= delta
;
2099 descr
->wheel_remain
= delta
;
2101 if (descr
->wheel_remain
&& pulScrollLines
)
2104 if (descr
->style
& LBS_MULTICOLUMN
)
2106 pulScrollLines
= min(descr
->width
/ descr
->column_width
, pulScrollLines
);
2107 pulScrollLines
= max(1, pulScrollLines
);
2108 cLineScroll
= pulScrollLines
* descr
->wheel_remain
/ WHEEL_DELTA
;
2109 descr
->wheel_remain
-= WHEEL_DELTA
* cLineScroll
/ pulScrollLines
;
2110 cLineScroll
*= descr
->page_size
;
2114 pulScrollLines
= min(descr
->page_size
, pulScrollLines
);
2115 cLineScroll
= pulScrollLines
* descr
->wheel_remain
/ WHEEL_DELTA
;
2116 descr
->wheel_remain
-= WHEEL_DELTA
* cLineScroll
/ pulScrollLines
;
2118 LISTBOX_SetTopItem( descr
, descr
->top_item
- cLineScroll
, TRUE
);
2123 /***********************************************************************
2124 * LISTBOX_HandleLButtonDown
2126 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
2128 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2130 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2131 descr
->self
, x
, y
, index
, descr
->focus_item
);
2133 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2135 if (!descr
->in_focus
)
2137 if( !descr
->lphc
) SetFocus( descr
->self
);
2138 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2141 if (index
== -1) return 0;
2145 if (descr
->style
& LBS_NOTIFY
)
2146 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2147 MAKELPARAM( x
, y
) );
2150 descr
->captured
= TRUE
;
2151 SetCapture( descr
->self
);
2153 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2155 /* we should perhaps make sure that all items are deselected
2156 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2157 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2158 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2161 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2162 if (keys
& MK_CONTROL
)
2164 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2165 LISTBOX_SetSelection( descr
, index
,
2166 !is_item_selected(descr
, index
),
2167 (descr
->style
& LBS_NOTIFY
) != 0);
2171 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2173 if (descr
->style
& LBS_EXTENDEDSEL
)
2175 LISTBOX_SetSelection( descr
, index
,
2176 is_item_selected(descr
, index
),
2177 (descr
->style
& LBS_NOTIFY
) != 0 );
2181 LISTBOX_SetSelection( descr
, index
,
2182 !is_item_selected(descr
, index
),
2183 (descr
->style
& LBS_NOTIFY
) != 0 );
2189 descr
->anchor_item
= index
;
2190 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2191 LISTBOX_SetSelection( descr
, index
,
2192 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2197 if (GetWindowLongW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2204 if (DragDetect( descr
->self
, pt
))
2205 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2212 /*************************************************************************
2213 * LISTBOX_HandleLButtonDownCombo [Internal]
2215 * Process LButtonDown message for the ComboListBox
2218 * pWnd [I] The windows internal structure
2219 * pDescr [I] The ListBox internal structure
2220 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2221 * x [I] X Mouse Coordinate
2222 * y [I] Y Mouse Coordinate
2225 * 0 since we are processing the WM_LBUTTONDOWN Message
2228 * This function is only to be used when a ListBox is a ComboListBox
2231 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2233 RECT clientRect
, screenRect
;
2239 GetClientRect(descr
->self
, &clientRect
);
2241 if(PtInRect(&clientRect
, mousePos
))
2243 /* MousePos is in client, resume normal processing */
2244 if (msg
== WM_LBUTTONDOWN
)
2246 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2247 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2249 else if (descr
->style
& LBS_NOTIFY
)
2250 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2254 POINT screenMousePos
;
2255 HWND hWndOldCapture
;
2257 /* Check the Non-Client Area */
2258 screenMousePos
= mousePos
;
2259 hWndOldCapture
= GetCapture();
2261 GetWindowRect(descr
->self
, &screenRect
);
2262 ClientToScreen(descr
->self
, &screenMousePos
);
2264 if(!PtInRect(&screenRect
, screenMousePos
))
2266 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2267 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2268 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2272 /* Check to see the NC is a scrollbar */
2274 LONG style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2275 /* Check Vertical scroll bar */
2276 if (style
& WS_VSCROLL
)
2278 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2279 if (PtInRect( &clientRect
, mousePos
))
2280 nHitTestType
= HTVSCROLL
;
2282 /* Check horizontal scroll bar */
2283 if (style
& WS_HSCROLL
)
2285 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2286 if (PtInRect( &clientRect
, mousePos
))
2287 nHitTestType
= HTHSCROLL
;
2289 /* Windows sends this message when a scrollbar is clicked
2292 if(nHitTestType
!= 0)
2294 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2295 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2297 /* Resume the Capture after scrolling is complete
2299 if(hWndOldCapture
!= 0)
2300 SetCapture(hWndOldCapture
);
2306 /***********************************************************************
2307 * LISTBOX_HandleLButtonUp
2309 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2311 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2312 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2313 LISTBOX_Timer
= LB_TIMER_NONE
;
2314 if (descr
->captured
)
2316 descr
->captured
= FALSE
;
2317 if (GetCapture() == descr
->self
) ReleaseCapture();
2318 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2319 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2325 /***********************************************************************
2326 * LISTBOX_HandleTimer
2328 * Handle scrolling upon a timer event.
2329 * Return TRUE if scrolling should continue.
2331 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2336 if (descr
->top_item
) index
= descr
->top_item
- 1;
2340 if (descr
->top_item
) index
-= descr
->page_size
;
2343 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2344 if (index
== descr
->focus_item
) index
++;
2345 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2347 case LB_TIMER_RIGHT
:
2348 if (index
+ descr
->page_size
< descr
->nb_items
)
2349 index
+= descr
->page_size
;
2354 if (index
== descr
->focus_item
) return FALSE
;
2355 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2360 /***********************************************************************
2361 * LISTBOX_HandleSystemTimer
2363 * WM_SYSTIMER handler.
2365 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2367 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2369 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2370 LISTBOX_Timer
= LB_TIMER_NONE
;
2376 /***********************************************************************
2377 * LISTBOX_HandleMouseMove
2379 * WM_MOUSEMOVE handler.
2381 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2385 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2387 if (!descr
->captured
) return;
2389 if (descr
->style
& LBS_MULTICOLUMN
)
2392 else if (y
>= descr
->item_height
* descr
->page_size
)
2393 y
= descr
->item_height
* descr
->page_size
- 1;
2397 dir
= LB_TIMER_LEFT
;
2400 else if (x
>= descr
->width
)
2402 dir
= LB_TIMER_RIGHT
;
2403 x
= descr
->width
- 1;
2408 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2409 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2412 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2413 if (index
== -1) index
= descr
->focus_item
;
2414 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2416 /* Start/stop the system timer */
2418 if (dir
!= LB_TIMER_NONE
)
2419 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2420 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2421 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2422 LISTBOX_Timer
= dir
;
2426 /***********************************************************************
2427 * LISTBOX_HandleKeyDown
2429 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2432 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2433 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2434 bForceSelection
= FALSE
; /* only for single select list */
2436 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2438 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2439 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2440 (LPARAM
)descr
->self
);
2441 if (caret
== -2) return 0;
2443 if (caret
== -1) switch(key
)
2446 if (descr
->style
& LBS_MULTICOLUMN
)
2448 bForceSelection
= FALSE
;
2449 if (descr
->focus_item
>= descr
->page_size
)
2450 caret
= descr
->focus_item
- descr
->page_size
;
2455 caret
= descr
->focus_item
- 1;
2456 if (caret
< 0) caret
= 0;
2459 if (descr
->style
& LBS_MULTICOLUMN
)
2461 bForceSelection
= FALSE
;
2462 caret
= min(descr
->focus_item
+ descr
->page_size
, descr
->nb_items
- 1);
2467 caret
= descr
->focus_item
+ 1;
2468 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2472 if (descr
->style
& LBS_MULTICOLUMN
)
2474 INT page
= descr
->width
/ descr
->column_width
;
2475 if (page
< 1) page
= 1;
2476 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2478 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2479 if (caret
< 0) caret
= 0;
2482 if (descr
->style
& LBS_MULTICOLUMN
)
2484 INT page
= descr
->width
/ descr
->column_width
;
2485 if (page
< 1) page
= 1;
2486 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2488 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2489 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2495 caret
= descr
->nb_items
- 1;
2498 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2499 else if (descr
->style
& LBS_MULTIPLESEL
)
2501 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2502 !is_item_selected(descr
, descr
->focus_item
),
2503 (descr
->style
& LBS_NOTIFY
) != 0 );
2507 bForceSelection
= FALSE
;
2509 if (bForceSelection
) /* focused item is used instead of key */
2510 caret
= descr
->focus_item
;
2513 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2514 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2515 !IS_MULTISELECT(descr
))
2516 descr
->anchor_item
= caret
;
2517 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2519 if (descr
->style
& LBS_MULTIPLESEL
)
2520 descr
->selected_item
= caret
;
2522 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2523 if (descr
->style
& LBS_NOTIFY
)
2525 if (descr
->lphc
&& IsWindowVisible( descr
->self
))
2527 /* make sure that combo parent doesn't hide us */
2528 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2530 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2537 /***********************************************************************
2538 * LISTBOX_HandleChar
2540 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2548 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2550 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2551 MAKEWPARAM(charW
, descr
->focus_item
),
2552 (LPARAM
)descr
->self
);
2553 if (caret
== -2) return 0;
2555 if (caret
== -1 && !(descr
->style
& LBS_NODATA
))
2556 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2559 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2560 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2561 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2562 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2563 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2569 /***********************************************************************
2572 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2575 MEASUREITEMSTRUCT mis
;
2578 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2581 GetClientRect( hwnd
, &rect
);
2583 descr
->owner
= GetParent( descr
->self
);
2584 descr
->style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2585 descr
->width
= rect
.right
- rect
.left
;
2586 descr
->height
= rect
.bottom
- rect
.top
;
2587 descr
->u
.items
= NULL
;
2588 descr
->items_size
= 0;
2589 descr
->nb_items
= 0;
2590 descr
->top_item
= 0;
2591 descr
->selected_item
= -1;
2592 descr
->focus_item
= 0;
2593 descr
->anchor_item
= -1;
2594 descr
->item_height
= 1;
2595 descr
->page_size
= 1;
2596 descr
->column_width
= 150;
2597 descr
->horz_extent
= 0;
2598 descr
->horz_pos
= 0;
2601 descr
->wheel_remain
= 0;
2602 descr
->caret_on
= !lphc
;
2603 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2604 descr
->in_focus
= FALSE
;
2605 descr
->captured
= FALSE
;
2607 descr
->locale
= GetUserDefaultLCID();
2612 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2613 descr
->owner
= lphc
->self
;
2616 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2618 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2620 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2621 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2622 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2623 if ((descr
->style
& (LBS_OWNERDRAWFIXED
| LBS_HASSTRINGS
| LBS_SORT
)) != LBS_OWNERDRAWFIXED
)
2624 descr
->style
&= ~LBS_NODATA
;
2625 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2627 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2629 descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2631 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2633 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2634 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2638 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2639 mis
.CtlType
= ODT_LISTBOX
;
2644 mis
.itemHeight
= descr
->item_height
;
2645 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2646 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2650 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2655 /***********************************************************************
2658 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2660 LISTBOX_ResetContent( descr
);
2661 SetWindowLongPtrW( descr
->self
, 0, 0 );
2662 HeapFree( GetProcessHeap(), 0, descr
);
2667 /***********************************************************************
2668 * ListBoxWndProc_common
2670 LRESULT
ListBoxWndProc_common( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2672 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2673 HEADCOMBO
*lphc
= NULL
;
2678 if (!IsWindow(hwnd
)) return 0;
2680 if (msg
== WM_CREATE
)
2682 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2683 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= lpcs
->lpCreateParams
;
2684 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2685 TRACE("creating hwnd %p descr %p\n", hwnd
, (void *)GetWindowLongPtrW( hwnd
, 0 ) );
2688 /* Ignore all other messages before we get a WM_CREATE */
2689 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2690 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2692 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2694 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2695 descr
->self
, SPY_GetMsgName(msg
, descr
->self
), wParam
, lParam
);
2699 case LB_RESETCONTENT
:
2700 LISTBOX_ResetContent( descr
);
2701 LISTBOX_UpdateScroll( descr
);
2702 InvalidateRect( descr
->self
, NULL
, TRUE
);
2709 if(unicode
|| !HAS_STRINGS(descr
))
2710 textW
= (LPWSTR
)lParam
;
2713 LPSTR textA
= (LPSTR
)lParam
;
2714 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2715 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2716 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2720 wParam
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2721 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2722 if (!unicode
&& HAS_STRINGS(descr
))
2723 HeapFree(GetProcessHeap(), 0, textW
);
2727 case LB_INSERTSTRING
:
2731 if(unicode
|| !HAS_STRINGS(descr
))
2732 textW
= (LPWSTR
)lParam
;
2735 LPSTR textA
= (LPSTR
)lParam
;
2736 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2737 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2738 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2742 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2743 if(!unicode
&& HAS_STRINGS(descr
))
2744 HeapFree(GetProcessHeap(), 0, textW
);
2752 if(unicode
|| !HAS_STRINGS(descr
))
2753 textW
= (LPWSTR
)lParam
;
2756 LPSTR textA
= (LPSTR
)lParam
;
2757 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2758 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2759 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2763 wParam
= LISTBOX_FindFileStrPos( descr
, textW
);
2764 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2765 if(!unicode
&& HAS_STRINGS(descr
))
2766 HeapFree(GetProcessHeap(), 0, textW
);
2770 case LB_DELETESTRING
:
2771 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2772 return descr
->nb_items
;
2775 SetLastError(ERROR_INVALID_INDEX
);
2779 case LB_GETITEMDATA
:
2780 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2782 SetLastError(ERROR_INVALID_INDEX
);
2785 return get_item_data(descr
, wParam
);
2787 case LB_SETITEMDATA
:
2788 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2790 SetLastError(ERROR_INVALID_INDEX
);
2793 set_item_data(descr
, wParam
, lParam
);
2794 /* undocumented: returns TRUE, not LB_OKAY (0) */
2798 return descr
->nb_items
;
2801 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, unicode
);
2804 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2806 SetLastError(ERROR_INVALID_INDEX
);
2809 if (!HAS_STRINGS(descr
)) return sizeof(ULONG_PTR
);
2810 if (unicode
) return strlenW(get_item_string(descr
, wParam
));
2811 return WideCharToMultiByte( CP_ACP
, 0, get_item_string(descr
, wParam
),
2812 strlenW(get_item_string(descr
, wParam
)), NULL
, 0, NULL
, NULL
);
2815 if (descr
->nb_items
== 0)
2817 if (!IS_MULTISELECT(descr
))
2818 return descr
->selected_item
;
2819 if (descr
->selected_item
!= -1)
2820 return descr
->selected_item
;
2821 return descr
->focus_item
;
2822 /* otherwise, if the user tries to move the selection with the */
2823 /* arrow keys, we will give the application something to choke on */
2824 case LB_GETTOPINDEX
:
2825 return descr
->top_item
;
2827 case LB_GETITEMHEIGHT
:
2828 return LISTBOX_GetItemHeight( descr
, wParam
);
2830 case LB_SETITEMHEIGHT
:
2831 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2833 case LB_ITEMFROMPOINT
:
2840 /* The hiword of the return value is not a client area
2841 hittest as suggested by MSDN, but rather a hittest on
2842 the returned listbox item. */
2844 if(descr
->nb_items
== 0)
2845 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2847 pt
.x
= (short)LOWORD(lParam
);
2848 pt
.y
= (short)HIWORD(lParam
);
2850 SetRect(&rect
, 0, 0, descr
->width
, descr
->height
);
2852 if(!PtInRect(&rect
, pt
))
2854 pt
.x
= min(pt
.x
, rect
.right
- 1);
2855 pt
.x
= max(pt
.x
, 0);
2856 pt
.y
= min(pt
.y
, rect
.bottom
- 1);
2857 pt
.y
= max(pt
.y
, 0);
2861 index
= LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
);
2865 index
= descr
->nb_items
- 1;
2868 return MAKELONG(index
, hit
? 0 : 1);
2871 case LB_SETCARETINDEX
:
2872 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2873 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2880 case LB_GETCARETINDEX
:
2881 return descr
->focus_item
;
2883 case LB_SETTOPINDEX
:
2884 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2886 case LB_SETCOLUMNWIDTH
:
2887 return LISTBOX_SetColumnWidth( descr
, wParam
);
2889 case LB_GETITEMRECT
:
2890 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2896 if(unicode
|| !HAS_STRINGS(descr
))
2897 textW
= (LPWSTR
)lParam
;
2900 LPSTR textA
= (LPSTR
)lParam
;
2901 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2902 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2903 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2905 ret
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2906 if(!unicode
&& HAS_STRINGS(descr
))
2907 HeapFree(GetProcessHeap(), 0, textW
);
2911 case LB_FINDSTRINGEXACT
:
2915 if(unicode
|| !HAS_STRINGS(descr
))
2916 textW
= (LPWSTR
)lParam
;
2919 LPSTR textA
= (LPSTR
)lParam
;
2920 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2921 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2922 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2924 ret
= LISTBOX_FindString( descr
, wParam
, textW
, TRUE
);
2925 if(!unicode
&& HAS_STRINGS(descr
))
2926 HeapFree(GetProcessHeap(), 0, textW
);
2930 case LB_SELECTSTRING
:
2935 if(HAS_STRINGS(descr
))
2936 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2937 debugstr_a((LPSTR
)lParam
));
2938 if(unicode
|| !HAS_STRINGS(descr
))
2939 textW
= (LPWSTR
)lParam
;
2942 LPSTR textA
= (LPSTR
)lParam
;
2943 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2944 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2945 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2947 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2948 if(!unicode
&& HAS_STRINGS(descr
))
2949 HeapFree(GetProcessHeap(), 0, textW
);
2950 if (index
!= LB_ERR
)
2952 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2953 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2959 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2961 return is_item_selected(descr
, wParam
);
2964 ret
= LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2965 if (ret
!= LB_ERR
&& wParam
)
2967 descr
->anchor_item
= lParam
;
2969 LISTBOX_SetCaretIndex( descr
, lParam
, TRUE
);
2974 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2975 LISTBOX_SetCaretIndex( descr
, wParam
, TRUE
);
2976 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2977 if (ret
!= LB_ERR
) ret
= descr
->selected_item
;
2980 case LB_GETSELCOUNT
:
2981 return LISTBOX_GetSelCount( descr
);
2983 case LB_GETSELITEMS
:
2984 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2986 case LB_SELITEMRANGE
:
2987 if (LOWORD(lParam
) <= HIWORD(lParam
))
2988 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2989 HIWORD(lParam
), wParam
);
2991 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2992 LOWORD(lParam
), wParam
);
2994 case LB_SELITEMRANGEEX
:
2995 if ((INT
)lParam
>= (INT
)wParam
)
2996 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2998 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
3000 case LB_GETHORIZONTALEXTENT
:
3001 return descr
->horz_extent
;
3003 case LB_SETHORIZONTALEXTENT
:
3004 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
3006 case LB_GETANCHORINDEX
:
3007 return descr
->anchor_item
;
3009 case LB_SETANCHORINDEX
:
3010 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
3012 SetLastError(ERROR_INVALID_INDEX
);
3015 descr
->anchor_item
= (INT
)wParam
;
3023 textW
= (LPWSTR
)lParam
;
3026 LPSTR textA
= (LPSTR
)lParam
;
3027 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
3028 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
3029 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
3031 ret
= LISTBOX_Directory( descr
, wParam
, textW
, msg
== LB_DIR
);
3033 HeapFree(GetProcessHeap(), 0, textW
);
3038 return descr
->locale
;
3043 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
3045 ret
= descr
->locale
;
3046 descr
->locale
= (LCID
)wParam
;
3050 case LB_INITSTORAGE
:
3051 return LISTBOX_InitStorage( descr
, wParam
);
3054 return LISTBOX_SetCount( descr
, (INT
)wParam
);
3056 case LB_SETTABSTOPS
:
3057 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
);
3060 if (descr
->caret_on
)
3062 descr
->caret_on
= TRUE
;
3063 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3064 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3068 if (!descr
->caret_on
)
3070 descr
->caret_on
= FALSE
;
3071 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3072 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3075 case LB_GETLISTBOXINFO
:
3076 return descr
->page_size
;
3079 return LISTBOX_Destroy( descr
);
3082 InvalidateRect( descr
->self
, NULL
, TRUE
);
3086 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
3090 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
3092 case WM_PRINTCLIENT
:
3096 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
3097 ret
= LISTBOX_Paint( descr
, hdc
);
3098 if( !wParam
) EndPaint( descr
->self
, &ps
);
3102 LISTBOX_UpdateSize( descr
);
3105 return (LRESULT
)descr
->font
;
3107 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
3108 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
3111 descr
->in_focus
= TRUE
;
3112 descr
->caret_on
= TRUE
;
3113 if (descr
->focus_item
!= -1)
3114 LISTBOX_DrawFocusRect( descr
, TRUE
);
3115 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
3118 LISTBOX_HandleLButtonUp( descr
); /* Release capture if we have it */
3119 descr
->in_focus
= FALSE
;
3120 descr
->wheel_remain
= 0;
3121 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
3122 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3123 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
3126 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3128 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3130 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
3131 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
3132 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
3133 case WM_LBUTTONDOWN
:
3135 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3136 (INT16
)LOWORD(lParam
),
3137 (INT16
)HIWORD(lParam
) );
3138 return LISTBOX_HandleLButtonDown( descr
, wParam
,
3139 (INT16
)LOWORD(lParam
),
3140 (INT16
)HIWORD(lParam
) );
3141 case WM_LBUTTONDBLCLK
:
3143 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3144 (INT16
)LOWORD(lParam
),
3145 (INT16
)HIWORD(lParam
) );
3146 if (descr
->style
& LBS_NOTIFY
)
3147 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
3150 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
3152 BOOL captured
= descr
->captured
;
3156 mousePos
.x
= (INT16
)LOWORD(lParam
);
3157 mousePos
.y
= (INT16
)HIWORD(lParam
);
3160 * If we are in a dropdown combobox, we simulate that
3161 * the mouse is captured to show the tracking of the item.
3163 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
3164 descr
->captured
= TRUE
;
3166 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
3168 descr
->captured
= captured
;
3170 else if (GetCapture() == descr
->self
)
3172 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
3173 (INT16
)HIWORD(lParam
) );
3183 * If the mouse button "up" is not in the listbox,
3184 * we make sure there is no selection by re-selecting the
3185 * item that was selected when the listbox was made visible.
3187 mousePos
.x
= (INT16
)LOWORD(lParam
);
3188 mousePos
.y
= (INT16
)HIWORD(lParam
);
3190 GetClientRect(descr
->self
, &clientRect
);
3193 * When the user clicks outside the combobox and the focus
3194 * is lost, the owning combobox will send a fake buttonup with
3195 * 0xFFFFFFF as the mouse location, we must also revert the
3196 * selection to the original selection.
3198 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
3199 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
3201 return LISTBOX_HandleLButtonUp( descr
);
3203 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3205 /* for some reason Windows makes it possible to
3206 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3208 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3209 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3210 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3212 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3216 return LISTBOX_HandleKeyDown( descr
, wParam
);
3221 charW
= (WCHAR
)wParam
;
3224 CHAR charA
= (CHAR
)wParam
;
3225 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
3227 return LISTBOX_HandleChar( descr
, charW
);
3230 return LISTBOX_HandleSystemTimer( descr
);
3232 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3235 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3236 wParam
, (LPARAM
)descr
->self
);
3237 TRACE("hbrush = %p\n", hbrush
);
3239 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3242 GetClientRect(descr
->self
, &rect
);
3243 FillRect((HDC
)wParam
, &rect
, hbrush
);
3248 if( lphc
) return 0;
3249 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3250 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3253 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3262 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3263 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3264 hwnd
, msg
, wParam
, lParam
);
3267 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3268 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3271 DWORD WINAPI
GetListBoxInfo(HWND hwnd
)
3273 TRACE("%p\n", hwnd
);
3274 return SendMessageW(hwnd
, LB_GETLISTBOXINFO
, 0, 0);