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.
30 * - LBS_NODATA ReactOS
35 WINE_DEFAULT_DEBUG_CHANNEL(listbox
);
37 /* Items array granularity */
38 #define LB_ARRAY_GRANULARITY 16
40 /* Scrolling timeout in ms */
41 #define LB_SCROLL_TIMEOUT 50
43 /* Listbox system timer id */
46 /* flag listbox changed while setredraw false - internal style */
47 #define LBS_DISPLAYCHANGED 0x80000000
52 LPWSTR str
; /* Item text */
53 BOOL selected
; /* Is item selected? */
54 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
55 ULONG_PTR data
; /* User data */
58 /* Listbox structure */
61 HWND self
; /* Our own window handle */
62 HWND owner
; /* Owner window to send notifications to */
63 UINT style
; /* Window style */
64 INT width
; /* Window width */
65 INT height
; /* Window height */
66 LB_ITEMDATA
*items
; /* Array of items */
67 INT nb_items
; /* Number of items */
68 INT top_item
; /* Top visible item */
69 INT selected_item
; /* Selected item */
70 INT focus_item
; /* Item that has the focus */
71 INT anchor_item
; /* Anchor item for extended selection */
72 INT item_height
; /* Default item height */
73 INT page_size
; /* Items per listbox page */
74 INT column_width
; /* Column width for multi-column listboxes */
75 INT horz_extent
; /* Horizontal extent */
76 INT horz_pos
; /* Horizontal position */
77 INT nb_tabs
; /* Number of tabs in array */
78 INT
*tabs
; /* Array of tabs */
79 INT avg_char_width
; /* Average width of characters */
80 INT wheel_remain
; /* Left over scroll amount */
81 BOOL caret_on
; /* Is caret on? */
82 BOOL captured
; /* Is mouse captured? */
84 HFONT font
; /* Current font */
85 LCID locale
; /* Current locale for string comparisons */
86 LPHEADCOMBO lphc
; /* ComboLBox */
87 LONG UIState
; // REACTOS
91 #define IS_OWNERDRAW(descr) \
92 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
94 #define HAS_STRINGS(descr) \
95 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
98 #define IS_MULTISELECT(descr) \
99 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
100 !((descr)->style & LBS_NOSEL))
102 #define SEND_NOTIFICATION(descr,code) \
103 (SendMessageW( (descr)->owner, WM_COMMAND, \
104 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
106 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
108 /* Current timer status */
118 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
120 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
);
122 /*********************************************************************
123 * listbox class descriptor
125 static const WCHAR listboxW
[] = {'L','i','s','t','B','o','x',0};
126 const struct builtin_class_descr LISTBOX_builtin_class
=
129 CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
130 ListBoxWndProcA
, /* procA */
131 ListBoxWndProcW
, /* procW */
132 sizeof(LB_DESCR
*), /* extra */
133 IDC_ARROW
, /* cursor */
138 /*********************************************************************
139 * combolbox class descriptor
141 static const WCHAR combolboxW
[] = {'C','o','m','b','o','L','B','o','x',0};
142 const struct builtin_class_descr COMBOLBOX_builtin_class
=
144 combolboxW
, /* name */
145 CS_DBLCLKS
| CS_SAVEBITS
, /* style */
146 ListBoxWndProcA
, /* procA */
147 ListBoxWndProcW
, /* procW */
148 sizeof(LB_DESCR
*), /* extra */
149 IDC_ARROW
, /* cursor */
154 /***********************************************************************
155 * LISTBOX_GetCurrentPageSize
157 * Return the current page size
159 static INT
LISTBOX_GetCurrentPageSize( const LB_DESCR
*descr
)
162 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
163 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
165 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
167 if (i
== descr
->top_item
) return 1;
168 else return i
- descr
->top_item
;
172 /***********************************************************************
173 * LISTBOX_GetMaxTopIndex
175 * Return the maximum possible index for the top of the listbox.
177 static INT
LISTBOX_GetMaxTopIndex( const LB_DESCR
*descr
)
181 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
183 page
= descr
->height
;
184 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
185 if ((page
-= descr
->items
[max
].height
) < 0) break;
186 if (max
< descr
->nb_items
- 1) max
++;
188 else if (descr
->style
& LBS_MULTICOLUMN
)
190 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
191 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
192 max
= (max
- page
) * descr
->page_size
;
196 max
= descr
->nb_items
- descr
->page_size
;
198 if (max
< 0) max
= 0;
203 /***********************************************************************
204 * LISTBOX_UpdateScroll
206 * Update the scrollbars. Should be called whenever the content
207 * of the listbox changes.
209 static void LISTBOX_UpdateScroll( LB_DESCR
*descr
)
213 /* Check the listbox scroll bar flags individually before we call
214 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
215 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
216 scroll bar when we do not need one.
217 if (!(descr->style & WS_VSCROLL)) return;
220 /* It is important that we check descr->style, and not wnd->dwStyle,
221 for WS_VSCROLL, as the former is exactly the one passed in
222 argument to CreateWindow.
223 In Windows (and from now on in Wine :) a listbox created
224 with such a style (no WS_SCROLL) does not update
225 the scrollbar with listbox-related data, thus letting
226 the programmer use it for his/her own purposes. */
228 if (descr
->style
& LBS_NOREDRAW
) return;
229 info
.cbSize
= sizeof(info
);
231 if (descr
->style
& LBS_MULTICOLUMN
)
234 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
235 info
.nPos
= descr
->top_item
/ descr
->page_size
;
236 info
.nPage
= descr
->width
/ descr
->column_width
;
237 if (info
.nPage
< 1) info
.nPage
= 1;
238 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
239 if (descr
->style
& LBS_DISABLENOSCROLL
)
240 info
.fMask
|= SIF_DISABLENOSCROLL
;
241 if (descr
->style
& WS_HSCROLL
)
242 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
244 info
.fMask
= SIF_RANGE
;
245 if (descr
->style
& WS_VSCROLL
)
246 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
251 info
.nMax
= descr
->nb_items
- 1;
252 info
.nPos
= descr
->top_item
;
253 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
254 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
255 if (descr
->style
& LBS_DISABLENOSCROLL
)
256 info
.fMask
|= SIF_DISABLENOSCROLL
;
257 if (descr
->style
& WS_VSCROLL
)
258 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
260 if ((descr
->style
& WS_HSCROLL
) && descr
->horz_extent
)
262 info
.nPos
= descr
->horz_pos
;
263 info
.nPage
= descr
->width
;
264 info
.fMask
= SIF_POS
| SIF_PAGE
;
265 if (descr
->style
& LBS_DISABLENOSCROLL
)
266 info
.fMask
|= SIF_DISABLENOSCROLL
;
267 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
271 if (descr
->style
& LBS_DISABLENOSCROLL
)
275 info
.fMask
= SIF_RANGE
| SIF_DISABLENOSCROLL
;
276 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
280 ShowScrollBar( descr
->self
, SB_HORZ
, FALSE
);
287 /***********************************************************************
290 * Set the top item of the listbox, scrolling up or down if necessary.
292 static LRESULT
LISTBOX_SetTopItem( LB_DESCR
*descr
, INT index
, BOOL scroll
)
294 INT max
= LISTBOX_GetMaxTopIndex( descr
);
296 TRACE("setting top item %d, scroll %d\n", index
, scroll
);
298 if (index
> max
) index
= max
;
299 if (index
< 0) index
= 0;
300 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
301 if (descr
->top_item
== index
) return LB_OKAY
;
305 if (descr
->style
& LBS_MULTICOLUMN
)
306 diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
307 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
311 if (index
> descr
->top_item
)
313 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
314 diff
-= descr
->items
[i
].height
;
318 for (i
= index
; i
< descr
->top_item
; i
++)
319 diff
+= descr
->items
[i
].height
;
323 diff
= (descr
->top_item
- index
) * descr
->item_height
;
325 ScrollWindowEx( descr
->self
, 0, diff
, NULL
, NULL
, 0, NULL
,
326 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
329 InvalidateRect( descr
->self
, NULL
, TRUE
);
330 descr
->top_item
= index
;
331 LISTBOX_UpdateScroll( descr
);
336 /***********************************************************************
339 * Update the page size. Should be called when the size of
340 * the client area or the item height changes.
342 static void LISTBOX_UpdatePage( LB_DESCR
*descr
)
346 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
348 if (page_size
== descr
->page_size
) return;
349 descr
->page_size
= page_size
;
350 if (descr
->style
& LBS_MULTICOLUMN
)
351 InvalidateRect( descr
->self
, NULL
, TRUE
);
352 LISTBOX_SetTopItem( descr
, descr
->top_item
, FALSE
);
356 /***********************************************************************
359 * Update the size of the listbox. Should be called when the size of
360 * the client area changes.
362 static void LISTBOX_UpdateSize( LB_DESCR
*descr
)
365 LONG style
= GetWindowLongPtrW( descr
->self
, GWL_STYLE
);
367 GetClientRect( descr
->self
, &rect
);
368 if (style
& WS_HSCROLL
)
369 rect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
370 descr
->width
= rect
.right
- rect
.left
;
371 descr
->height
= rect
.bottom
- rect
.top
;
372 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
377 GetWindowRect( descr
->self
, &rect
);
378 if(descr
->item_height
!= 0)
379 remaining
= descr
->height
% descr
->item_height
;
382 if ((descr
->height
> descr
->item_height
) && remaining
)
384 TRACE("[%p]: changing height %d -> %d\n",
385 descr
->self
, descr
->height
, descr
->height
- remaining
);
386 SetWindowPos( descr
->self
, 0, 0, 0, rect
.right
- rect
.left
,
387 rect
.bottom
- rect
.top
- remaining
,
388 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
392 TRACE("[%p]: new size = %d,%d\n", descr
->self
, descr
->width
, descr
->height
);
393 LISTBOX_UpdatePage( descr
);
394 LISTBOX_UpdateScroll( descr
);
396 /* Invalidate the focused item so it will be repainted correctly */
397 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
399 InvalidateRect( descr
->self
, &rect
, FALSE
);
404 /***********************************************************************
405 * LISTBOX_GetItemRect
407 * Get the rectangle enclosing an item, in listbox client coordinates.
408 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
410 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
)
412 /* Index <= 0 is legal even on empty listboxes */
413 if (index
&& (index
>= descr
->nb_items
))
415 memset(rect
, 0, sizeof(*rect
));
416 SetLastError(ERROR_INVALID_INDEX
);
419 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
420 if (descr
->style
& LBS_MULTICOLUMN
)
422 INT col
= (index
/ descr
->page_size
) -
423 (descr
->top_item
/ descr
->page_size
);
424 rect
->left
+= col
* descr
->column_width
;
425 rect
->right
= rect
->left
+ descr
->column_width
;
426 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
427 rect
->bottom
= rect
->top
+ descr
->item_height
;
429 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
432 rect
->right
+= descr
->horz_pos
;
433 if ((index
>= 0) && (index
< descr
->nb_items
))
435 if (index
< descr
->top_item
)
437 for (i
= descr
->top_item
-1; i
>= index
; i
--)
438 rect
->top
-= descr
->items
[i
].height
;
442 for (i
= descr
->top_item
; i
< index
; i
++)
443 rect
->top
+= descr
->items
[i
].height
;
445 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
451 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
452 rect
->bottom
= rect
->top
+ descr
->item_height
;
453 rect
->right
+= descr
->horz_pos
;
456 TRACE("item %d, rect %s\n", index
, wine_dbgstr_rect(rect
));
458 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
459 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
463 /***********************************************************************
464 * LISTBOX_GetItemFromPoint
466 * Return the item nearest from point (x,y) (in client coordinates).
468 static INT
LISTBOX_GetItemFromPoint( const LB_DESCR
*descr
, INT x
, INT y
)
470 INT index
= descr
->top_item
;
472 if (!descr
->nb_items
) return -1; /* No items */
473 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
478 while (index
< descr
->nb_items
)
480 if ((pos
+= descr
->items
[index
].height
) > y
) break;
489 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
493 else if (descr
->style
& LBS_MULTICOLUMN
)
495 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
496 if (y
>= 0) index
+= y
/ descr
->item_height
;
497 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
498 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
502 index
+= (y
/ descr
->item_height
);
504 if (index
< 0) return 0;
505 if (index
>= descr
->nb_items
) return -1;
510 /***********************************************************************
515 static void LISTBOX_PaintItem( LB_DESCR
*descr
, HDC hdc
, const RECT
*rect
,
516 INT index
, UINT action
, BOOL ignoreFocus
)
518 LB_ITEMDATA
*item
= NULL
;
519 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
521 if (IS_OWNERDRAW(descr
))
529 if (action
== ODA_FOCUS
)
531 if (!(descr
->UIState
& UISF_HIDEFOCUS
))
532 DrawFocusRect( hdc
, rect
);
535 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
539 /* some programs mess with the clipping region when
540 drawing the item, *and* restore the previous region
541 after they are done, so a region has better to exist
542 else everything ends clipped */
543 GetClientRect(descr
->self
, &r
);
544 hrgn
= set_control_clipping( hdc
, &r
);
546 dis
.CtlType
= ODT_LISTBOX
;
547 dis
.CtlID
= GetWindowLongPtrW( descr
->self
, GWLP_ID
);
548 dis
.hwndItem
= descr
->self
;
549 dis
.itemAction
= action
;
553 if (item
->selected
) dis
.itemState
|= ODS_SELECTED
;
554 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
556 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
557 if (!IsWindowEnabled(descr
->self
)) dis
.itemState
|= ODS_DISABLED
;
558 dis
.itemData
= item
->data
;
560 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
561 descr
->self
, index
, debugstr_w(item
->str
), action
,
562 dis
.itemState
, wine_dbgstr_rect(rect
) );
563 SendMessageW(descr
->owner
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
564 SelectClipRgn( hdc
, hrgn
);
565 if (hrgn
) DeleteObject( hrgn
);
569 COLORREF oldText
= 0, oldBk
= 0;
571 if (action
== ODA_FOCUS
)
573 if (!(descr
->UIState
& UISF_HIDEFOCUS
)) // REACTOS
574 DrawFocusRect( hdc
, rect
);
577 if (item
&& item
->selected
)
579 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
580 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
583 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
584 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
585 wine_dbgstr_rect(rect
) );
587 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
588 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
589 else if (!(descr
->style
& LBS_USETABSTOPS
))
590 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
591 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
592 strlenW(item
->str
), NULL
);
595 /* Output empty string to paint background in the full width. */
596 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
597 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
598 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
599 item
->str
, strlenW(item
->str
),
600 descr
->nb_tabs
, descr
->tabs
, 0);
602 if (item
&& item
->selected
)
604 SetBkColor( hdc
, oldBk
);
605 SetTextColor( hdc
, oldText
);
607 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
610 !(descr
->UIState
& UISF_HIDEFOCUS
)) DrawFocusRect( hdc
, rect
);
615 /***********************************************************************
618 * Change the redraw flag.
620 static void LISTBOX_SetRedraw( LB_DESCR
*descr
, BOOL on
)
624 if (!(descr
->style
& LBS_NOREDRAW
)) return;
625 descr
->style
&= ~LBS_NOREDRAW
;
626 if (descr
->style
& LBS_DISPLAYCHANGED
)
627 { /* page was changed while setredraw false, refresh automatically */
628 InvalidateRect(descr
->self
, NULL
, TRUE
);
629 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
630 { /* reset top of page if less than number of items/page */
631 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
632 if (descr
->top_item
< 0) descr
->top_item
= 0;
634 descr
->style
&= ~LBS_DISPLAYCHANGED
;
636 LISTBOX_UpdateScroll( descr
);
638 else descr
->style
|= LBS_NOREDRAW
;
642 /***********************************************************************
643 * LISTBOX_RepaintItem
645 * Repaint a single item synchronously.
647 static void LISTBOX_RepaintItem( LB_DESCR
*descr
, INT index
, UINT action
)
652 HBRUSH hbrush
, oldBrush
= 0;
654 /* Do not repaint the item if the item is not visible */
655 if (!IsWindowVisible(descr
->self
)) return;
656 if (descr
->style
& LBS_NOREDRAW
)
658 descr
->style
|= LBS_DISPLAYCHANGED
;
661 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
662 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
663 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
665 hbrush
= GetControlColor( descr
->owner
, descr
->self
, hdc
, WM_CTLCOLORLISTBOX
);
667 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
668 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
670 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
671 if (!IsWindowEnabled(descr
->self
))
672 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
673 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
674 LISTBOX_PaintItem( descr
, hdc
, &rect
, index
, action
, TRUE
);
675 if (oldFont
) SelectObject( hdc
, oldFont
);
676 if (oldBrush
) SelectObject( hdc
, oldBrush
);
677 ReleaseDC( descr
->self
, hdc
);
681 /***********************************************************************
682 * LISTBOX_DrawFocusRect
684 static void LISTBOX_DrawFocusRect( LB_DESCR
*descr
, BOOL on
)
690 /* Do not repaint the item if the item is not visible */
691 if (!IsWindowVisible(descr
->self
)) return;
693 if (descr
->focus_item
== -1) return;
694 if (!descr
->caret_on
|| !descr
->in_focus
) return;
696 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) != 1) return;
697 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
698 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
699 if (!IsWindowEnabled(descr
->self
))
700 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
701 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
702 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, !on
);
703 if (oldFont
) SelectObject( hdc
, oldFont
);
704 ReleaseDC( descr
->self
, hdc
);
708 /***********************************************************************
709 * LISTBOX_InitStorage
711 static LRESULT
LISTBOX_InitStorage( LB_DESCR
*descr
, INT nb_items
)
715 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
716 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
718 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
719 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
720 nb_items
* sizeof(LB_ITEMDATA
));
723 item
= HeapAlloc( GetProcessHeap(), 0,
724 nb_items
* sizeof(LB_ITEMDATA
));
729 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
737 /***********************************************************************
738 * LISTBOX_SetTabStops
740 static BOOL
LISTBOX_SetTabStops( LB_DESCR
*descr
, INT count
, LPINT tabs
)
744 if (!(descr
->style
& LBS_USETABSTOPS
))
746 SetLastError(ERROR_LB_WITHOUT_TABSTOPS
);
750 HeapFree( GetProcessHeap(), 0, descr
->tabs
);
751 if (!(descr
->nb_tabs
= count
))
756 if (!(descr
->tabs
= HeapAlloc( GetProcessHeap(), 0,
757 descr
->nb_tabs
* sizeof(INT
) )))
759 memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
761 /* convert into "dialog units"*/
762 for (i
= 0; i
< descr
->nb_tabs
; i
++)
763 descr
->tabs
[i
] = MulDiv(descr
->tabs
[i
], descr
->avg_char_width
, 4);
769 /***********************************************************************
772 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPWSTR buffer
, BOOL unicode
)
776 if ((index
< 0) || (index
>= descr
->nb_items
))
778 SetLastError(ERROR_INVALID_INDEX
);
781 if (HAS_STRINGS(descr
))
785 len
= strlenW(descr
->items
[index
].str
);
788 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[index
].str
, len
,
789 NULL
, 0, NULL
, NULL
);
792 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
794 _SEH2_TRY
/* hide a Delphi bug that passes a read-only buffer */
798 strcpyW( buffer
, descr
->items
[index
].str
);
799 len
= strlenW(buffer
);
803 len
= WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1,
804 (LPSTR
)buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
807 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
809 WARN( "got an invalid buffer (Delphi bug?)\n" );
810 SetLastError( ERROR_INVALID_PARAMETER
);
816 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
822 static inline INT
LISTBOX_lstrcmpiW( LCID lcid
, LPCWSTR str1
, LPCWSTR str2
)
824 INT ret
= CompareStringW( lcid
, NORM_IGNORECASE
, str1
, -1, str2
, -1 );
825 if (ret
== CSTR_LESS_THAN
)
827 if (ret
== CSTR_EQUAL
)
829 if (ret
== CSTR_GREATER_THAN
)
834 /***********************************************************************
835 * LISTBOX_FindStringPos
837 * Find the nearest string located before a given string in sort order.
838 * If 'exact' is TRUE, return an error if we don't get an exact match.
840 static INT
LISTBOX_FindStringPos( LB_DESCR
*descr
, LPCWSTR str
, BOOL exact
)
842 INT index
, min
, max
, res
;
844 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
846 max
= descr
->nb_items
;
849 index
= (min
+ max
) / 2;
850 if (HAS_STRINGS(descr
))
851 res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, descr
->items
[index
].str
);
854 COMPAREITEMSTRUCT cis
;
855 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
857 cis
.CtlType
= ODT_LISTBOX
;
859 cis
.hwndItem
= descr
->self
;
860 /* note that some application (MetaStock) expects the second item
861 * to be in the listbox */
863 cis
.itemData1
= (ULONG_PTR
)str
;
865 cis
.itemData2
= descr
->items
[index
].data
;
866 cis
.dwLocaleId
= descr
->locale
;
867 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
869 if (!res
) return index
;
870 if (res
< 0) max
= index
;
871 else min
= index
+ 1;
873 return exact
? -1 : max
;
877 /***********************************************************************
878 * LISTBOX_FindFileStrPos
880 * Find the nearest string located before a given string in directory
881 * sort order (i.e. first files, then directories, then drives).
883 static INT
LISTBOX_FindFileStrPos( LB_DESCR
*descr
, LPCWSTR str
)
887 if (!HAS_STRINGS(descr
))
888 return LISTBOX_FindStringPos( descr
, str
, FALSE
);
890 max
= descr
->nb_items
;
893 INT index
= (min
+ max
) / 2;
894 LPCWSTR p
= descr
->items
[index
].str
;
895 if (*p
== '[') /* drive or directory */
897 if (*str
!= '[') res
= -1;
898 else if (p
[1] == '-') /* drive */
900 if (str
[1] == '-') res
= str
[2] - p
[2];
905 if (str
[1] == '-') res
= 1;
906 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
911 if (*str
== '[') res
= 1;
912 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
914 if (!res
) return index
;
915 if (res
< 0) max
= index
;
916 else min
= index
+ 1;
922 /***********************************************************************
925 * Find the item beginning with a given string.
927 static INT
LISTBOX_FindString( LB_DESCR
*descr
, INT start
, LPCWSTR str
, BOOL exact
)
932 if (start
>= descr
->nb_items
) start
= -1;
933 item
= descr
->items
+ start
+ 1;
934 if (HAS_STRINGS(descr
))
936 if (!str
|| ! str
[0] ) return LB_ERR
;
939 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
940 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
941 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
942 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
946 /* Special case for drives and directories: ignore prefix */
947 #define CHECK_DRIVE(item) \
948 if ((item)->str[0] == '[') \
950 if (!strncmpiW( str, (item)->str+1, len )) return i; \
951 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
955 INT len
= strlenW(str
);
956 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
958 if (!strncmpiW( str
, item
->str
, len
)) return i
;
961 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
963 if (!strncmpiW( str
, item
->str
, len
)) return i
;
971 if (exact
&& (descr
->style
& LBS_SORT
))
972 /* If sorted, use a WM_COMPAREITEM binary search */
973 return LISTBOX_FindStringPos( descr
, str
, TRUE
);
975 /* Otherwise use a linear search */
976 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
977 if (item
->data
== (ULONG_PTR
)str
) return i
;
978 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
979 if (item
->data
== (ULONG_PTR
)str
) return i
;
985 /***********************************************************************
986 * LISTBOX_GetSelCount
988 static LRESULT
LISTBOX_GetSelCount( const LB_DESCR
*descr
)
991 const LB_ITEMDATA
*item
= descr
->items
;
993 if (!(descr
->style
& LBS_MULTIPLESEL
) ||
994 (descr
->style
& LBS_NOSEL
))
996 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
997 if (item
->selected
) count
++;
1002 /***********************************************************************
1003 * LISTBOX_GetSelItems
1005 static LRESULT
LISTBOX_GetSelItems( const LB_DESCR
*descr
, INT max
, LPINT array
)
1008 const LB_ITEMDATA
*item
= descr
->items
;
1010 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1011 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
1012 if (item
->selected
) array
[count
++] = i
;
1017 /***********************************************************************
1020 static LRESULT
LISTBOX_Paint( LB_DESCR
*descr
, HDC hdc
)
1022 INT i
, col_pos
= descr
->page_size
- 1;
1024 RECT focusRect
= {-1, -1, -1, -1};
1026 HBRUSH hbrush
, oldBrush
= 0;
1028 if (descr
->style
& LBS_NOREDRAW
) return 0;
1030 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1031 if (descr
->style
& LBS_MULTICOLUMN
)
1032 rect
.right
= rect
.left
+ descr
->column_width
;
1033 else if (descr
->horz_pos
)
1035 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1036 rect
.right
+= descr
->horz_pos
;
1039 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1041 hbrush
= GetControlColor( descr
->owner
, descr
->self
, hdc
, WM_CTLCOLORLISTBOX
);
1043 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
1044 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
1046 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1047 if (!IsWindowEnabled(descr
->self
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1049 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1052 /* Special case for empty listbox: paint focus rect */
1053 rect
.bottom
= rect
.top
+ descr
->item_height
;
1054 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1055 &rect
, NULL
, 0, NULL
);
1056 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1057 rect
.top
= rect
.bottom
;
1060 /* Paint all the item, regarding the selection
1061 Focus state will be painted after */
1063 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1065 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1066 rect
.bottom
= rect
.top
+ descr
->item_height
;
1068 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1070 /* keep the focus rect, to paint the focus item after */
1071 if (i
== descr
->focus_item
)
1074 LISTBOX_PaintItem( descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1075 rect
.top
= rect
.bottom
;
1077 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1079 if (!IS_OWNERDRAW(descr
))
1081 /* Clear the bottom of the column */
1082 if (rect
.top
< descr
->height
)
1084 rect
.bottom
= descr
->height
;
1085 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1086 &rect
, NULL
, 0, NULL
);
1090 /* Go to the next column */
1091 rect
.left
+= descr
->column_width
;
1092 rect
.right
+= descr
->column_width
;
1094 col_pos
= descr
->page_size
- 1;
1099 if (rect
.top
>= descr
->height
) break;
1103 /* Paint the focus item now */
1104 if (focusRect
.top
!= focusRect
.bottom
&&
1105 descr
->caret_on
&& descr
->in_focus
)
1106 LISTBOX_PaintItem( descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1108 if (!IS_OWNERDRAW(descr
))
1110 /* Clear the remainder of the client area */
1111 if (rect
.top
< descr
->height
)
1113 rect
.bottom
= descr
->height
;
1114 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1115 &rect
, NULL
, 0, NULL
);
1117 if (rect
.right
< descr
->width
)
1119 rect
.left
= rect
.right
;
1120 rect
.right
= descr
->width
;
1122 rect
.bottom
= descr
->height
;
1123 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1124 &rect
, NULL
, 0, NULL
);
1127 if (oldFont
) SelectObject( hdc
, oldFont
);
1128 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1133 /***********************************************************************
1134 * LISTBOX_InvalidateItems
1136 * Invalidate all items from a given item. If the specified item is not
1137 * visible, nothing happens.
1139 static void LISTBOX_InvalidateItems( LB_DESCR
*descr
, INT index
)
1143 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1145 if (descr
->style
& LBS_NOREDRAW
)
1147 descr
->style
|= LBS_DISPLAYCHANGED
;
1150 rect
.bottom
= descr
->height
;
1151 InvalidateRect( descr
->self
, &rect
, TRUE
);
1152 if (descr
->style
& LBS_MULTICOLUMN
)
1154 /* Repaint the other columns */
1155 rect
.left
= rect
.right
;
1156 rect
.right
= descr
->width
;
1158 InvalidateRect( descr
->self
, &rect
, TRUE
);
1163 static void LISTBOX_InvalidateItemRect( LB_DESCR
*descr
, INT index
)
1167 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1168 InvalidateRect( descr
->self
, &rect
, TRUE
);
1171 /***********************************************************************
1172 * LISTBOX_GetItemHeight
1174 static LRESULT
LISTBOX_GetItemHeight( const LB_DESCR
*descr
, INT index
)
1176 if (descr
->style
& LBS_OWNERDRAWVARIABLE
&& descr
->nb_items
> 0)
1178 if ((index
< 0) || (index
>= descr
->nb_items
))
1180 SetLastError(ERROR_INVALID_INDEX
);
1183 return descr
->items
[index
].height
;
1185 else return descr
->item_height
;
1189 /***********************************************************************
1190 * LISTBOX_SetItemHeight
1192 static LRESULT
LISTBOX_SetItemHeight( LB_DESCR
*descr
, INT index
, INT height
, BOOL repaint
)
1194 if (height
> MAXBYTE
)
1197 if (!height
) height
= 1;
1199 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1201 if ((index
< 0) || (index
>= descr
->nb_items
))
1203 SetLastError(ERROR_INVALID_INDEX
);
1206 TRACE("[%p]: item %d height = %d\n", descr
->self
, index
, height
);
1207 descr
->items
[index
].height
= height
;
1208 LISTBOX_UpdateScroll( descr
);
1210 LISTBOX_InvalidateItems( descr
, index
);
1212 else if (height
!= descr
->item_height
)
1214 TRACE("[%p]: new height = %d\n", descr
->self
, height
);
1215 descr
->item_height
= height
;
1216 LISTBOX_UpdatePage( descr
);
1217 LISTBOX_UpdateScroll( descr
);
1219 InvalidateRect( descr
->self
, 0, TRUE
);
1225 /***********************************************************************
1226 * LISTBOX_SetHorizontalPos
1228 static void LISTBOX_SetHorizontalPos( LB_DESCR
*descr
, INT pos
)
1232 if (pos
> descr
->horz_extent
- descr
->width
)
1233 pos
= descr
->horz_extent
- descr
->width
;
1234 if (pos
< 0) pos
= 0;
1235 if (!(diff
= descr
->horz_pos
- pos
)) return;
1236 TRACE("[%p]: new horz pos = %d\n", descr
->self
, pos
);
1237 descr
->horz_pos
= pos
;
1238 LISTBOX_UpdateScroll( descr
);
1239 if (abs(diff
) < descr
->width
)
1242 /* Invalidate the focused item so it will be repainted correctly */
1243 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1244 InvalidateRect( descr
->self
, &rect
, TRUE
);
1245 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
1246 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1249 InvalidateRect( descr
->self
, NULL
, TRUE
);
1253 /***********************************************************************
1254 * LISTBOX_SetHorizontalExtent
1256 static LRESULT
LISTBOX_SetHorizontalExtent( LB_DESCR
*descr
, INT extent
)
1258 if (descr
->style
& LBS_MULTICOLUMN
)
1260 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1261 TRACE("[%p]: new horz extent = %d\n", descr
->self
, extent
);
1262 descr
->horz_extent
= extent
;
1263 if (descr
->style
& WS_HSCROLL
) {
1265 info
.cbSize
= sizeof(info
);
1267 info
.nMax
= descr
->horz_extent
? descr
->horz_extent
- 1 : 0;
1268 info
.fMask
= SIF_RANGE
;
1269 if (descr
->style
& LBS_DISABLENOSCROLL
)
1270 info
.fMask
|= SIF_DISABLENOSCROLL
;
1271 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
1273 if (descr
->horz_pos
> extent
- descr
->width
)
1274 LISTBOX_SetHorizontalPos( descr
, extent
- descr
->width
);
1279 /***********************************************************************
1280 * LISTBOX_SetColumnWidth
1282 static LRESULT
LISTBOX_SetColumnWidth( LB_DESCR
*descr
, INT width
)
1284 if (width
== descr
->column_width
) return LB_OKAY
;
1285 TRACE("[%p]: new column width = %d\n", descr
->self
, width
);
1286 descr
->column_width
= width
;
1287 LISTBOX_UpdatePage( descr
);
1292 /***********************************************************************
1295 * Returns the item height.
1297 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1301 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1306 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1308 ERR("unable to get DC.\n" );
1311 if (font
) oldFont
= SelectObject( hdc
, font
);
1312 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1313 if (oldFont
) SelectObject( hdc
, oldFont
);
1314 ReleaseDC( descr
->self
, hdc
);
1316 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1317 if (!IS_OWNERDRAW(descr
))
1318 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1323 /***********************************************************************
1324 * LISTBOX_MakeItemVisible
1326 * Make sure that a given item is partially or fully visible.
1328 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1332 TRACE("current top item %d, index %d, fully %d\n", descr
->top_item
, index
, fully
);
1334 if (index
<= descr
->top_item
) top
= index
;
1335 else if (descr
->style
& LBS_MULTICOLUMN
)
1337 INT cols
= descr
->width
;
1338 if (!fully
) cols
+= descr
->column_width
- 1;
1339 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1341 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1342 top
= index
- descr
->page_size
* (cols
- 1);
1344 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1346 INT height
= fully
? descr
->items
[index
].height
: 1;
1347 for (top
= index
; top
> descr
->top_item
; top
--)
1348 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1352 if (index
< descr
->top_item
+ descr
->page_size
) return;
1353 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1354 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1355 top
= index
- descr
->page_size
+ 1;
1357 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1360 /***********************************************************************
1361 * LISTBOX_SetCaretIndex
1364 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1367 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1369 INT oldfocus
= descr
->focus_item
;
1371 TRACE("old focus %d, index %d\n", oldfocus
, index
);
1373 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1374 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1375 if (index
== oldfocus
) return LB_OKAY
;
1377 LISTBOX_DrawFocusRect( descr
, FALSE
);
1378 descr
->focus_item
= index
;
1380 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1381 LISTBOX_DrawFocusRect( descr
, TRUE
);
1387 /***********************************************************************
1388 * LISTBOX_SelectItemRange
1390 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1392 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1397 /* A few sanity checks */
1399 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1400 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1402 if (!descr
->nb_items
) return LB_OKAY
;
1404 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1405 if (first
< 0) first
= 0;
1406 if (last
< first
) return LB_OKAY
;
1408 if (on
) /* Turn selection on */
1410 for (i
= first
; i
<= last
; i
++)
1412 if (descr
->items
[i
].selected
) continue;
1413 descr
->items
[i
].selected
= TRUE
;
1414 LISTBOX_InvalidateItemRect(descr
, i
);
1417 else /* Turn selection off */
1419 for (i
= first
; i
<= last
; i
++)
1421 if (!descr
->items
[i
].selected
) continue;
1422 descr
->items
[i
].selected
= FALSE
;
1423 LISTBOX_InvalidateItemRect(descr
, i
);
1429 /***********************************************************************
1430 * LISTBOX_SetSelection
1432 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1433 BOOL on
, BOOL send_notify
)
1435 TRACE( "cur_sel=%d index=%d notify=%s\n",
1436 descr
->selected_item
, index
, send_notify
? "YES" : "NO" );
1438 if (descr
->style
& LBS_NOSEL
)
1440 descr
->selected_item
= index
;
1443 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1444 if (descr
->style
& LBS_MULTIPLESEL
)
1446 if (index
== -1) /* Select all items */
1447 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1448 else /* Only one item */
1449 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1453 INT oldsel
= descr
->selected_item
;
1454 if (index
== oldsel
) return LB_OKAY
;
1455 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1456 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1457 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1458 descr
->selected_item
= index
;
1459 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1460 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1461 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1463 if( descr
->lphc
) /* set selection change flag for parent combo */
1464 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1470 /***********************************************************************
1473 * Change the caret position and extend the selection to the new caret.
1475 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1477 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1479 if ((index
< 0) || (index
>= descr
->nb_items
))
1482 /* Important, repaint needs to be done in this order if
1483 you want to mimic Windows behavior:
1484 1. Remove the focus and paint the item
1485 2. Remove the selection and paint the item(s)
1486 3. Set the selection and repaint the item(s)
1487 4. Set the focus to 'index' and repaint the item */
1489 /* 1. remove the focus and repaint the item */
1490 LISTBOX_DrawFocusRect( descr
, FALSE
);
1492 /* 2. then turn off the previous selection */
1493 /* 3. repaint the new selected item */
1494 if (descr
->style
& LBS_EXTENDEDSEL
)
1496 if (descr
->anchor_item
!= -1)
1498 INT first
= min( index
, descr
->anchor_item
);
1499 INT last
= max( index
, descr
->anchor_item
);
1501 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1502 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1503 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1506 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1508 /* Set selection to new caret item */
1509 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1512 /* 4. repaint the new item with the focus */
1513 descr
->focus_item
= index
;
1514 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1515 LISTBOX_DrawFocusRect( descr
, TRUE
);
1519 /***********************************************************************
1520 * LISTBOX_InsertItem
1522 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1523 LPWSTR str
, ULONG_PTR data
)
1527 INT oldfocus
= descr
->focus_item
;
1529 if (index
== -1) index
= descr
->nb_items
;
1530 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1531 if (!descr
->items
) max_items
= 0;
1532 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1533 if (descr
->nb_items
== max_items
)
1535 /* We need to grow the array */
1536 max_items
+= LB_ARRAY_GRANULARITY
;
1538 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1539 max_items
* sizeof(LB_ITEMDATA
) );
1541 item
= HeapAlloc( GetProcessHeap(), 0,
1542 max_items
* sizeof(LB_ITEMDATA
) );
1545 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1548 descr
->items
= item
;
1551 /* Insert the item structure */
1553 item
= &descr
->items
[index
];
1554 if (index
< descr
->nb_items
)
1555 RtlMoveMemory( item
+ 1, item
,
1556 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1560 item
->selected
= FALSE
;
1563 /* Get item height */
1565 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1567 MEASUREITEMSTRUCT mis
;
1568 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1570 mis
.CtlType
= ODT_LISTBOX
;
1573 mis
.itemData
= descr
->items
[index
].data
;
1574 mis
.itemHeight
= descr
->item_height
;
1575 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1576 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1577 TRACE("[%p]: measure item %d (%s) = %d\n",
1578 descr
->self
, index
, str
? debugstr_w(str
) : "", item
->height
);
1581 /* Repaint the items */
1583 LISTBOX_UpdateScroll( descr
);
1584 LISTBOX_InvalidateItems( descr
, index
);
1586 /* Move selection and focused item */
1587 /* If listbox was empty, set focus to the first item */
1588 if (descr
->nb_items
== 1)
1589 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1590 /* single select don't change selection index in win31 */
1591 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1593 descr
->selected_item
++;
1594 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1598 if (index
<= descr
->selected_item
)
1600 descr
->selected_item
++;
1601 descr
->focus_item
= oldfocus
; /* focus not changed */
1608 /***********************************************************************
1609 * LISTBOX_InsertString
1611 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
, LPCWSTR str
)
1613 LPWSTR new_str
= NULL
;
1617 if (HAS_STRINGS(descr
))
1619 static const WCHAR empty_stringW
[] = { 0 };
1620 if (!str
) str
= empty_stringW
;
1621 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1623 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1626 strcpyW(new_str
, str
);
1628 else data
= (ULONG_PTR
)str
;
1630 if (index
== -1) index
= descr
->nb_items
;
1631 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, data
)) != 0)
1633 HeapFree( GetProcessHeap(), 0, new_str
);
1637 TRACE("[%p]: added item %d %s\n",
1638 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1643 /***********************************************************************
1644 * LISTBOX_DeleteItem
1646 * Delete the content of an item. 'index' must be a valid index.
1648 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1650 /* save the item data before it gets freed by LB_RESETCONTENT */
1651 ULONG_PTR item_data
= descr
->items
[index
].data
;
1652 LPWSTR item_str
= descr
->items
[index
].str
;
1654 if (!descr
->nb_items
)
1655 SendMessageW( descr
->self
, LB_RESETCONTENT
, 0, 0 );
1657 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1658 * while Win95 sends it for all items with user data.
1659 * It's probably better to send it too often than not
1660 * often enough, so this is what we do here.
1662 if (IS_OWNERDRAW(descr
) || item_data
)
1664 DELETEITEMSTRUCT dis
;
1665 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1667 dis
.CtlType
= ODT_LISTBOX
;
1670 dis
.hwndItem
= descr
->self
;
1671 dis
.itemData
= item_data
;
1672 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1674 if (HAS_STRINGS(descr
))
1675 HeapFree( GetProcessHeap(), 0, item_str
);
1679 /***********************************************************************
1680 * LISTBOX_RemoveItem
1682 * Remove an item from the listbox and delete its content.
1684 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1689 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1691 /* We need to invalidate the original rect instead of the updated one. */
1692 LISTBOX_InvalidateItems( descr
, index
);
1695 LISTBOX_DeleteItem( descr
, index
);
1697 if (!descr
->nb_items
) return LB_OKAY
;
1699 /* Remove the item */
1701 item
= &descr
->items
[index
];
1702 if (index
< descr
->nb_items
)
1703 RtlMoveMemory( item
, item
+ 1,
1704 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1705 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1707 /* Shrink the item array if possible */
1709 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1710 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1712 max_items
-= LB_ARRAY_GRANULARITY
;
1713 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1714 max_items
* sizeof(LB_ITEMDATA
) );
1715 if (item
) descr
->items
= item
;
1717 /* Repaint the items */
1719 LISTBOX_UpdateScroll( descr
);
1720 /* if we removed the scrollbar, reset the top of the list
1721 (correct for owner-drawn ???) */
1722 if (descr
->nb_items
== descr
->page_size
)
1723 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1725 /* Move selection and focused item */
1726 if (!IS_MULTISELECT(descr
))
1728 if (index
== descr
->selected_item
)
1729 descr
->selected_item
= -1;
1730 else if (index
< descr
->selected_item
)
1732 descr
->selected_item
--;
1733 if (ISWIN31
) /* win 31 do not change the selected item number */
1734 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1738 if (descr
->focus_item
>= descr
->nb_items
)
1740 descr
->focus_item
= descr
->nb_items
- 1;
1741 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1747 /***********************************************************************
1748 * LISTBOX_ResetContent
1750 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1754 for(i
= descr
->nb_items
- 1; i
>=0; i
--) LISTBOX_DeleteItem( descr
, i
);
1755 HeapFree( GetProcessHeap(), 0, descr
->items
);
1756 descr
->nb_items
= 0;
1757 descr
->top_item
= 0;
1758 descr
->selected_item
= -1;
1759 descr
->focus_item
= 0;
1760 descr
->anchor_item
= -1;
1761 descr
->items
= NULL
;
1765 /***********************************************************************
1768 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, INT count
)
1772 if (HAS_STRINGS(descr
))
1774 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1778 /* FIXME: this is far from optimal... */
1779 if (count
> descr
->nb_items
)
1781 while (count
> descr
->nb_items
)
1782 if ((ret
= LISTBOX_InsertString( descr
, -1, 0 )) < 0)
1785 else if (count
< descr
->nb_items
)
1787 while (count
< descr
->nb_items
)
1788 if ((ret
= LISTBOX_RemoveItem( descr
, (descr
->nb_items
- 1) )) < 0)
1792 InvalidateRect( descr
->self
, NULL
, TRUE
);
1797 /***********************************************************************
1800 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1801 LPCWSTR filespec
, BOOL long_names
)
1804 LRESULT ret
= LB_OKAY
;
1805 WIN32_FIND_DATAW entry
;
1807 LRESULT maxinsert
= LB_ERR
;
1809 /* don't scan directory if we just want drives exclusively */
1810 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1811 /* scan directory */
1812 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1814 int le
= GetLastError();
1815 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1822 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1824 static const WCHAR bracketW
[] = { ']',0 };
1825 static const WCHAR dotW
[] = { '.',0 };
1826 if (!(attrib
& DDL_DIRECTORY
) ||
1827 !strcmpW( entry
.cFileName
, dotW
)) continue;
1829 if (!long_names
&& entry
.cAlternateFileName
[0])
1830 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1832 strcpyW( buffer
+ 1, entry
.cFileName
);
1833 strcatW(buffer
, bracketW
);
1835 else /* not a directory */
1837 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1838 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1840 if ((attrib
& DDL_EXCLUSIVE
) &&
1841 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1844 if (!long_names
&& entry
.cAlternateFileName
[0])
1845 strcpyW( buffer
, entry
.cAlternateFileName
);
1847 strcpyW( buffer
, entry
.cFileName
);
1849 if (!long_names
) CharLowerW( buffer
);
1850 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1851 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1853 if (ret
<= maxinsert
) maxinsert
++; else maxinsert
= ret
;
1854 } while (FindNextFileW( handle
, &entry
));
1855 FindClose( handle
);
1863 if (attrib
& DDL_DRIVES
)
1865 WCHAR buffer
[] = {'[','-','a','-',']',0};
1866 WCHAR root
[] = {'A',':','\\',0};
1868 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1870 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1871 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1880 /***********************************************************************
1881 * LISTBOX_HandleVScroll
1883 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1887 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1891 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1894 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1897 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1898 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1901 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1902 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1904 case SB_THUMBPOSITION
:
1905 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1908 info
.cbSize
= sizeof(info
);
1909 info
.fMask
= SIF_TRACKPOS
;
1910 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1911 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1914 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1917 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1924 /***********************************************************************
1925 * LISTBOX_HandleHScroll
1927 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1932 if (descr
->style
& LBS_MULTICOLUMN
)
1937 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
1941 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
1945 page
= descr
->width
/ descr
->column_width
;
1946 if (page
< 1) page
= 1;
1947 LISTBOX_SetTopItem( descr
,
1948 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1951 page
= descr
->width
/ descr
->column_width
;
1952 if (page
< 1) page
= 1;
1953 LISTBOX_SetTopItem( descr
,
1954 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1956 case SB_THUMBPOSITION
:
1957 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
1960 info
.cbSize
= sizeof(info
);
1961 info
.fMask
= SIF_TRACKPOS
;
1962 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1963 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
1967 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1970 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1974 else if (descr
->horz_extent
)
1979 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
1982 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
1985 LISTBOX_SetHorizontalPos( descr
,
1986 descr
->horz_pos
- descr
->width
);
1989 LISTBOX_SetHorizontalPos( descr
,
1990 descr
->horz_pos
+ descr
->width
);
1992 case SB_THUMBPOSITION
:
1993 LISTBOX_SetHorizontalPos( descr
, pos
);
1996 info
.cbSize
= sizeof(info
);
1997 info
.fMask
= SIF_TRACKPOS
;
1998 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
1999 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
2002 LISTBOX_SetHorizontalPos( descr
, 0 );
2005 LISTBOX_SetHorizontalPos( descr
,
2006 descr
->horz_extent
- descr
->width
);
2013 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
2015 UINT pulScrollLines
= 3;
2017 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
2019 /* if scrolling changes direction, ignore left overs */
2020 if ((delta
< 0 && descr
->wheel_remain
< 0) ||
2021 (delta
> 0 && descr
->wheel_remain
> 0))
2022 descr
->wheel_remain
+= delta
;
2024 descr
->wheel_remain
= delta
;
2026 if (descr
->wheel_remain
&& pulScrollLines
)
2029 pulScrollLines
= min((UINT
) descr
->page_size
, pulScrollLines
);
2030 cLineScroll
= pulScrollLines
* (float)descr
->wheel_remain
/ WHEEL_DELTA
;
2031 descr
->wheel_remain
-= WHEEL_DELTA
* cLineScroll
/ (int)pulScrollLines
;
2032 LISTBOX_SetTopItem( descr
, descr
->top_item
- cLineScroll
, TRUE
);
2037 /***********************************************************************
2038 * LISTBOX_HandleLButtonDown
2040 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
2042 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2044 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2045 descr
->self
, x
, y
, index
, descr
->focus_item
);
2047 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2049 if (!descr
->in_focus
)
2051 if( !descr
->lphc
) SetFocus( descr
->self
);
2052 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2055 if (index
== -1) return 0;
2059 if (descr
->style
& LBS_NOTIFY
)
2060 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2061 MAKELPARAM( x
, y
) );
2064 descr
->captured
= TRUE
;
2065 SetCapture( descr
->self
);
2067 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2069 /* we should perhaps make sure that all items are deselected
2070 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2071 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2072 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2075 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2076 if (keys
& MK_CONTROL
)
2078 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2079 LISTBOX_SetSelection( descr
, index
,
2080 !descr
->items
[index
].selected
,
2081 (descr
->style
& LBS_NOTIFY
) != 0);
2085 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2087 if (descr
->style
& LBS_EXTENDEDSEL
)
2089 LISTBOX_SetSelection( descr
, index
,
2090 descr
->items
[index
].selected
,
2091 (descr
->style
& LBS_NOTIFY
) != 0 );
2095 LISTBOX_SetSelection( descr
, index
,
2096 !descr
->items
[index
].selected
,
2097 (descr
->style
& LBS_NOTIFY
) != 0 );
2103 descr
->anchor_item
= index
;
2104 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2105 LISTBOX_SetSelection( descr
, index
,
2106 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2110 { // See rev 40864 use Ptr for 64 bit.
2111 if (GetWindowLongPtrW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2118 if (DragDetect( descr
->self
, pt
))
2119 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2126 /*************************************************************************
2127 * LISTBOX_HandleLButtonDownCombo [Internal]
2129 * Process LButtonDown message for the ComboListBox
2132 * pWnd [I] The windows internal structure
2133 * pDescr [I] The ListBox internal structure
2134 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2135 * x [I] X Mouse Coordinate
2136 * y [I] Y Mouse Coordinate
2139 * 0 since we are processing the WM_LBUTTONDOWN Message
2142 * This function is only to be used when a ListBox is a ComboListBox
2145 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2147 RECT clientRect
, screenRect
;
2153 GetClientRect(descr
->self
, &clientRect
);
2155 if(PtInRect(&clientRect
, mousePos
))
2157 /* MousePos is in client, resume normal processing */
2158 if (msg
== WM_LBUTTONDOWN
)
2160 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2161 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2163 else if (descr
->style
& LBS_NOTIFY
)
2164 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2168 POINT screenMousePos
;
2169 HWND hWndOldCapture
;
2171 /* Check the Non-Client Area */
2172 screenMousePos
= mousePos
;
2173 hWndOldCapture
= GetCapture();
2175 GetWindowRect(descr
->self
, &screenRect
);
2176 ClientToScreen(descr
->self
, &screenMousePos
);
2178 if(!PtInRect(&screenRect
, screenMousePos
))
2180 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2181 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2182 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2186 /* Check to see the NC is a scrollbar */
2188 LONG style
= GetWindowLongPtrW( descr
->self
, GWL_STYLE
);
2189 /* Check Vertical scroll bar */
2190 if (style
& WS_VSCROLL
)
2192 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2193 if (PtInRect( &clientRect
, mousePos
))
2194 nHitTestType
= HTVSCROLL
;
2196 /* Check horizontal scroll bar */
2197 if (style
& WS_HSCROLL
)
2199 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2200 if (PtInRect( &clientRect
, mousePos
))
2201 nHitTestType
= HTHSCROLL
;
2203 /* Windows sends this message when a scrollbar is clicked
2206 if(nHitTestType
!= 0)
2208 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2209 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2211 /* Resume the Capture after scrolling is complete
2213 if(hWndOldCapture
!= 0)
2214 SetCapture(hWndOldCapture
);
2220 /***********************************************************************
2221 * LISTBOX_HandleLButtonUp
2223 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2225 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2226 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2227 LISTBOX_Timer
= LB_TIMER_NONE
;
2228 if (descr
->captured
)
2230 descr
->captured
= FALSE
;
2231 if (GetCapture() == descr
->self
) ReleaseCapture();
2232 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2233 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2239 /***********************************************************************
2240 * LISTBOX_HandleTimer
2242 * Handle scrolling upon a timer event.
2243 * Return TRUE if scrolling should continue.
2245 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2250 if (descr
->top_item
) index
= descr
->top_item
- 1;
2254 if (descr
->top_item
) index
-= descr
->page_size
;
2257 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2258 if (index
== descr
->focus_item
) index
++;
2259 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2261 case LB_TIMER_RIGHT
:
2262 if (index
+ descr
->page_size
< descr
->nb_items
)
2263 index
+= descr
->page_size
;
2268 if (index
== descr
->focus_item
) return FALSE
;
2269 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2274 /***********************************************************************
2275 * LISTBOX_HandleSystemTimer
2277 * WM_SYSTIMER handler.
2279 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2281 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2283 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2284 LISTBOX_Timer
= LB_TIMER_NONE
;
2290 /***********************************************************************
2291 * LISTBOX_HandleMouseMove
2293 * WM_MOUSEMOVE handler.
2295 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2299 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2301 if (!descr
->captured
) return;
2303 if (descr
->style
& LBS_MULTICOLUMN
)
2306 else if (y
>= descr
->item_height
* descr
->page_size
)
2307 y
= descr
->item_height
* descr
->page_size
- 1;
2311 dir
= LB_TIMER_LEFT
;
2314 else if (x
>= descr
->width
)
2316 dir
= LB_TIMER_RIGHT
;
2317 x
= descr
->width
- 1;
2322 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2323 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2326 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2327 if (index
== -1) index
= descr
->focus_item
;
2328 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2330 /* Start/stop the system timer */
2332 if (dir
!= LB_TIMER_NONE
)
2333 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2334 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2335 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2336 LISTBOX_Timer
= dir
;
2340 /***********************************************************************
2341 * LISTBOX_HandleKeyDown
2343 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2346 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2347 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2348 bForceSelection
= FALSE
; /* only for single select list */
2350 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2352 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2353 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2354 (LPARAM
)descr
->self
);
2355 if (caret
== -2) return 0;
2357 if (caret
== -1) switch(key
)
2360 if (descr
->style
& LBS_MULTICOLUMN
)
2362 bForceSelection
= FALSE
;
2363 if (descr
->focus_item
>= descr
->page_size
)
2364 caret
= descr
->focus_item
- descr
->page_size
;
2369 caret
= descr
->focus_item
- 1;
2370 if (caret
< 0) caret
= 0;
2373 if (descr
->style
& LBS_MULTICOLUMN
)
2375 bForceSelection
= FALSE
;
2376 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2377 caret
= descr
->focus_item
+ descr
->page_size
;
2382 caret
= descr
->focus_item
+ 1;
2383 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2387 if (descr
->style
& LBS_MULTICOLUMN
)
2389 INT page
= descr
->width
/ descr
->column_width
;
2390 if (page
< 1) page
= 1;
2391 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2393 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2394 if (caret
< 0) caret
= 0;
2397 if (descr
->style
& LBS_MULTICOLUMN
)
2399 INT page
= descr
->width
/ descr
->column_width
;
2400 if (page
< 1) page
= 1;
2401 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2403 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2404 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2410 caret
= descr
->nb_items
- 1;
2413 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2414 else if (descr
->style
& LBS_MULTIPLESEL
)
2416 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2417 !descr
->items
[descr
->focus_item
].selected
,
2418 (descr
->style
& LBS_NOTIFY
) != 0 );
2422 bForceSelection
= FALSE
;
2424 if (bForceSelection
) /* focused item is used instead of key */
2425 caret
= descr
->focus_item
;
2428 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2429 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2430 !IS_MULTISELECT(descr
))
2431 descr
->anchor_item
= caret
;
2432 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2434 if (descr
->style
& LBS_MULTIPLESEL
)
2435 descr
->selected_item
= caret
;
2437 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2438 if (descr
->style
& LBS_NOTIFY
)
2440 if (descr
->lphc
&& IsWindowVisible( descr
->self
))
2442 /* make sure that combo parent doesn't hide us */
2443 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2445 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2452 /***********************************************************************
2453 * LISTBOX_HandleChar
2455 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2463 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2465 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2466 MAKEWPARAM(charW
, descr
->focus_item
),
2467 (LPARAM
)descr
->self
);
2468 if (caret
== -2) return 0;
2471 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2474 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2475 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2476 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2477 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2478 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2483 /* ReactOS Retrieve the UI state for the control */
2484 static BOOL
LISTBOX_update_uistate(LB_DESCR
*descr
)
2488 prev_flags
= descr
->UIState
;
2489 descr
->UIState
= DefWindowProcW(descr
->self
, WM_QUERYUISTATE
, 0, 0);
2490 return prev_flags
!= descr
->UIState
;
2494 /***********************************************************************
2497 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2500 MEASUREITEMSTRUCT mis
;
2503 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2506 GetClientRect( hwnd
, &rect
);
2508 descr
->owner
= GetParent( descr
->self
);
2509 descr
->style
= GetWindowLongPtrW( descr
->self
, GWL_STYLE
);
2510 descr
->width
= rect
.right
- rect
.left
;
2511 descr
->height
= rect
.bottom
- rect
.top
;
2512 descr
->items
= NULL
;
2513 descr
->nb_items
= 0;
2514 descr
->top_item
= 0;
2515 descr
->selected_item
= -1;
2516 descr
->focus_item
= 0;
2517 descr
->anchor_item
= -1;
2518 descr
->item_height
= 1;
2519 descr
->page_size
= 1;
2520 descr
->column_width
= 150;
2521 descr
->horz_extent
= 0;
2522 descr
->horz_pos
= 0;
2525 descr
->wheel_remain
= 0;
2526 descr
->caret_on
= !lphc
;
2527 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2528 descr
->in_focus
= FALSE
;
2529 descr
->captured
= FALSE
;
2531 descr
->locale
= GetUserDefaultLCID();
2536 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2537 descr
->owner
= lphc
->self
;
2540 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2542 LISTBOX_update_uistate(descr
); // ReactOS
2544 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2546 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2547 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2548 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2551 /* A no-data list box must also have the LBS_OWNERDRAWFIXED style, but must
2552 not have the LBS_SORT or LBS_HASSTRINGS style. */
2553 if ( descr
->style
& LBS_NODATA
&&
2554 (!(descr
->style
& LBS_OWNERDRAWFIXED
) || descr
->style
& (LBS_HASSTRINGS
|LBS_SORT
) ) )
2555 descr
->style
&= ~LBS_NODATA
;
2557 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2559 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2561 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2563 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2564 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2568 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2569 mis
.CtlType
= ODT_LISTBOX
;
2574 mis
.itemHeight
= descr
->item_height
;
2575 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2576 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2580 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2585 /***********************************************************************
2588 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2590 LISTBOX_ResetContent( descr
);
2591 SetWindowLongPtrW( descr
->self
, 0, 0 );
2592 HeapFree( GetProcessHeap(), 0, descr
);
2597 /***********************************************************************
2598 * ListBoxWndProc_common
2600 LRESULT WINAPI
ListBoxWndProc_common( HWND hwnd
, UINT msg
,
2601 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2603 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2604 LPHEADCOMBO lphc
= 0;
2609 pWnd
= ValidateHwnd(hwnd
);
2614 NtUserSetWindowFNID(hwnd
, FNID_LISTBOX
); // Could be FNID_COMBOLBOX by class.
2618 if (pWnd
->fnid
!= FNID_LISTBOX
)
2620 ERR("Wrong window class for listbox! fnId 0x%x\n",pWnd
->fnid
);
2629 if (!IsWindow(hwnd
)) return 0;
2631 if (msg
== WM_CREATE
)
2633 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2634 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= lpcs
->lpCreateParams
;
2635 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2636 TRACE("creating hwnd %p descr %p\n", hwnd
, (void *)GetWindowLongPtrW( hwnd
, 0 ) );
2639 /* Ignore all other messages before we get a WM_CREATE */
2640 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2641 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2643 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2645 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2646 descr
->self
, SPY_GetMsgName(msg
, descr
->self
), wParam
, lParam
);
2650 case LB_RESETCONTENT
:
2651 LISTBOX_ResetContent( descr
);
2652 LISTBOX_UpdateScroll( descr
);
2653 InvalidateRect( descr
->self
, NULL
, TRUE
);
2658 case LB_ADDSTRING_LOWER
:
2659 case LB_ADDSTRING_UPPER
:
2664 if(unicode
|| !HAS_STRINGS(descr
))
2665 textW
= (LPWSTR
)lParam
;
2668 LPSTR textA
= (LPSTR
)lParam
;
2669 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2670 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2671 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2676 /* in the unicode the version, the string is really overwritten
2677 during the converting case */
2678 if (msg
== LB_ADDSTRING_LOWER
)
2680 else if (msg
== LB_ADDSTRING_UPPER
)
2683 wParam
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2684 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2685 if (!unicode
&& HAS_STRINGS(descr
))
2686 HeapFree(GetProcessHeap(), 0, textW
);
2690 case LB_INSERTSTRING
:
2692 case LB_INSERTSTRING_UPPER
:
2693 case LB_INSERTSTRING_LOWER
:
2698 if(unicode
|| !HAS_STRINGS(descr
))
2699 textW
= (LPWSTR
)lParam
;
2702 LPSTR textA
= (LPSTR
)lParam
;
2703 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2704 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2705 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2710 /* in the unicode the version, the string is really overwritten
2711 during the converting case */
2712 if (msg
== LB_INSERTSTRING_LOWER
)
2714 else if (msg
== LB_INSERTSTRING_UPPER
)
2717 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2718 if(!unicode
&& HAS_STRINGS(descr
))
2719 HeapFree(GetProcessHeap(), 0, textW
);
2727 if(unicode
|| !HAS_STRINGS(descr
))
2728 textW
= (LPWSTR
)lParam
;
2731 LPSTR textA
= (LPSTR
)lParam
;
2732 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2733 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2734 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2738 wParam
= LISTBOX_FindFileStrPos( descr
, textW
);
2739 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2740 if(!unicode
&& HAS_STRINGS(descr
))
2741 HeapFree(GetProcessHeap(), 0, textW
);
2745 case LB_DELETESTRING
:
2746 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2747 return descr
->nb_items
;
2750 SetLastError(ERROR_INVALID_INDEX
);
2754 case LB_GETITEMDATA
:
2755 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2757 SetLastError(ERROR_INVALID_INDEX
);
2760 return descr
->items
[wParam
].data
;
2762 case LB_SETITEMDATA
:
2763 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2765 SetLastError(ERROR_INVALID_INDEX
);
2768 descr
->items
[wParam
].data
= lParam
;
2769 /* undocumented: returns TRUE, not LB_OKAY (0) */
2773 return descr
->nb_items
;
2776 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, unicode
);
2779 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2781 SetLastError(ERROR_INVALID_INDEX
);
2784 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2785 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2786 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2787 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2790 if (descr
->nb_items
== 0)
2792 if (!IS_MULTISELECT(descr
))
2793 return descr
->selected_item
;
2794 if (descr
->selected_item
!= -1)
2795 return descr
->selected_item
;
2796 return descr
->focus_item
;
2797 /* otherwise, if the user tries to move the selection with the */
2798 /* arrow keys, we will give the application something to choke on */
2799 case LB_GETTOPINDEX
:
2800 return descr
->top_item
;
2802 case LB_GETITEMHEIGHT
:
2803 return LISTBOX_GetItemHeight( descr
, wParam
);
2805 case LB_SETITEMHEIGHT
:
2806 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2808 case LB_ITEMFROMPOINT
:
2815 /* The hiword of the return value is not a client area
2816 hittest as suggested by MSDN, but rather a hittest on
2817 the returned listbox item. */
2819 if(descr
->nb_items
== 0)
2820 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2822 pt
.x
= (short)LOWORD(lParam
);
2823 pt
.y
= (short)HIWORD(lParam
);
2825 SetRect(&rect
, 0, 0, descr
->width
, descr
->height
);
2827 if(!PtInRect(&rect
, pt
))
2829 pt
.x
= min(pt
.x
, rect
.right
- 1);
2830 pt
.x
= max(pt
.x
, 0);
2831 pt
.y
= min(pt
.y
, rect
.bottom
- 1);
2832 pt
.y
= max(pt
.y
, 0);
2836 index
= LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
);
2840 index
= descr
->nb_items
- 1;
2843 return MAKELONG(index
, hit
? 0 : 1);
2846 case LB_SETCARETINDEX
:
2847 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2848 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2855 case LB_GETCARETINDEX
:
2856 return descr
->focus_item
;
2858 case LB_SETTOPINDEX
:
2859 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2861 case LB_SETCOLUMNWIDTH
:
2862 return LISTBOX_SetColumnWidth( descr
, wParam
);
2864 case LB_GETITEMRECT
:
2865 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2871 if(unicode
|| !HAS_STRINGS(descr
))
2872 textW
= (LPWSTR
)lParam
;
2875 LPSTR textA
= (LPSTR
)lParam
;
2876 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2877 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2878 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2880 ret
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2881 if(!unicode
&& HAS_STRINGS(descr
))
2882 HeapFree(GetProcessHeap(), 0, textW
);
2886 case LB_FINDSTRINGEXACT
:
2890 if(unicode
|| !HAS_STRINGS(descr
))
2891 textW
= (LPWSTR
)lParam
;
2894 LPSTR textA
= (LPSTR
)lParam
;
2895 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2896 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2897 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2899 ret
= LISTBOX_FindString( descr
, wParam
, textW
, TRUE
);
2900 if(!unicode
&& HAS_STRINGS(descr
))
2901 HeapFree(GetProcessHeap(), 0, textW
);
2905 case LB_SELECTSTRING
:
2910 if(HAS_STRINGS(descr
))
2911 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2912 debugstr_a((LPSTR
)lParam
));
2913 if(unicode
|| !HAS_STRINGS(descr
))
2914 textW
= (LPWSTR
)lParam
;
2917 LPSTR textA
= (LPSTR
)lParam
;
2918 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2919 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2920 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2922 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2923 if(!unicode
&& HAS_STRINGS(descr
))
2924 HeapFree(GetProcessHeap(), 0, textW
);
2925 if (index
!= LB_ERR
)
2927 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2928 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2934 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2936 return descr
->items
[wParam
].selected
;
2939 return LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2942 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2943 LISTBOX_SetCaretIndex( descr
, wParam
, TRUE
);
2944 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2945 if (ret
!= LB_ERR
) ret
= descr
->selected_item
;
2948 case LB_GETSELCOUNT
:
2949 return LISTBOX_GetSelCount( descr
);
2951 case LB_GETSELITEMS
:
2952 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2954 case LB_SELITEMRANGE
:
2955 if (LOWORD(lParam
) <= HIWORD(lParam
))
2956 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2957 HIWORD(lParam
), wParam
);
2959 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2960 LOWORD(lParam
), wParam
);
2962 case LB_SELITEMRANGEEX
:
2963 if ((INT
)lParam
>= (INT
)wParam
)
2964 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2966 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
2968 case LB_GETHORIZONTALEXTENT
:
2969 return descr
->horz_extent
;
2971 case LB_SETHORIZONTALEXTENT
:
2972 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
2974 case LB_GETANCHORINDEX
:
2975 return descr
->anchor_item
;
2977 case LB_SETANCHORINDEX
:
2978 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2980 SetLastError(ERROR_INVALID_INDEX
);
2983 descr
->anchor_item
= (INT
)wParam
;
2991 textW
= (LPWSTR
)lParam
;
2994 LPSTR textA
= (LPSTR
)lParam
;
2995 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2996 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2997 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2999 ret
= LISTBOX_Directory( descr
, wParam
, textW
, msg
== LB_DIR
);
3001 HeapFree(GetProcessHeap(), 0, textW
);
3006 return descr
->locale
;
3011 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
3013 ret
= descr
->locale
;
3014 descr
->locale
= (LCID
)wParam
;
3018 case LB_INITSTORAGE
:
3019 return LISTBOX_InitStorage( descr
, wParam
);
3022 return LISTBOX_SetCount( descr
, (INT
)wParam
);
3024 case LB_SETTABSTOPS
:
3025 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
);
3028 if (descr
->caret_on
)
3030 descr
->caret_on
= TRUE
;
3031 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3032 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3036 if (!descr
->caret_on
)
3038 descr
->caret_on
= FALSE
;
3039 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3040 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3043 case LB_GETLISTBOXINFO
:
3044 if (descr
->style
& LBS_MULTICOLUMN
) //// ReactOS
3045 return descr
->page_size
* descr
->column_width
;
3047 return descr
->page_size
;
3050 return LISTBOX_Destroy( descr
);
3053 InvalidateRect( descr
->self
, NULL
, TRUE
);
3057 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
3061 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
3063 case WM_PRINTCLIENT
:
3067 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
3068 ret
= LISTBOX_Paint( descr
, hdc
);
3069 if( !wParam
) EndPaint( descr
->self
, &ps
);
3073 LISTBOX_UpdateSize( descr
);
3076 return (LRESULT
)descr
->font
;
3078 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
3079 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
3082 descr
->in_focus
= TRUE
;
3083 descr
->caret_on
= TRUE
;
3084 if (descr
->focus_item
!= -1)
3085 LISTBOX_DrawFocusRect( descr
, TRUE
);
3086 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
3089 LISTBOX_HandleLButtonUp( descr
); /* Release capture if we have it */
3090 descr
->in_focus
= FALSE
;
3091 descr
->wheel_remain
= 0;
3092 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
3093 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3094 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
3097 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3099 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3101 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
3102 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
3103 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
3104 case WM_LBUTTONDOWN
:
3106 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3107 (INT16
)LOWORD(lParam
),
3108 (INT16
)HIWORD(lParam
) );
3109 return LISTBOX_HandleLButtonDown( descr
, wParam
,
3110 (INT16
)LOWORD(lParam
),
3111 (INT16
)HIWORD(lParam
) );
3112 case WM_LBUTTONDBLCLK
:
3114 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3115 (INT16
)LOWORD(lParam
),
3116 (INT16
)HIWORD(lParam
) );
3117 if (descr
->style
& LBS_NOTIFY
)
3118 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
3121 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
3123 BOOL captured
= descr
->captured
;
3127 mousePos
.x
= (INT16
)LOWORD(lParam
);
3128 mousePos
.y
= (INT16
)HIWORD(lParam
);
3131 * If we are in a dropdown combobox, we simulate that
3132 * the mouse is captured to show the tracking of the item.
3134 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
3135 descr
->captured
= TRUE
;
3137 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
3139 descr
->captured
= captured
;
3141 else if (GetCapture() == descr
->self
)
3143 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
3144 (INT16
)HIWORD(lParam
) );
3154 * If the mouse button "up" is not in the listbox,
3155 * we make sure there is no selection by re-selecting the
3156 * item that was selected when the listbox was made visible.
3158 mousePos
.x
= (INT16
)LOWORD(lParam
);
3159 mousePos
.y
= (INT16
)HIWORD(lParam
);
3161 GetClientRect(descr
->self
, &clientRect
);
3164 * When the user clicks outside the combobox and the focus
3165 * is lost, the owning combobox will send a fake buttonup with
3166 * 0xFFFFFFF as the mouse location, we must also revert the
3167 * selection to the original selection.
3169 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
3170 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
3172 return LISTBOX_HandleLButtonUp( descr
);
3174 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3176 /* for some reason Windows makes it possible to
3177 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3179 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3180 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3181 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3183 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3187 return LISTBOX_HandleKeyDown( descr
, wParam
);
3192 charW
= (WCHAR
)wParam
;
3195 CHAR charA
= (CHAR
)wParam
;
3196 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
3198 return LISTBOX_HandleChar( descr
, charW
);
3201 return LISTBOX_HandleSystemTimer( descr
);
3203 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3207 HBRUSH hbrush
= GetControlColor( descr
->owner
, descr
->self
, (HDC
)wParam
, WM_CTLCOLORLISTBOX
);
3209 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3210 wParam
, (LPARAM
)descr
->self
);
3212 TRACE("hbrush = %p\n", hbrush
);
3214 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3217 GetClientRect(descr
->self
, &rect
);
3218 FillRect((HDC
)wParam
, &rect
, hbrush
);
3223 if( lphc
) return 0;
3224 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3225 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3228 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3231 NtUserSetWindowFNID(hwnd
, FNID_DESTROY
);
3239 case WM_UPDATEUISTATE
:
3241 DefWindowProcW(descr
->self
, msg
, wParam
, lParam
);
3243 DefWindowProcA(descr
->self
, msg
, wParam
, lParam
);
3245 if (LISTBOX_update_uistate(descr
))
3248 if (descr
->focus_item
!= -1)
3249 LISTBOX_DrawFocusRect( descr
, descr
->in_focus
);
3254 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3255 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3256 hwnd
, msg
, wParam
, lParam
);
3259 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3260 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3263 /***********************************************************************
3266 LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3268 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3271 /***********************************************************************
3274 LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3276 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);