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
22 * This code was audited for completeness against the documented features
23 * of Comctl32.dll version 6.0 on Oct. 9, 2004, by Dimitrie O. Paun.
25 * Unless otherwise noted, we believe this code to be complete, as per
26 * the specification mentioned above.
27 * If you discover missing features, or bugs, please note them below.
40 #include "wine/unicode.h"
41 #include "user_private.h"
43 #include "wine/exception.h"
44 #include "wine/debug.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(listbox
);
48 /* Items array granularity */
49 #define LB_ARRAY_GRANULARITY 16
51 /* Scrolling timeout in ms */
52 #define LB_SCROLL_TIMEOUT 50
54 /* Listbox system timer id */
57 /* flag listbox changed while setredraw false - internal style */
58 #define LBS_DISPLAYCHANGED 0x80000000
63 LPWSTR str
; /* Item text */
64 BOOL selected
; /* Is item selected? */
65 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
66 ULONG_PTR data
; /* User data */
69 /* Listbox structure */
72 HWND self
; /* Our own window handle */
73 HWND owner
; /* Owner window to send notifications to */
74 UINT style
; /* Window style */
75 INT width
; /* Window width */
76 INT height
; /* Window height */
77 LB_ITEMDATA
*items
; /* Array of items */
78 INT nb_items
; /* Number of items */
79 INT top_item
; /* Top visible item */
80 INT selected_item
; /* Selected item */
81 INT focus_item
; /* Item that has the focus */
82 INT anchor_item
; /* Anchor item for extended selection */
83 INT item_height
; /* Default item height */
84 INT page_size
; /* Items per listbox page */
85 INT column_width
; /* Column width for multi-column listboxes */
86 INT horz_extent
; /* Horizontal extent */
87 INT horz_pos
; /* Horizontal position */
88 INT nb_tabs
; /* Number of tabs in array */
89 INT
*tabs
; /* Array of tabs */
90 INT avg_char_width
; /* Average width of characters */
91 INT wheel_remain
; /* Left over scroll amount */
92 BOOL caret_on
; /* Is caret on? */
93 BOOL captured
; /* Is mouse captured? */
95 HFONT font
; /* Current font */
96 LCID locale
; /* Current locale for string comparisons */
97 LPHEADCOMBO lphc
; /* ComboLBox */
101 #define IS_OWNERDRAW(descr) \
102 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
104 #define HAS_STRINGS(descr) \
105 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
108 #define IS_MULTISELECT(descr) \
109 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
110 !((descr)->style & LBS_NOSEL))
112 #define SEND_NOTIFICATION(descr,code) \
113 (SendMessageW( (descr)->owner, WM_COMMAND, \
114 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
116 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
118 /* Current timer status */
128 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
130 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
);
132 /*********************************************************************
133 * listbox class descriptor
135 static const WCHAR listboxW
[] = {'L','i','s','t','B','o','x',0};
136 const struct builtin_class_descr LISTBOX_builtin_class
=
139 CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
140 WINPROC_LISTBOX
, /* proc */
141 sizeof(LB_DESCR
*), /* extra */
142 IDC_ARROW
, /* cursor */
147 /*********************************************************************
148 * combolbox class descriptor
150 static const WCHAR combolboxW
[] = {'C','o','m','b','o','L','B','o','x',0};
151 const struct builtin_class_descr COMBOLBOX_builtin_class
=
153 combolboxW
, /* name */
154 CS_DBLCLKS
| CS_SAVEBITS
, /* style */
155 WINPROC_LISTBOX
, /* proc */
156 sizeof(LB_DESCR
*), /* extra */
157 IDC_ARROW
, /* cursor */
162 /***********************************************************************
163 * LISTBOX_GetCurrentPageSize
165 * Return the current page size
167 static INT
LISTBOX_GetCurrentPageSize( const LB_DESCR
*descr
)
170 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
171 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
173 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
175 if (i
== descr
->top_item
) return 1;
176 else return i
- descr
->top_item
;
180 /***********************************************************************
181 * LISTBOX_GetMaxTopIndex
183 * Return the maximum possible index for the top of the listbox.
185 static INT
LISTBOX_GetMaxTopIndex( const LB_DESCR
*descr
)
189 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
191 page
= descr
->height
;
192 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
193 if ((page
-= descr
->items
[max
].height
) < 0) break;
194 if (max
< descr
->nb_items
- 1) max
++;
196 else if (descr
->style
& LBS_MULTICOLUMN
)
198 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
199 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
200 max
= (max
- page
) * descr
->page_size
;
204 max
= descr
->nb_items
- descr
->page_size
;
206 if (max
< 0) max
= 0;
211 /***********************************************************************
212 * LISTBOX_UpdateScroll
214 * Update the scrollbars. Should be called whenever the content
215 * of the listbox changes.
217 static void LISTBOX_UpdateScroll( LB_DESCR
*descr
)
221 /* Check the listbox scroll bar flags individually before we call
222 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
223 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
224 scroll bar when we do not need one.
225 if (!(descr->style & WS_VSCROLL)) return;
228 /* It is important that we check descr->style, and not wnd->dwStyle,
229 for WS_VSCROLL, as the former is exactly the one passed in
230 argument to CreateWindow.
231 In Windows (and from now on in Wine :) a listbox created
232 with such a style (no WS_SCROLL) does not update
233 the scrollbar with listbox-related data, thus letting
234 the programmer use it for his/her own purposes. */
236 if (descr
->style
& LBS_NOREDRAW
) return;
237 info
.cbSize
= sizeof(info
);
239 if (descr
->style
& LBS_MULTICOLUMN
)
242 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
243 info
.nPos
= descr
->top_item
/ descr
->page_size
;
244 info
.nPage
= descr
->width
/ descr
->column_width
;
245 if (info
.nPage
< 1) info
.nPage
= 1;
246 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
247 if (descr
->style
& LBS_DISABLENOSCROLL
)
248 info
.fMask
|= SIF_DISABLENOSCROLL
;
249 if (descr
->style
& WS_HSCROLL
)
250 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
252 info
.fMask
= SIF_RANGE
;
253 if (descr
->style
& WS_VSCROLL
)
254 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
259 info
.nMax
= descr
->nb_items
- 1;
260 info
.nPos
= descr
->top_item
;
261 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
262 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
263 if (descr
->style
& LBS_DISABLENOSCROLL
)
264 info
.fMask
|= SIF_DISABLENOSCROLL
;
265 if (descr
->style
& WS_VSCROLL
)
266 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
268 if (descr
->style
& WS_HSCROLL
)
270 info
.nPos
= descr
->horz_pos
;
271 info
.nPage
= descr
->width
;
272 info
.fMask
= SIF_POS
| SIF_PAGE
;
273 if (descr
->style
& LBS_DISABLENOSCROLL
)
274 info
.fMask
|= SIF_DISABLENOSCROLL
;
275 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
281 /***********************************************************************
284 * Set the top item of the listbox, scrolling up or down if necessary.
286 static LRESULT
LISTBOX_SetTopItem( LB_DESCR
*descr
, INT index
, BOOL scroll
)
288 INT max
= LISTBOX_GetMaxTopIndex( descr
);
290 TRACE("setting top item %d, scroll %d\n", index
, scroll
);
292 if (index
> max
) index
= max
;
293 if (index
< 0) index
= 0;
294 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
295 if (descr
->top_item
== index
) return LB_OKAY
;
299 if (descr
->style
& LBS_MULTICOLUMN
)
300 diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
301 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
305 if (index
> descr
->top_item
)
307 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
308 diff
-= descr
->items
[i
].height
;
312 for (i
= index
; i
< descr
->top_item
; i
++)
313 diff
+= descr
->items
[i
].height
;
317 diff
= (descr
->top_item
- index
) * descr
->item_height
;
319 ScrollWindowEx( descr
->self
, 0, diff
, NULL
, NULL
, 0, NULL
,
320 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
322 if (!scroll
) InvalidateRect( descr
->self
, NULL
, TRUE
);
323 descr
->top_item
= index
;
324 LISTBOX_UpdateScroll( descr
);
329 /***********************************************************************
332 * Update the page size. Should be called when the size of
333 * the client area or the item height changes.
335 static void LISTBOX_UpdatePage( LB_DESCR
*descr
)
339 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
341 if (page_size
== descr
->page_size
) return;
342 descr
->page_size
= page_size
;
343 if (descr
->style
& LBS_MULTICOLUMN
)
344 InvalidateRect( descr
->self
, NULL
, TRUE
);
345 LISTBOX_SetTopItem( descr
, descr
->top_item
, FALSE
);
349 /***********************************************************************
352 * Update the size of the listbox. Should be called when the size of
353 * the client area changes.
355 static void LISTBOX_UpdateSize( LB_DESCR
*descr
)
359 GetClientRect( descr
->self
, &rect
);
360 descr
->width
= rect
.right
- rect
.left
;
361 descr
->height
= rect
.bottom
- rect
.top
;
362 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
367 GetWindowRect( descr
->self
, &rect
);
368 if(descr
->item_height
!= 0)
369 remaining
= descr
->height
% descr
->item_height
;
372 if ((descr
->height
> descr
->item_height
) && remaining
)
374 TRACE("[%p]: changing height %d -> %d\n",
375 descr
->self
, descr
->height
, descr
->height
- remaining
);
376 SetWindowPos( descr
->self
, 0, 0, 0, rect
.right
- rect
.left
,
377 rect
.bottom
- rect
.top
- remaining
,
378 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
382 TRACE("[%p]: new size = %d,%d\n", descr
->self
, descr
->width
, descr
->height
);
383 LISTBOX_UpdatePage( descr
);
384 LISTBOX_UpdateScroll( descr
);
386 /* Invalidate the focused item so it will be repainted correctly */
387 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
389 InvalidateRect( descr
->self
, &rect
, FALSE
);
394 /***********************************************************************
395 * LISTBOX_GetItemRect
397 * Get the rectangle enclosing an item, in listbox client coordinates.
398 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
400 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
)
402 /* Index <= 0 is legal even on empty listboxes */
403 if (index
&& (index
>= descr
->nb_items
))
405 memset(rect
, 0, sizeof(*rect
));
406 SetLastError(ERROR_INVALID_INDEX
);
409 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
410 if (descr
->style
& LBS_MULTICOLUMN
)
412 INT col
= (index
/ descr
->page_size
) -
413 (descr
->top_item
/ descr
->page_size
);
414 rect
->left
+= col
* descr
->column_width
;
415 rect
->right
= rect
->left
+ descr
->column_width
;
416 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
417 rect
->bottom
= rect
->top
+ descr
->item_height
;
419 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
422 rect
->right
+= descr
->horz_pos
;
423 if ((index
>= 0) && (index
< descr
->nb_items
))
425 if (index
< descr
->top_item
)
427 for (i
= descr
->top_item
-1; i
>= index
; i
--)
428 rect
->top
-= descr
->items
[i
].height
;
432 for (i
= descr
->top_item
; i
< index
; i
++)
433 rect
->top
+= descr
->items
[i
].height
;
435 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
441 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
442 rect
->bottom
= rect
->top
+ descr
->item_height
;
443 rect
->right
+= descr
->horz_pos
;
446 TRACE("item %d, rect %s\n", index
, wine_dbgstr_rect(rect
));
448 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
449 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
453 /***********************************************************************
454 * LISTBOX_GetItemFromPoint
456 * Return the item nearest from point (x,y) (in client coordinates).
458 static INT
LISTBOX_GetItemFromPoint( const LB_DESCR
*descr
, INT x
, INT y
)
460 INT index
= descr
->top_item
;
462 if (!descr
->nb_items
) return -1; /* No items */
463 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
468 while (index
< descr
->nb_items
)
470 if ((pos
+= descr
->items
[index
].height
) > y
) break;
479 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
483 else if (descr
->style
& LBS_MULTICOLUMN
)
485 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
486 if (y
>= 0) index
+= y
/ descr
->item_height
;
487 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
488 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
492 index
+= (y
/ descr
->item_height
);
494 if (index
< 0) return 0;
495 if (index
>= descr
->nb_items
) return -1;
500 /***********************************************************************
505 static void LISTBOX_PaintItem( LB_DESCR
*descr
, HDC hdc
, const RECT
*rect
,
506 INT index
, UINT action
, BOOL ignoreFocus
)
508 LB_ITEMDATA
*item
= NULL
;
509 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
511 if (IS_OWNERDRAW(descr
))
519 if (action
== ODA_FOCUS
)
520 DrawFocusRect( hdc
, rect
);
522 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
526 /* some programs mess with the clipping region when
527 drawing the item, *and* restore the previous region
528 after they are done, so a region has better to exist
529 else everything ends clipped */
530 GetClientRect(descr
->self
, &r
);
531 hrgn
= set_control_clipping( hdc
, &r
);
533 dis
.CtlType
= ODT_LISTBOX
;
534 dis
.CtlID
= GetWindowLongPtrW( descr
->self
, GWLP_ID
);
535 dis
.hwndItem
= descr
->self
;
536 dis
.itemAction
= action
;
540 if (item
->selected
) dis
.itemState
|= ODS_SELECTED
;
541 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
543 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
544 if (!IsWindowEnabled(descr
->self
)) dis
.itemState
|= ODS_DISABLED
;
545 dis
.itemData
= item
->data
;
547 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
548 descr
->self
, index
, debugstr_w(item
->str
), action
,
549 dis
.itemState
, wine_dbgstr_rect(rect
) );
550 SendMessageW(descr
->owner
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
551 SelectClipRgn( hdc
, hrgn
);
552 if (hrgn
) DeleteObject( hrgn
);
556 COLORREF oldText
= 0, oldBk
= 0;
558 if (action
== ODA_FOCUS
)
560 DrawFocusRect( hdc
, rect
);
563 if (item
&& item
->selected
)
565 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
566 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
569 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
570 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
571 wine_dbgstr_rect(rect
) );
573 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
574 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
575 else if (!(descr
->style
& LBS_USETABSTOPS
))
576 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
577 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
578 strlenW(item
->str
), NULL
);
581 /* Output empty string to paint background in the full width. */
582 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
583 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
584 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
585 item
->str
, strlenW(item
->str
),
586 descr
->nb_tabs
, descr
->tabs
, 0);
588 if (item
&& item
->selected
)
590 SetBkColor( hdc
, oldBk
);
591 SetTextColor( hdc
, oldText
);
593 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
595 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
600 /***********************************************************************
603 * Change the redraw flag.
605 static void LISTBOX_SetRedraw( LB_DESCR
*descr
, BOOL on
)
609 if (!(descr
->style
& LBS_NOREDRAW
)) return;
610 descr
->style
&= ~LBS_NOREDRAW
;
611 if (descr
->style
& LBS_DISPLAYCHANGED
)
612 { /* page was changed while setredraw false, refresh automatically */
613 InvalidateRect(descr
->self
, NULL
, TRUE
);
614 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
615 { /* reset top of page if less than number of items/page */
616 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
617 if (descr
->top_item
< 0) descr
->top_item
= 0;
619 descr
->style
&= ~LBS_DISPLAYCHANGED
;
621 LISTBOX_UpdateScroll( descr
);
623 else descr
->style
|= LBS_NOREDRAW
;
627 /***********************************************************************
628 * LISTBOX_RepaintItem
630 * Repaint a single item synchronously.
632 static void LISTBOX_RepaintItem( LB_DESCR
*descr
, INT index
, UINT action
)
637 HBRUSH hbrush
, oldBrush
= 0;
639 /* Do not repaint the item if the item is not visible */
640 if (!IsWindowVisible(descr
->self
)) return;
641 if (descr
->style
& LBS_NOREDRAW
)
643 descr
->style
|= LBS_DISPLAYCHANGED
;
646 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
647 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
648 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
649 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
650 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
651 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
652 if (!IsWindowEnabled(descr
->self
))
653 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
654 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
655 LISTBOX_PaintItem( descr
, hdc
, &rect
, index
, action
, TRUE
);
656 if (oldFont
) SelectObject( hdc
, oldFont
);
657 if (oldBrush
) SelectObject( hdc
, oldBrush
);
658 ReleaseDC( descr
->self
, hdc
);
662 /***********************************************************************
663 * LISTBOX_DrawFocusRect
665 static void LISTBOX_DrawFocusRect( LB_DESCR
*descr
, BOOL on
)
671 /* Do not repaint the item if the item is not visible */
672 if (!IsWindowVisible(descr
->self
)) return;
674 if (descr
->focus_item
== -1) return;
675 if (!descr
->caret_on
|| !descr
->in_focus
) return;
677 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) != 1) return;
678 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
679 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
680 if (!IsWindowEnabled(descr
->self
))
681 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
682 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
683 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, !on
);
684 if (oldFont
) SelectObject( hdc
, oldFont
);
685 ReleaseDC( descr
->self
, hdc
);
689 /***********************************************************************
690 * LISTBOX_InitStorage
692 static LRESULT
LISTBOX_InitStorage( LB_DESCR
*descr
, INT nb_items
)
696 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
697 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
699 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
700 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
701 nb_items
* sizeof(LB_ITEMDATA
));
704 item
= HeapAlloc( GetProcessHeap(), 0,
705 nb_items
* sizeof(LB_ITEMDATA
));
710 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
718 /***********************************************************************
719 * LISTBOX_SetTabStops
721 static BOOL
LISTBOX_SetTabStops( LB_DESCR
*descr
, INT count
, LPINT tabs
)
725 if (!(descr
->style
& LBS_USETABSTOPS
))
727 SetLastError(ERROR_LB_WITHOUT_TABSTOPS
);
731 HeapFree( GetProcessHeap(), 0, descr
->tabs
);
732 if (!(descr
->nb_tabs
= count
))
737 if (!(descr
->tabs
= HeapAlloc( GetProcessHeap(), 0,
738 descr
->nb_tabs
* sizeof(INT
) )))
740 memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
742 /* convert into "dialog units"*/
743 for (i
= 0; i
< descr
->nb_tabs
; i
++)
744 descr
->tabs
[i
] = MulDiv(descr
->tabs
[i
], descr
->avg_char_width
, 4);
750 /***********************************************************************
753 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPWSTR buffer
, BOOL unicode
)
757 if ((index
< 0) || (index
>= descr
->nb_items
))
759 SetLastError(ERROR_INVALID_INDEX
);
762 if (HAS_STRINGS(descr
))
766 len
= strlenW(descr
->items
[index
].str
);
769 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[index
].str
, len
,
770 NULL
, 0, NULL
, NULL
);
773 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
775 __TRY
/* hide a Delphi bug that passes a read-only buffer */
779 strcpyW( buffer
, descr
->items
[index
].str
);
780 len
= strlenW(buffer
);
784 len
= WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1,
785 (LPSTR
)buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
790 WARN( "got an invalid buffer (Delphi bug?)\n" );
791 SetLastError( ERROR_INVALID_PARAMETER
);
797 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
803 static inline INT
LISTBOX_lstrcmpiW( LCID lcid
, LPCWSTR str1
, LPCWSTR str2
)
805 INT ret
= CompareStringW( lcid
, NORM_IGNORECASE
, str1
, -1, str2
, -1 );
806 if (ret
== CSTR_LESS_THAN
)
808 if (ret
== CSTR_EQUAL
)
810 if (ret
== CSTR_GREATER_THAN
)
815 /***********************************************************************
816 * LISTBOX_FindStringPos
818 * Find the nearest string located before a given string in sort order.
819 * If 'exact' is TRUE, return an error if we don't get an exact match.
821 static INT
LISTBOX_FindStringPos( LB_DESCR
*descr
, LPCWSTR str
, BOOL exact
)
823 INT index
, min
, max
, res
= -1;
825 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
827 max
= descr
->nb_items
;
830 index
= (min
+ max
) / 2;
831 if (HAS_STRINGS(descr
))
832 res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, descr
->items
[index
].str
);
835 COMPAREITEMSTRUCT cis
;
836 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
838 cis
.CtlType
= ODT_LISTBOX
;
840 cis
.hwndItem
= descr
->self
;
841 /* note that some application (MetaStock) expects the second item
842 * to be in the listbox */
844 cis
.itemData1
= (ULONG_PTR
)str
;
846 cis
.itemData2
= descr
->items
[index
].data
;
847 cis
.dwLocaleId
= descr
->locale
;
848 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
850 if (!res
) return index
;
851 if (res
< 0) max
= index
;
852 else min
= index
+ 1;
854 return exact
? -1 : max
;
858 /***********************************************************************
859 * LISTBOX_FindFileStrPos
861 * Find the nearest string located before a given string in directory
862 * sort order (i.e. first files, then directories, then drives).
864 static INT
LISTBOX_FindFileStrPos( LB_DESCR
*descr
, LPCWSTR str
)
866 INT min
, max
, res
= -1;
868 if (!HAS_STRINGS(descr
))
869 return LISTBOX_FindStringPos( descr
, str
, FALSE
);
871 max
= descr
->nb_items
;
874 INT index
= (min
+ max
) / 2;
875 LPCWSTR p
= descr
->items
[index
].str
;
876 if (*p
== '[') /* drive or directory */
878 if (*str
!= '[') res
= -1;
879 else if (p
[1] == '-') /* drive */
881 if (str
[1] == '-') res
= str
[2] - p
[2];
886 if (str
[1] == '-') res
= 1;
887 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
892 if (*str
== '[') res
= 1;
893 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
895 if (!res
) return index
;
896 if (res
< 0) max
= index
;
897 else min
= index
+ 1;
903 /***********************************************************************
906 * Find the item beginning with a given string.
908 static INT
LISTBOX_FindString( LB_DESCR
*descr
, INT start
, LPCWSTR str
, BOOL exact
)
913 if (start
>= descr
->nb_items
) start
= -1;
914 item
= descr
->items
+ start
+ 1;
915 if (HAS_STRINGS(descr
))
917 if (!str
|| ! str
[0] ) return LB_ERR
;
920 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
921 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
922 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
923 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
927 /* Special case for drives and directories: ignore prefix */
928 #define CHECK_DRIVE(item) \
929 if ((item)->str[0] == '[') \
931 if (!strncmpiW( str, (item)->str+1, len )) return i; \
932 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
936 INT len
= strlenW(str
);
937 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
939 if (!strncmpiW( str
, item
->str
, len
)) return i
;
942 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
944 if (!strncmpiW( str
, item
->str
, len
)) return i
;
952 if (exact
&& (descr
->style
& LBS_SORT
))
953 /* If sorted, use a WM_COMPAREITEM binary search */
954 return LISTBOX_FindStringPos( descr
, str
, TRUE
);
956 /* Otherwise use a linear search */
957 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
958 if (item
->data
== (ULONG_PTR
)str
) return i
;
959 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
960 if (item
->data
== (ULONG_PTR
)str
) return i
;
966 /***********************************************************************
967 * LISTBOX_GetSelCount
969 static LRESULT
LISTBOX_GetSelCount( const LB_DESCR
*descr
)
972 const LB_ITEMDATA
*item
= descr
->items
;
974 if (!(descr
->style
& LBS_MULTIPLESEL
) ||
975 (descr
->style
& LBS_NOSEL
))
977 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
978 if (item
->selected
) count
++;
983 /***********************************************************************
984 * LISTBOX_GetSelItems
986 static LRESULT
LISTBOX_GetSelItems( const LB_DESCR
*descr
, INT max
, LPINT array
)
989 const LB_ITEMDATA
*item
= descr
->items
;
991 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
992 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
993 if (item
->selected
) array
[count
++] = i
;
998 /***********************************************************************
1001 static LRESULT
LISTBOX_Paint( LB_DESCR
*descr
, HDC hdc
)
1003 INT i
, col_pos
= descr
->page_size
- 1;
1005 RECT focusRect
= {-1, -1, -1, -1};
1007 HBRUSH hbrush
, oldBrush
= 0;
1009 if (descr
->style
& LBS_NOREDRAW
) return 0;
1011 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1012 if (descr
->style
& LBS_MULTICOLUMN
)
1013 rect
.right
= rect
.left
+ descr
->column_width
;
1014 else if (descr
->horz_pos
)
1016 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1017 rect
.right
+= descr
->horz_pos
;
1020 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1021 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
1022 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
1023 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1024 if (!IsWindowEnabled(descr
->self
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1026 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1029 /* Special case for empty listbox: paint focus rect */
1030 rect
.bottom
= rect
.top
+ descr
->item_height
;
1031 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1032 &rect
, NULL
, 0, NULL
);
1033 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1034 rect
.top
= rect
.bottom
;
1037 /* Paint all the item, regarding the selection
1038 Focus state will be painted after */
1040 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1042 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1043 rect
.bottom
= rect
.top
+ descr
->item_height
;
1045 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1047 if (i
== descr
->focus_item
)
1049 /* keep the focus rect, to paint the focus item after */
1050 focusRect
.left
= rect
.left
;
1051 focusRect
.right
= rect
.right
;
1052 focusRect
.top
= rect
.top
;
1053 focusRect
.bottom
= rect
.bottom
;
1055 LISTBOX_PaintItem( descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1056 rect
.top
= rect
.bottom
;
1058 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1060 if (!IS_OWNERDRAW(descr
))
1062 /* Clear the bottom of the column */
1063 if (rect
.top
< descr
->height
)
1065 rect
.bottom
= descr
->height
;
1066 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1067 &rect
, NULL
, 0, NULL
);
1071 /* Go to the next column */
1072 rect
.left
+= descr
->column_width
;
1073 rect
.right
+= descr
->column_width
;
1075 col_pos
= descr
->page_size
- 1;
1080 if (rect
.top
>= descr
->height
) break;
1084 /* Paint the focus item now */
1085 if (focusRect
.top
!= focusRect
.bottom
&&
1086 descr
->caret_on
&& descr
->in_focus
)
1087 LISTBOX_PaintItem( descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1089 if (!IS_OWNERDRAW(descr
))
1091 /* Clear the remainder of the client area */
1092 if (rect
.top
< descr
->height
)
1094 rect
.bottom
= descr
->height
;
1095 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1096 &rect
, NULL
, 0, NULL
);
1098 if (rect
.right
< descr
->width
)
1100 rect
.left
= rect
.right
;
1101 rect
.right
= descr
->width
;
1103 rect
.bottom
= descr
->height
;
1104 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1105 &rect
, NULL
, 0, NULL
);
1108 if (oldFont
) SelectObject( hdc
, oldFont
);
1109 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1114 /***********************************************************************
1115 * LISTBOX_InvalidateItems
1117 * Invalidate all items from a given item. If the specified item is not
1118 * visible, nothing happens.
1120 static void LISTBOX_InvalidateItems( LB_DESCR
*descr
, INT index
)
1124 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1126 if (descr
->style
& LBS_NOREDRAW
)
1128 descr
->style
|= LBS_DISPLAYCHANGED
;
1131 rect
.bottom
= descr
->height
;
1132 InvalidateRect( descr
->self
, &rect
, TRUE
);
1133 if (descr
->style
& LBS_MULTICOLUMN
)
1135 /* Repaint the other columns */
1136 rect
.left
= rect
.right
;
1137 rect
.right
= descr
->width
;
1139 InvalidateRect( descr
->self
, &rect
, TRUE
);
1144 static void LISTBOX_InvalidateItemRect( LB_DESCR
*descr
, INT index
)
1148 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1149 InvalidateRect( descr
->self
, &rect
, TRUE
);
1152 /***********************************************************************
1153 * LISTBOX_GetItemHeight
1155 static LRESULT
LISTBOX_GetItemHeight( const LB_DESCR
*descr
, INT index
)
1157 if (descr
->style
& LBS_OWNERDRAWVARIABLE
&& descr
->nb_items
> 0)
1159 if ((index
< 0) || (index
>= descr
->nb_items
))
1161 SetLastError(ERROR_INVALID_INDEX
);
1164 return descr
->items
[index
].height
;
1166 else return descr
->item_height
;
1170 /***********************************************************************
1171 * LISTBOX_SetItemHeight
1173 static LRESULT
LISTBOX_SetItemHeight( LB_DESCR
*descr
, INT index
, INT height
, BOOL repaint
)
1175 if (height
> MAXBYTE
)
1178 if (!height
) height
= 1;
1180 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1182 if ((index
< 0) || (index
>= descr
->nb_items
))
1184 SetLastError(ERROR_INVALID_INDEX
);
1187 TRACE("[%p]: item %d height = %d\n", descr
->self
, index
, height
);
1188 descr
->items
[index
].height
= height
;
1189 LISTBOX_UpdateScroll( descr
);
1191 LISTBOX_InvalidateItems( descr
, index
);
1193 else if (height
!= descr
->item_height
)
1195 TRACE("[%p]: new height = %d\n", descr
->self
, height
);
1196 descr
->item_height
= height
;
1197 LISTBOX_UpdatePage( descr
);
1198 LISTBOX_UpdateScroll( descr
);
1200 InvalidateRect( descr
->self
, 0, TRUE
);
1206 /***********************************************************************
1207 * LISTBOX_SetHorizontalPos
1209 static void LISTBOX_SetHorizontalPos( LB_DESCR
*descr
, INT pos
)
1213 if (pos
> descr
->horz_extent
- descr
->width
)
1214 pos
= descr
->horz_extent
- descr
->width
;
1215 if (pos
< 0) pos
= 0;
1216 if (!(diff
= descr
->horz_pos
- pos
)) return;
1217 TRACE("[%p]: new horz pos = %d\n", descr
->self
, pos
);
1218 descr
->horz_pos
= pos
;
1219 LISTBOX_UpdateScroll( descr
);
1220 if (abs(diff
) < descr
->width
)
1223 /* Invalidate the focused item so it will be repainted correctly */
1224 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1225 InvalidateRect( descr
->self
, &rect
, TRUE
);
1226 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
1227 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1230 InvalidateRect( descr
->self
, NULL
, TRUE
);
1234 /***********************************************************************
1235 * LISTBOX_SetHorizontalExtent
1237 static LRESULT
LISTBOX_SetHorizontalExtent( LB_DESCR
*descr
, INT extent
)
1239 if (descr
->style
& LBS_MULTICOLUMN
)
1241 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1242 TRACE("[%p]: new horz extent = %d\n", descr
->self
, extent
);
1243 descr
->horz_extent
= extent
;
1244 if (descr
->style
& WS_HSCROLL
) {
1246 info
.cbSize
= sizeof(info
);
1248 info
.nMax
= descr
->horz_extent
? descr
->horz_extent
- 1 : 0;
1249 info
.fMask
= SIF_RANGE
;
1250 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
1252 if (descr
->horz_pos
> extent
- descr
->width
)
1253 LISTBOX_SetHorizontalPos( descr
, extent
- descr
->width
);
1258 /***********************************************************************
1259 * LISTBOX_SetColumnWidth
1261 static LRESULT
LISTBOX_SetColumnWidth( LB_DESCR
*descr
, INT width
)
1263 if (width
== descr
->column_width
) return LB_OKAY
;
1264 TRACE("[%p]: new column width = %d\n", descr
->self
, width
);
1265 descr
->column_width
= width
;
1266 LISTBOX_UpdatePage( descr
);
1271 /***********************************************************************
1274 * Returns the item height.
1276 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1280 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1285 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1287 ERR("unable to get DC.\n" );
1290 if (font
) oldFont
= SelectObject( hdc
, font
);
1291 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1292 if (oldFont
) SelectObject( hdc
, oldFont
);
1293 ReleaseDC( descr
->self
, hdc
);
1295 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1296 if (!IS_OWNERDRAW(descr
))
1297 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1302 /***********************************************************************
1303 * LISTBOX_MakeItemVisible
1305 * Make sure that a given item is partially or fully visible.
1307 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1311 TRACE("current top item %d, index %d, fully %d\n", descr
->top_item
, index
, fully
);
1313 if (index
<= descr
->top_item
) top
= index
;
1314 else if (descr
->style
& LBS_MULTICOLUMN
)
1316 INT cols
= descr
->width
;
1317 if (!fully
) cols
+= descr
->column_width
- 1;
1318 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1320 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1321 top
= index
- descr
->page_size
* (cols
- 1);
1323 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1325 INT height
= fully
? descr
->items
[index
].height
: 1;
1326 for (top
= index
; top
> descr
->top_item
; top
--)
1327 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1331 if (index
< descr
->top_item
+ descr
->page_size
) return;
1332 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1333 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1334 top
= index
- descr
->page_size
+ 1;
1336 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1339 /***********************************************************************
1340 * LISTBOX_SetCaretIndex
1343 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1346 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1348 INT oldfocus
= descr
->focus_item
;
1350 TRACE("old focus %d, index %d\n", oldfocus
, index
);
1352 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1353 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1354 if (index
== oldfocus
) return LB_OKAY
;
1356 LISTBOX_DrawFocusRect( descr
, FALSE
);
1357 descr
->focus_item
= index
;
1359 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1360 LISTBOX_DrawFocusRect( descr
, TRUE
);
1366 /***********************************************************************
1367 * LISTBOX_SelectItemRange
1369 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1371 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1376 /* A few sanity checks */
1378 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1379 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1381 if (!descr
->nb_items
) return LB_OKAY
;
1383 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1384 if (first
< 0) first
= 0;
1385 if (last
< first
) return LB_OKAY
;
1387 if (on
) /* Turn selection on */
1389 for (i
= first
; i
<= last
; i
++)
1391 if (descr
->items
[i
].selected
) continue;
1392 descr
->items
[i
].selected
= TRUE
;
1393 LISTBOX_InvalidateItemRect(descr
, i
);
1396 else /* Turn selection off */
1398 for (i
= first
; i
<= last
; i
++)
1400 if (!descr
->items
[i
].selected
) continue;
1401 descr
->items
[i
].selected
= FALSE
;
1402 LISTBOX_InvalidateItemRect(descr
, i
);
1408 /***********************************************************************
1409 * LISTBOX_SetSelection
1411 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1412 BOOL on
, BOOL send_notify
)
1414 TRACE( "cur_sel=%d index=%d notify=%s\n",
1415 descr
->selected_item
, index
, send_notify
? "YES" : "NO" );
1417 if (descr
->style
& LBS_NOSEL
)
1419 descr
->selected_item
= index
;
1422 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1423 if (descr
->style
& LBS_MULTIPLESEL
)
1425 if (index
== -1) /* Select all items */
1426 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1427 else /* Only one item */
1428 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1432 INT oldsel
= descr
->selected_item
;
1433 if (index
== oldsel
) return LB_OKAY
;
1434 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1435 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1436 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1437 descr
->selected_item
= index
;
1438 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1439 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1440 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1442 if( descr
->lphc
) /* set selection change flag for parent combo */
1443 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1449 /***********************************************************************
1452 * Change the caret position and extend the selection to the new caret.
1454 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1456 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1458 if ((index
< 0) || (index
>= descr
->nb_items
))
1461 /* Important, repaint needs to be done in this order if
1462 you want to mimic Windows behavior:
1463 1. Remove the focus and paint the item
1464 2. Remove the selection and paint the item(s)
1465 3. Set the selection and repaint the item(s)
1466 4. Set the focus to 'index' and repaint the item */
1468 /* 1. remove the focus and repaint the item */
1469 LISTBOX_DrawFocusRect( descr
, FALSE
);
1471 /* 2. then turn off the previous selection */
1472 /* 3. repaint the new selected item */
1473 if (descr
->style
& LBS_EXTENDEDSEL
)
1475 if (descr
->anchor_item
!= -1)
1477 INT first
= min( index
, descr
->anchor_item
);
1478 INT last
= max( index
, descr
->anchor_item
);
1480 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1481 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1482 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1485 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1487 /* Set selection to new caret item */
1488 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1491 /* 4. repaint the new item with the focus */
1492 descr
->focus_item
= index
;
1493 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1494 LISTBOX_DrawFocusRect( descr
, TRUE
);
1498 /***********************************************************************
1499 * LISTBOX_InsertItem
1501 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1502 LPWSTR str
, ULONG_PTR data
)
1506 INT oldfocus
= descr
->focus_item
;
1508 if (index
== -1) index
= descr
->nb_items
;
1509 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1510 if (!descr
->items
) max_items
= 0;
1511 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1512 if (descr
->nb_items
== max_items
)
1514 /* We need to grow the array */
1515 max_items
+= LB_ARRAY_GRANULARITY
;
1517 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1518 max_items
* sizeof(LB_ITEMDATA
) );
1520 item
= HeapAlloc( GetProcessHeap(), 0,
1521 max_items
* sizeof(LB_ITEMDATA
) );
1524 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1527 descr
->items
= item
;
1530 /* Insert the item structure */
1532 item
= &descr
->items
[index
];
1533 if (index
< descr
->nb_items
)
1534 RtlMoveMemory( item
+ 1, item
,
1535 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1539 item
->selected
= FALSE
;
1542 /* Get item height */
1544 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1546 MEASUREITEMSTRUCT mis
;
1547 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1549 mis
.CtlType
= ODT_LISTBOX
;
1552 mis
.itemData
= descr
->items
[index
].data
;
1553 mis
.itemHeight
= descr
->item_height
;
1554 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1555 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1556 TRACE("[%p]: measure item %d (%s) = %d\n",
1557 descr
->self
, index
, str
? debugstr_w(str
) : "", item
->height
);
1560 /* Repaint the items */
1562 LISTBOX_UpdateScroll( descr
);
1563 LISTBOX_InvalidateItems( descr
, index
);
1565 /* Move selection and focused item */
1566 /* If listbox was empty, set focus to the first item */
1567 if (descr
->nb_items
== 1)
1568 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1569 /* single select don't change selection index in win31 */
1570 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1572 descr
->selected_item
++;
1573 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1577 if (index
<= descr
->selected_item
)
1579 descr
->selected_item
++;
1580 descr
->focus_item
= oldfocus
; /* focus not changed */
1587 /***********************************************************************
1588 * LISTBOX_InsertString
1590 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
, LPCWSTR str
)
1592 LPWSTR new_str
= NULL
;
1596 if (HAS_STRINGS(descr
))
1598 static const WCHAR empty_stringW
[] = { 0 };
1599 if (!str
) str
= empty_stringW
;
1600 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1602 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1605 strcpyW(new_str
, str
);
1607 else data
= (ULONG_PTR
)str
;
1609 if (index
== -1) index
= descr
->nb_items
;
1610 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, data
)) != 0)
1612 HeapFree( GetProcessHeap(), 0, new_str
);
1616 TRACE("[%p]: added item %d %s\n",
1617 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1622 /***********************************************************************
1623 * LISTBOX_DeleteItem
1625 * Delete the content of an item. 'index' must be a valid index.
1627 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1629 /* save the item data before it gets freed by LB_RESETCONTENT */
1630 ULONG_PTR item_data
= descr
->items
[index
].data
;
1631 LPWSTR item_str
= descr
->items
[index
].str
;
1633 if (!descr
->nb_items
)
1634 SendMessageW( descr
->self
, LB_RESETCONTENT
, 0, 0 );
1636 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1637 * while Win95 sends it for all items with user data.
1638 * It's probably better to send it too often than not
1639 * often enough, so this is what we do here.
1641 if (IS_OWNERDRAW(descr
) || item_data
)
1643 DELETEITEMSTRUCT dis
;
1644 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1646 dis
.CtlType
= ODT_LISTBOX
;
1649 dis
.hwndItem
= descr
->self
;
1650 dis
.itemData
= item_data
;
1651 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1653 if (HAS_STRINGS(descr
))
1654 HeapFree( GetProcessHeap(), 0, item_str
);
1658 /***********************************************************************
1659 * LISTBOX_RemoveItem
1661 * Remove an item from the listbox and delete its content.
1663 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1668 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1670 /* We need to invalidate the original rect instead of the updated one. */
1671 LISTBOX_InvalidateItems( descr
, index
);
1674 LISTBOX_DeleteItem( descr
, index
);
1676 if (!descr
->nb_items
) return LB_OKAY
;
1678 /* Remove the item */
1680 item
= &descr
->items
[index
];
1681 if (index
< descr
->nb_items
)
1682 RtlMoveMemory( item
, item
+ 1,
1683 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1684 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1686 /* Shrink the item array if possible */
1688 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1689 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1691 max_items
-= LB_ARRAY_GRANULARITY
;
1692 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1693 max_items
* sizeof(LB_ITEMDATA
) );
1694 if (item
) descr
->items
= item
;
1696 /* Repaint the items */
1698 LISTBOX_UpdateScroll( descr
);
1699 /* if we removed the scrollbar, reset the top of the list
1700 (correct for owner-drawn ???) */
1701 if (descr
->nb_items
== descr
->page_size
)
1702 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1704 /* Move selection and focused item */
1705 if (!IS_MULTISELECT(descr
))
1707 if (index
== descr
->selected_item
)
1708 descr
->selected_item
= -1;
1709 else if (index
< descr
->selected_item
)
1711 descr
->selected_item
--;
1712 if (ISWIN31
) /* win 31 do not change the selected item number */
1713 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1717 if (descr
->focus_item
>= descr
->nb_items
)
1719 descr
->focus_item
= descr
->nb_items
- 1;
1720 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1726 /***********************************************************************
1727 * LISTBOX_ResetContent
1729 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1733 for(i
= descr
->nb_items
- 1; i
>=0; i
--) LISTBOX_DeleteItem( descr
, i
);
1734 HeapFree( GetProcessHeap(), 0, descr
->items
);
1735 descr
->nb_items
= 0;
1736 descr
->top_item
= 0;
1737 descr
->selected_item
= -1;
1738 descr
->focus_item
= 0;
1739 descr
->anchor_item
= -1;
1740 descr
->items
= NULL
;
1744 /***********************************************************************
1747 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, INT count
)
1751 if (HAS_STRINGS(descr
))
1753 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1757 /* FIXME: this is far from optimal... */
1758 if (count
> descr
->nb_items
)
1760 while (count
> descr
->nb_items
)
1761 if ((ret
= LISTBOX_InsertString( descr
, -1, 0 )) < 0)
1764 else if (count
< descr
->nb_items
)
1766 while (count
< descr
->nb_items
)
1767 if ((ret
= LISTBOX_RemoveItem( descr
, (descr
->nb_items
- 1) )) < 0)
1771 InvalidateRect( descr
->self
, NULL
, TRUE
);
1776 /***********************************************************************
1779 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1780 LPCWSTR filespec
, BOOL long_names
)
1783 LRESULT ret
= LB_OKAY
;
1784 WIN32_FIND_DATAW entry
;
1786 LRESULT maxinsert
= LB_ERR
;
1788 /* don't scan directory if we just want drives exclusively */
1789 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1790 /* scan directory */
1791 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1793 int le
= GetLastError();
1794 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1801 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1803 static const WCHAR bracketW
[] = { ']',0 };
1804 static const WCHAR dotW
[] = { '.',0 };
1805 if (!(attrib
& DDL_DIRECTORY
) ||
1806 !strcmpW( entry
.cFileName
, dotW
)) continue;
1808 if (!long_names
&& entry
.cAlternateFileName
[0])
1809 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1811 strcpyW( buffer
+ 1, entry
.cFileName
);
1812 strcatW(buffer
, bracketW
);
1814 else /* not a directory */
1816 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1817 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1819 if ((attrib
& DDL_EXCLUSIVE
) &&
1820 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1823 if (!long_names
&& entry
.cAlternateFileName
[0])
1824 strcpyW( buffer
, entry
.cAlternateFileName
);
1826 strcpyW( buffer
, entry
.cFileName
);
1828 if (!long_names
) CharLowerW( buffer
);
1829 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1830 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1832 if (ret
<= maxinsert
) maxinsert
++; else maxinsert
= ret
;
1833 } while (FindNextFileW( handle
, &entry
));
1834 FindClose( handle
);
1842 if (attrib
& DDL_DRIVES
)
1844 WCHAR buffer
[] = {'[','-','a','-',']',0};
1845 WCHAR root
[] = {'A',':','\\',0};
1847 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1849 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1850 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1859 /***********************************************************************
1860 * LISTBOX_HandleVScroll
1862 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1866 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1870 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1873 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1876 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1877 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1880 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1881 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1883 case SB_THUMBPOSITION
:
1884 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1887 info
.cbSize
= sizeof(info
);
1888 info
.fMask
= SIF_TRACKPOS
;
1889 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1890 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1893 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1896 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1903 /***********************************************************************
1904 * LISTBOX_HandleHScroll
1906 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1911 if (descr
->style
& LBS_MULTICOLUMN
)
1916 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
1920 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
1924 page
= descr
->width
/ descr
->column_width
;
1925 if (page
< 1) page
= 1;
1926 LISTBOX_SetTopItem( descr
,
1927 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1930 page
= descr
->width
/ descr
->column_width
;
1931 if (page
< 1) page
= 1;
1932 LISTBOX_SetTopItem( descr
,
1933 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1935 case SB_THUMBPOSITION
:
1936 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
1939 info
.cbSize
= sizeof(info
);
1940 info
.fMask
= SIF_TRACKPOS
;
1941 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1942 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
1946 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1949 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1953 else if (descr
->horz_extent
)
1958 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
1961 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
1964 LISTBOX_SetHorizontalPos( descr
,
1965 descr
->horz_pos
- descr
->width
);
1968 LISTBOX_SetHorizontalPos( descr
,
1969 descr
->horz_pos
+ descr
->width
);
1971 case SB_THUMBPOSITION
:
1972 LISTBOX_SetHorizontalPos( descr
, pos
);
1975 info
.cbSize
= sizeof(info
);
1976 info
.fMask
= SIF_TRACKPOS
;
1977 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
1978 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
1981 LISTBOX_SetHorizontalPos( descr
, 0 );
1984 LISTBOX_SetHorizontalPos( descr
,
1985 descr
->horz_extent
- descr
->width
);
1992 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
1994 UINT pulScrollLines
= 3;
1996 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1998 /* if scrolling changes direction, ignore left overs */
1999 if ((delta
< 0 && descr
->wheel_remain
< 0) ||
2000 (delta
> 0 && descr
->wheel_remain
> 0))
2001 descr
->wheel_remain
+= delta
;
2003 descr
->wheel_remain
= delta
;
2005 if (descr
->wheel_remain
&& pulScrollLines
)
2008 pulScrollLines
= min((UINT
) descr
->page_size
, pulScrollLines
);
2009 cLineScroll
= pulScrollLines
* (float)descr
->wheel_remain
/ WHEEL_DELTA
;
2010 descr
->wheel_remain
-= WHEEL_DELTA
* cLineScroll
/ (int)pulScrollLines
;
2011 LISTBOX_SetTopItem( descr
, descr
->top_item
- cLineScroll
, TRUE
);
2016 /***********************************************************************
2017 * LISTBOX_HandleLButtonDown
2019 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
2021 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2023 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2024 descr
->self
, x
, y
, index
, descr
->focus_item
);
2026 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2028 if (!descr
->in_focus
)
2030 if( !descr
->lphc
) SetFocus( descr
->self
);
2031 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2034 if (index
== -1) return 0;
2038 if (descr
->style
& LBS_NOTIFY
)
2039 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2040 MAKELPARAM( x
, y
) );
2043 descr
->captured
= TRUE
;
2044 SetCapture( descr
->self
);
2046 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2048 /* we should perhaps make sure that all items are deselected
2049 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2050 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2051 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2054 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2055 if (keys
& MK_CONTROL
)
2057 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2058 LISTBOX_SetSelection( descr
, index
,
2059 !descr
->items
[index
].selected
,
2060 (descr
->style
& LBS_NOTIFY
) != 0);
2064 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2066 if (descr
->style
& LBS_EXTENDEDSEL
)
2068 LISTBOX_SetSelection( descr
, index
,
2069 descr
->items
[index
].selected
,
2070 (descr
->style
& LBS_NOTIFY
) != 0 );
2074 LISTBOX_SetSelection( descr
, index
,
2075 !descr
->items
[index
].selected
,
2076 (descr
->style
& LBS_NOTIFY
) != 0 );
2082 descr
->anchor_item
= index
;
2083 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2084 LISTBOX_SetSelection( descr
, index
,
2085 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2090 if (GetWindowLongW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2097 if (DragDetect( descr
->self
, pt
))
2098 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2105 /*************************************************************************
2106 * LISTBOX_HandleLButtonDownCombo [Internal]
2108 * Process LButtonDown message for the ComboListBox
2111 * pWnd [I] The windows internal structure
2112 * pDescr [I] The ListBox internal structure
2113 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2114 * x [I] X Mouse Coordinate
2115 * y [I] Y Mouse Coordinate
2118 * 0 since we are processing the WM_LBUTTONDOWN Message
2121 * This function is only to be used when a ListBox is a ComboListBox
2124 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2126 RECT clientRect
, screenRect
;
2132 GetClientRect(descr
->self
, &clientRect
);
2134 if(PtInRect(&clientRect
, mousePos
))
2136 /* MousePos is in client, resume normal processing */
2137 if (msg
== WM_LBUTTONDOWN
)
2139 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2140 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2142 else if (descr
->style
& LBS_NOTIFY
)
2143 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2147 POINT screenMousePos
;
2148 HWND hWndOldCapture
;
2150 /* Check the Non-Client Area */
2151 screenMousePos
= mousePos
;
2152 hWndOldCapture
= GetCapture();
2154 GetWindowRect(descr
->self
, &screenRect
);
2155 ClientToScreen(descr
->self
, &screenMousePos
);
2157 if(!PtInRect(&screenRect
, screenMousePos
))
2159 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2160 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2161 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2165 /* Check to see the NC is a scrollbar */
2167 LONG style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2168 /* Check Vertical scroll bar */
2169 if (style
& WS_VSCROLL
)
2171 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2172 if (PtInRect( &clientRect
, mousePos
))
2173 nHitTestType
= HTVSCROLL
;
2175 /* Check horizontal scroll bar */
2176 if (style
& WS_HSCROLL
)
2178 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2179 if (PtInRect( &clientRect
, mousePos
))
2180 nHitTestType
= HTHSCROLL
;
2182 /* Windows sends this message when a scrollbar is clicked
2185 if(nHitTestType
!= 0)
2187 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2188 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2190 /* Resume the Capture after scrolling is complete
2192 if(hWndOldCapture
!= 0)
2193 SetCapture(hWndOldCapture
);
2199 /***********************************************************************
2200 * LISTBOX_HandleLButtonUp
2202 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2204 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2205 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2206 LISTBOX_Timer
= LB_TIMER_NONE
;
2207 if (descr
->captured
)
2209 descr
->captured
= FALSE
;
2210 if (GetCapture() == descr
->self
) ReleaseCapture();
2211 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2212 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2218 /***********************************************************************
2219 * LISTBOX_HandleTimer
2221 * Handle scrolling upon a timer event.
2222 * Return TRUE if scrolling should continue.
2224 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2229 if (descr
->top_item
) index
= descr
->top_item
- 1;
2233 if (descr
->top_item
) index
-= descr
->page_size
;
2236 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2237 if (index
== descr
->focus_item
) index
++;
2238 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2240 case LB_TIMER_RIGHT
:
2241 if (index
+ descr
->page_size
< descr
->nb_items
)
2242 index
+= descr
->page_size
;
2247 if (index
== descr
->focus_item
) return FALSE
;
2248 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2253 /***********************************************************************
2254 * LISTBOX_HandleSystemTimer
2256 * WM_SYSTIMER handler.
2258 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2260 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2262 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2263 LISTBOX_Timer
= LB_TIMER_NONE
;
2269 /***********************************************************************
2270 * LISTBOX_HandleMouseMove
2272 * WM_MOUSEMOVE handler.
2274 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2278 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2280 if (!descr
->captured
) return;
2282 if (descr
->style
& LBS_MULTICOLUMN
)
2285 else if (y
>= descr
->item_height
* descr
->page_size
)
2286 y
= descr
->item_height
* descr
->page_size
- 1;
2290 dir
= LB_TIMER_LEFT
;
2293 else if (x
>= descr
->width
)
2295 dir
= LB_TIMER_RIGHT
;
2296 x
= descr
->width
- 1;
2301 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2302 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2305 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2306 if (index
== -1) index
= descr
->focus_item
;
2307 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2309 /* Start/stop the system timer */
2311 if (dir
!= LB_TIMER_NONE
)
2312 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2313 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2314 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2315 LISTBOX_Timer
= dir
;
2319 /***********************************************************************
2320 * LISTBOX_HandleKeyDown
2322 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2325 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2326 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2327 bForceSelection
= FALSE
; /* only for single select list */
2329 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2331 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2332 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2333 (LPARAM
)descr
->self
);
2334 if (caret
== -2) return 0;
2336 if (caret
== -1) switch(key
)
2339 if (descr
->style
& LBS_MULTICOLUMN
)
2341 bForceSelection
= FALSE
;
2342 if (descr
->focus_item
>= descr
->page_size
)
2343 caret
= descr
->focus_item
- descr
->page_size
;
2348 caret
= descr
->focus_item
- 1;
2349 if (caret
< 0) caret
= 0;
2352 if (descr
->style
& LBS_MULTICOLUMN
)
2354 bForceSelection
= FALSE
;
2355 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2356 caret
= descr
->focus_item
+ descr
->page_size
;
2361 caret
= descr
->focus_item
+ 1;
2362 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2366 if (descr
->style
& LBS_MULTICOLUMN
)
2368 INT page
= descr
->width
/ descr
->column_width
;
2369 if (page
< 1) page
= 1;
2370 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2372 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2373 if (caret
< 0) caret
= 0;
2376 if (descr
->style
& LBS_MULTICOLUMN
)
2378 INT page
= descr
->width
/ descr
->column_width
;
2379 if (page
< 1) page
= 1;
2380 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2382 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2383 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2389 caret
= descr
->nb_items
- 1;
2392 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2393 else if (descr
->style
& LBS_MULTIPLESEL
)
2395 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2396 !descr
->items
[descr
->focus_item
].selected
,
2397 (descr
->style
& LBS_NOTIFY
) != 0 );
2401 bForceSelection
= FALSE
;
2403 if (bForceSelection
) /* focused item is used instead of key */
2404 caret
= descr
->focus_item
;
2407 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2408 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2409 !IS_MULTISELECT(descr
))
2410 descr
->anchor_item
= caret
;
2411 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2413 if (descr
->style
& LBS_MULTIPLESEL
)
2414 descr
->selected_item
= caret
;
2416 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2417 if (descr
->style
& LBS_NOTIFY
)
2419 if (descr
->lphc
&& IsWindowVisible( descr
->self
))
2421 /* make sure that combo parent doesn't hide us */
2422 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2424 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2431 /***********************************************************************
2432 * LISTBOX_HandleChar
2434 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2442 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2444 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2445 MAKEWPARAM(charW
, descr
->focus_item
),
2446 (LPARAM
)descr
->self
);
2447 if (caret
== -2) return 0;
2450 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2453 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2454 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2455 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2456 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2457 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2463 /***********************************************************************
2466 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2469 MEASUREITEMSTRUCT mis
;
2472 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2475 GetClientRect( hwnd
, &rect
);
2477 descr
->owner
= GetParent( descr
->self
);
2478 descr
->style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2479 descr
->width
= rect
.right
- rect
.left
;
2480 descr
->height
= rect
.bottom
- rect
.top
;
2481 descr
->items
= NULL
;
2482 descr
->nb_items
= 0;
2483 descr
->top_item
= 0;
2484 descr
->selected_item
= -1;
2485 descr
->focus_item
= 0;
2486 descr
->anchor_item
= -1;
2487 descr
->item_height
= 1;
2488 descr
->page_size
= 1;
2489 descr
->column_width
= 150;
2490 descr
->horz_extent
= 0;
2491 descr
->horz_pos
= 0;
2494 descr
->wheel_remain
= 0;
2495 descr
->caret_on
= !lphc
;
2496 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2497 descr
->in_focus
= FALSE
;
2498 descr
->captured
= FALSE
;
2500 descr
->locale
= GetUserDefaultLCID();
2505 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2506 descr
->owner
= lphc
->self
;
2509 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2511 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2513 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2514 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2515 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2516 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2518 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2520 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2522 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2523 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2527 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2528 mis
.CtlType
= ODT_LISTBOX
;
2533 mis
.itemHeight
= descr
->item_height
;
2534 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2535 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2539 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2544 /***********************************************************************
2547 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2549 LISTBOX_ResetContent( descr
);
2550 SetWindowLongPtrW( descr
->self
, 0, 0 );
2551 HeapFree( GetProcessHeap(), 0, descr
);
2556 /***********************************************************************
2557 * ListBoxWndProc_common
2559 LRESULT
ListBoxWndProc_common( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2561 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2562 LPHEADCOMBO lphc
= 0;
2567 if (!IsWindow(hwnd
)) return 0;
2569 if (msg
== WM_CREATE
)
2571 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2572 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= lpcs
->lpCreateParams
;
2573 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2574 TRACE("creating hwnd %p descr %p\n", hwnd
, (void *)GetWindowLongPtrW( hwnd
, 0 ) );
2577 /* Ignore all other messages before we get a WM_CREATE */
2578 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2579 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2581 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2583 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2584 descr
->self
, SPY_GetMsgName(msg
, descr
->self
), wParam
, lParam
);
2588 case LB_RESETCONTENT
:
2589 LISTBOX_ResetContent( descr
);
2590 LISTBOX_UpdateScroll( descr
);
2591 InvalidateRect( descr
->self
, NULL
, TRUE
);
2598 if(unicode
|| !HAS_STRINGS(descr
))
2599 textW
= (LPWSTR
)lParam
;
2602 LPSTR textA
= (LPSTR
)lParam
;
2603 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2604 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2605 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2609 wParam
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2610 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2611 if (!unicode
&& HAS_STRINGS(descr
))
2612 HeapFree(GetProcessHeap(), 0, textW
);
2616 case LB_INSERTSTRING
:
2620 if(unicode
|| !HAS_STRINGS(descr
))
2621 textW
= (LPWSTR
)lParam
;
2624 LPSTR textA
= (LPSTR
)lParam
;
2625 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2626 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2627 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2631 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2632 if(!unicode
&& HAS_STRINGS(descr
))
2633 HeapFree(GetProcessHeap(), 0, textW
);
2641 if(unicode
|| !HAS_STRINGS(descr
))
2642 textW
= (LPWSTR
)lParam
;
2645 LPSTR textA
= (LPSTR
)lParam
;
2646 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2647 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2648 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2652 wParam
= LISTBOX_FindFileStrPos( descr
, textW
);
2653 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2654 if(!unicode
&& HAS_STRINGS(descr
))
2655 HeapFree(GetProcessHeap(), 0, textW
);
2659 case LB_DELETESTRING
:
2660 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2661 return descr
->nb_items
;
2664 SetLastError(ERROR_INVALID_INDEX
);
2668 case LB_GETITEMDATA
:
2669 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2671 SetLastError(ERROR_INVALID_INDEX
);
2674 return descr
->items
[wParam
].data
;
2676 case LB_SETITEMDATA
:
2677 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2679 SetLastError(ERROR_INVALID_INDEX
);
2682 descr
->items
[wParam
].data
= lParam
;
2683 /* undocumented: returns TRUE, not LB_OKAY (0) */
2687 return descr
->nb_items
;
2690 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, unicode
);
2693 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2695 SetLastError(ERROR_INVALID_INDEX
);
2698 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2699 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2700 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2701 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2704 if (descr
->nb_items
== 0)
2706 if (!IS_MULTISELECT(descr
))
2707 return descr
->selected_item
;
2708 if (descr
->selected_item
!= -1)
2709 return descr
->selected_item
;
2710 return descr
->focus_item
;
2711 /* otherwise, if the user tries to move the selection with the */
2712 /* arrow keys, we will give the application something to choke on */
2713 case LB_GETTOPINDEX
:
2714 return descr
->top_item
;
2716 case LB_GETITEMHEIGHT
:
2717 return LISTBOX_GetItemHeight( descr
, wParam
);
2719 case LB_SETITEMHEIGHT
:
2720 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2722 case LB_ITEMFROMPOINT
:
2729 /* The hiword of the return value is not a client area
2730 hittest as suggested by MSDN, but rather a hittest on
2731 the returned listbox item. */
2733 if(descr
->nb_items
== 0)
2734 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2736 pt
.x
= (short)LOWORD(lParam
);
2737 pt
.y
= (short)HIWORD(lParam
);
2739 SetRect(&rect
, 0, 0, descr
->width
, descr
->height
);
2741 if(!PtInRect(&rect
, pt
))
2743 pt
.x
= min(pt
.x
, rect
.right
- 1);
2744 pt
.x
= max(pt
.x
, 0);
2745 pt
.y
= min(pt
.y
, rect
.bottom
- 1);
2746 pt
.y
= max(pt
.y
, 0);
2750 index
= LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
);
2754 index
= descr
->nb_items
- 1;
2757 return MAKELONG(index
, hit
? 0 : 1);
2760 case LB_SETCARETINDEX
:
2761 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2762 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2769 case LB_GETCARETINDEX
:
2770 return descr
->focus_item
;
2772 case LB_SETTOPINDEX
:
2773 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2775 case LB_SETCOLUMNWIDTH
:
2776 return LISTBOX_SetColumnWidth( descr
, wParam
);
2778 case LB_GETITEMRECT
:
2779 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2785 if(unicode
|| !HAS_STRINGS(descr
))
2786 textW
= (LPWSTR
)lParam
;
2789 LPSTR textA
= (LPSTR
)lParam
;
2790 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2791 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2792 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2794 ret
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2795 if(!unicode
&& HAS_STRINGS(descr
))
2796 HeapFree(GetProcessHeap(), 0, textW
);
2800 case LB_FINDSTRINGEXACT
:
2804 if(unicode
|| !HAS_STRINGS(descr
))
2805 textW
= (LPWSTR
)lParam
;
2808 LPSTR textA
= (LPSTR
)lParam
;
2809 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2810 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2811 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2813 ret
= LISTBOX_FindString( descr
, wParam
, textW
, TRUE
);
2814 if(!unicode
&& HAS_STRINGS(descr
))
2815 HeapFree(GetProcessHeap(), 0, textW
);
2819 case LB_SELECTSTRING
:
2824 if(HAS_STRINGS(descr
))
2825 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2826 debugstr_a((LPSTR
)lParam
));
2827 if(unicode
|| !HAS_STRINGS(descr
))
2828 textW
= (LPWSTR
)lParam
;
2831 LPSTR textA
= (LPSTR
)lParam
;
2832 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2833 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2834 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2836 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2837 if(!unicode
&& HAS_STRINGS(descr
))
2838 HeapFree(GetProcessHeap(), 0, textW
);
2839 if (index
!= LB_ERR
)
2841 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2842 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2848 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2850 return descr
->items
[wParam
].selected
;
2853 return LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2856 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2857 LISTBOX_SetCaretIndex( descr
, wParam
, FALSE
);
2858 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2859 if (ret
!= LB_ERR
) ret
= descr
->selected_item
;
2862 case LB_GETSELCOUNT
:
2863 return LISTBOX_GetSelCount( descr
);
2865 case LB_GETSELITEMS
:
2866 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2868 case LB_SELITEMRANGE
:
2869 if (LOWORD(lParam
) <= HIWORD(lParam
))
2870 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2871 HIWORD(lParam
), wParam
);
2873 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2874 LOWORD(lParam
), wParam
);
2876 case LB_SELITEMRANGEEX
:
2877 if ((INT
)lParam
>= (INT
)wParam
)
2878 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2880 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
2882 case LB_GETHORIZONTALEXTENT
:
2883 return descr
->horz_extent
;
2885 case LB_SETHORIZONTALEXTENT
:
2886 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
2888 case LB_GETANCHORINDEX
:
2889 return descr
->anchor_item
;
2891 case LB_SETANCHORINDEX
:
2892 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2894 SetLastError(ERROR_INVALID_INDEX
);
2897 descr
->anchor_item
= (INT
)wParam
;
2905 textW
= (LPWSTR
)lParam
;
2908 LPSTR textA
= (LPSTR
)lParam
;
2909 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2910 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2911 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2913 ret
= LISTBOX_Directory( descr
, wParam
, textW
, msg
== LB_DIR
);
2915 HeapFree(GetProcessHeap(), 0, textW
);
2920 return descr
->locale
;
2925 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
2927 ret
= descr
->locale
;
2928 descr
->locale
= (LCID
)wParam
;
2932 case LB_INITSTORAGE
:
2933 return LISTBOX_InitStorage( descr
, wParam
);
2936 return LISTBOX_SetCount( descr
, (INT
)wParam
);
2938 case LB_SETTABSTOPS
:
2939 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
);
2942 if (descr
->caret_on
)
2944 descr
->caret_on
= TRUE
;
2945 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2946 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2950 if (!descr
->caret_on
)
2952 descr
->caret_on
= FALSE
;
2953 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2954 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2957 case LB_GETLISTBOXINFO
:
2958 return descr
->page_size
;
2961 return LISTBOX_Destroy( descr
);
2964 InvalidateRect( descr
->self
, NULL
, TRUE
);
2968 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
2972 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2974 case WM_PRINTCLIENT
:
2978 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
2979 ret
= LISTBOX_Paint( descr
, hdc
);
2980 if( !wParam
) EndPaint( descr
->self
, &ps
);
2984 LISTBOX_UpdateSize( descr
);
2987 return (LRESULT
)descr
->font
;
2989 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
2990 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
2993 descr
->in_focus
= TRUE
;
2994 descr
->caret_on
= TRUE
;
2995 if (descr
->focus_item
!= -1)
2996 LISTBOX_DrawFocusRect( descr
, TRUE
);
2997 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
3000 LISTBOX_HandleLButtonUp( descr
); /* Release capture if we have it */
3001 descr
->in_focus
= FALSE
;
3002 descr
->wheel_remain
= 0;
3003 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
3004 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3005 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
3008 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3010 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3012 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
3013 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
3014 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
3015 case WM_LBUTTONDOWN
:
3017 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3018 (INT16
)LOWORD(lParam
),
3019 (INT16
)HIWORD(lParam
) );
3020 return LISTBOX_HandleLButtonDown( descr
, wParam
,
3021 (INT16
)LOWORD(lParam
),
3022 (INT16
)HIWORD(lParam
) );
3023 case WM_LBUTTONDBLCLK
:
3025 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3026 (INT16
)LOWORD(lParam
),
3027 (INT16
)HIWORD(lParam
) );
3028 if (descr
->style
& LBS_NOTIFY
)
3029 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
3032 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
3034 BOOL captured
= descr
->captured
;
3038 mousePos
.x
= (INT16
)LOWORD(lParam
);
3039 mousePos
.y
= (INT16
)HIWORD(lParam
);
3042 * If we are in a dropdown combobox, we simulate that
3043 * the mouse is captured to show the tracking of the item.
3045 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
3046 descr
->captured
= TRUE
;
3048 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
3050 descr
->captured
= captured
;
3052 else if (GetCapture() == descr
->self
)
3054 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
3055 (INT16
)HIWORD(lParam
) );
3065 * If the mouse button "up" is not in the listbox,
3066 * we make sure there is no selection by re-selecting the
3067 * item that was selected when the listbox was made visible.
3069 mousePos
.x
= (INT16
)LOWORD(lParam
);
3070 mousePos
.y
= (INT16
)HIWORD(lParam
);
3072 GetClientRect(descr
->self
, &clientRect
);
3075 * When the user clicks outside the combobox and the focus
3076 * is lost, the owning combobox will send a fake buttonup with
3077 * 0xFFFFFFF as the mouse location, we must also revert the
3078 * selection to the original selection.
3080 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
3081 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
3083 return LISTBOX_HandleLButtonUp( descr
);
3085 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3087 /* for some reason Windows makes it possible to
3088 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3090 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3091 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3092 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3094 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3098 return LISTBOX_HandleKeyDown( descr
, wParam
);
3103 charW
= (WCHAR
)wParam
;
3106 CHAR charA
= (CHAR
)wParam
;
3107 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
3109 return LISTBOX_HandleChar( descr
, charW
);
3112 return LISTBOX_HandleSystemTimer( descr
);
3114 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3117 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3118 wParam
, (LPARAM
)descr
->self
);
3119 TRACE("hbrush = %p\n", hbrush
);
3121 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3124 GetClientRect(descr
->self
, &rect
);
3125 FillRect((HDC
)wParam
, &rect
, hbrush
);
3130 if( lphc
) return 0;
3131 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3132 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3135 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3144 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3145 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3146 hwnd
, msg
, wParam
, lParam
);
3149 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3150 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3153 DWORD WINAPI
GetListBoxInfo(HWND hwnd
)
3155 TRACE("%p\n", hwnd
);
3156 return SendMessageW(hwnd
, LB_GETLISTBOXINFO
, 0, 0);