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 #include <wine/debug.h>
37 WINE_DEFAULT_DEBUG_CHANNEL(listbox
);
39 /* Items array granularity */
40 #define LB_ARRAY_GRANULARITY 16
42 /* Scrolling timeout in ms */
43 #define LB_SCROLL_TIMEOUT 50
45 /* Listbox system timer id */
48 /* flag listbox changed while setredraw false - internal style */
49 #define LBS_DISPLAYCHANGED 0x80000000
54 LPWSTR str
; /* Item text */
55 BOOL selected
; /* Is item selected? */
56 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
57 ULONG_PTR data
; /* User data */
60 /* Listbox structure */
63 HWND self
; /* Our own window handle */
64 HWND owner
; /* Owner window to send notifications to */
65 UINT style
; /* Window style */
66 INT width
; /* Window width */
67 INT height
; /* Window height */
68 LB_ITEMDATA
*items
; /* Array of items */
69 INT nb_items
; /* Number of items */
70 INT top_item
; /* Top visible item */
71 INT selected_item
; /* Selected item */
72 INT focus_item
; /* Item that has the focus */
73 INT anchor_item
; /* Anchor item for extended selection */
74 INT item_height
; /* Default item height */
75 INT page_size
; /* Items per listbox page */
76 INT column_width
; /* Column width for multi-column listboxes */
77 INT horz_extent
; /* Horizontal extent */
78 INT horz_pos
; /* Horizontal position */
79 INT nb_tabs
; /* Number of tabs in array */
80 INT
*tabs
; /* Array of tabs */
81 INT avg_char_width
; /* Average width of characters */
82 INT wheel_remain
; /* Left over scroll amount */
83 BOOL caret_on
; /* Is caret on? */
84 BOOL captured
; /* Is mouse captured? */
86 HFONT font
; /* Current font */
87 LCID locale
; /* Current locale for string comparisons */
88 LPHEADCOMBO lphc
; /* ComboLBox */
89 LONG UIState
; // REACTOS
93 #define IS_OWNERDRAW(descr) \
94 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
96 #define HAS_STRINGS(descr) \
97 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
100 #define IS_MULTISELECT(descr) \
101 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
102 !((descr)->style & LBS_NOSEL))
104 #define SEND_NOTIFICATION(descr,code) \
105 (SendMessageW( (descr)->owner, WM_COMMAND, \
106 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
108 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
110 /* Current timer status */
120 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
122 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
);
124 /*********************************************************************
125 * listbox class descriptor
127 static const WCHAR listboxW
[] = {'L','i','s','t','B','o','x',0};
128 const struct builtin_class_descr LISTBOX_builtin_class
=
131 CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
132 ListBoxWndProcA
, /* procA */
133 ListBoxWndProcW
, /* procW */
134 sizeof(LB_DESCR
*), /* extra */
135 IDC_ARROW
, /* cursor */
140 /*********************************************************************
141 * combolbox class descriptor
143 static const WCHAR combolboxW
[] = {'C','o','m','b','o','L','B','o','x',0};
144 const struct builtin_class_descr COMBOLBOX_builtin_class
=
146 combolboxW
, /* name */
147 CS_DBLCLKS
| CS_SAVEBITS
, /* style */
148 ListBoxWndProcA
, /* procA */
149 ListBoxWndProcW
, /* procW */
150 sizeof(LB_DESCR
*), /* extra */
151 IDC_ARROW
, /* cursor */
156 /***********************************************************************
157 * LISTBOX_GetCurrentPageSize
159 * Return the current page size
161 static INT
LISTBOX_GetCurrentPageSize( const LB_DESCR
*descr
)
164 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
165 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
167 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
169 if (i
== descr
->top_item
) return 1;
170 else return i
- descr
->top_item
;
174 /***********************************************************************
175 * LISTBOX_GetMaxTopIndex
177 * Return the maximum possible index for the top of the listbox.
179 static INT
LISTBOX_GetMaxTopIndex( const LB_DESCR
*descr
)
183 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
185 page
= descr
->height
;
186 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
187 if ((page
-= descr
->items
[max
].height
) < 0) break;
188 if (max
< descr
->nb_items
- 1) max
++;
190 else if (descr
->style
& LBS_MULTICOLUMN
)
192 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
193 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
194 max
= (max
- page
) * descr
->page_size
;
198 max
= descr
->nb_items
- descr
->page_size
;
200 if (max
< 0) max
= 0;
205 /***********************************************************************
206 * LISTBOX_UpdateScroll
208 * Update the scrollbars. Should be called whenever the content
209 * of the listbox changes.
211 static void LISTBOX_UpdateScroll( LB_DESCR
*descr
)
215 /* Check the listbox scroll bar flags individually before we call
216 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
217 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
218 scroll bar when we do not need one.
219 if (!(descr->style & WS_VSCROLL)) return;
222 /* It is important that we check descr->style, and not wnd->dwStyle,
223 for WS_VSCROLL, as the former is exactly the one passed in
224 argument to CreateWindow.
225 In Windows (and from now on in Wine :) a listbox created
226 with such a style (no WS_SCROLL) does not update
227 the scrollbar with listbox-related data, thus letting
228 the programmer use it for his/her own purposes. */
230 if (descr
->style
& LBS_NOREDRAW
) return;
231 info
.cbSize
= sizeof(info
);
233 if (descr
->style
& LBS_MULTICOLUMN
)
236 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
237 info
.nPos
= descr
->top_item
/ descr
->page_size
;
238 info
.nPage
= descr
->width
/ descr
->column_width
;
239 if (info
.nPage
< 1) info
.nPage
= 1;
240 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
241 if (descr
->style
& LBS_DISABLENOSCROLL
)
242 info
.fMask
|= SIF_DISABLENOSCROLL
;
243 if (descr
->style
& WS_HSCROLL
)
244 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
246 info
.fMask
= SIF_RANGE
;
247 if (descr
->style
& WS_VSCROLL
)
248 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
253 info
.nMax
= descr
->nb_items
- 1;
254 info
.nPos
= descr
->top_item
;
255 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
256 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
257 if (descr
->style
& LBS_DISABLENOSCROLL
)
258 info
.fMask
|= SIF_DISABLENOSCROLL
;
259 if (descr
->style
& WS_VSCROLL
)
260 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
262 if (descr
->style
& WS_HSCROLL
)
264 info
.nPos
= descr
->horz_pos
;
265 info
.nPage
= descr
->width
;
266 info
.fMask
= SIF_POS
| SIF_PAGE
;
267 if (descr
->style
& LBS_DISABLENOSCROLL
)
268 info
.fMask
|= SIF_DISABLENOSCROLL
;
269 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
275 /***********************************************************************
278 * Set the top item of the listbox, scrolling up or down if necessary.
280 static LRESULT
LISTBOX_SetTopItem( LB_DESCR
*descr
, INT index
, BOOL scroll
)
282 INT max
= LISTBOX_GetMaxTopIndex( descr
);
284 TRACE("setting top item %d, scroll %d\n", index
, scroll
);
286 if (index
> max
) index
= max
;
287 if (index
< 0) index
= 0;
288 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
289 if (descr
->top_item
== index
) return LB_OKAY
;
293 if (descr
->style
& LBS_MULTICOLUMN
)
294 diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
295 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
299 if (index
> descr
->top_item
)
301 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
302 diff
-= descr
->items
[i
].height
;
306 for (i
= index
; i
< descr
->top_item
; i
++)
307 diff
+= descr
->items
[i
].height
;
311 diff
= (descr
->top_item
- index
) * descr
->item_height
;
313 ScrollWindowEx( descr
->self
, 0, diff
, NULL
, NULL
, 0, NULL
,
314 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
316 if (!scroll
) InvalidateRect( descr
->self
, NULL
, TRUE
);
317 descr
->top_item
= index
;
318 LISTBOX_UpdateScroll( descr
);
323 /***********************************************************************
326 * Update the page size. Should be called when the size of
327 * the client area or the item height changes.
329 static void LISTBOX_UpdatePage( LB_DESCR
*descr
)
333 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
335 if (page_size
== descr
->page_size
) return;
336 descr
->page_size
= page_size
;
337 if (descr
->style
& LBS_MULTICOLUMN
)
338 InvalidateRect( descr
->self
, NULL
, TRUE
);
339 LISTBOX_SetTopItem( descr
, descr
->top_item
, FALSE
);
343 /***********************************************************************
346 * Update the size of the listbox. Should be called when the size of
347 * the client area changes.
349 static void LISTBOX_UpdateSize( LB_DESCR
*descr
)
353 GetClientRect( descr
->self
, &rect
);
354 descr
->width
= rect
.right
- rect
.left
;
355 descr
->height
= rect
.bottom
- rect
.top
;
356 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
361 GetWindowRect( descr
->self
, &rect
);
362 if(descr
->item_height
!= 0)
363 remaining
= descr
->height
% descr
->item_height
;
366 if ((descr
->height
> descr
->item_height
) && remaining
)
368 TRACE("[%p]: changing height %d -> %d\n",
369 descr
->self
, descr
->height
, descr
->height
- remaining
);
370 SetWindowPos( descr
->self
, 0, 0, 0, rect
.right
- rect
.left
,
371 rect
.bottom
- rect
.top
- remaining
,
372 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
376 TRACE("[%p]: new size = %d,%d\n", descr
->self
, descr
->width
, descr
->height
);
377 LISTBOX_UpdatePage( descr
);
378 LISTBOX_UpdateScroll( descr
);
380 /* Invalidate the focused item so it will be repainted correctly */
381 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
383 InvalidateRect( descr
->self
, &rect
, FALSE
);
388 /***********************************************************************
389 * LISTBOX_GetItemRect
391 * Get the rectangle enclosing an item, in listbox client coordinates.
392 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
394 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
)
396 /* Index <= 0 is legal even on empty listboxes */
397 if (index
&& (index
>= descr
->nb_items
))
399 memset(rect
, 0, sizeof(*rect
));
400 SetLastError(ERROR_INVALID_INDEX
);
403 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
404 if (descr
->style
& LBS_MULTICOLUMN
)
406 INT col
= (index
/ descr
->page_size
) -
407 (descr
->top_item
/ descr
->page_size
);
408 rect
->left
+= col
* descr
->column_width
;
409 rect
->right
= rect
->left
+ descr
->column_width
;
410 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
411 rect
->bottom
= rect
->top
+ descr
->item_height
;
413 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
416 rect
->right
+= descr
->horz_pos
;
417 if ((index
>= 0) && (index
< descr
->nb_items
))
419 if (index
< descr
->top_item
)
421 for (i
= descr
->top_item
-1; i
>= index
; i
--)
422 rect
->top
-= descr
->items
[i
].height
;
426 for (i
= descr
->top_item
; i
< index
; i
++)
427 rect
->top
+= descr
->items
[i
].height
;
429 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
435 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
436 rect
->bottom
= rect
->top
+ descr
->item_height
;
437 rect
->right
+= descr
->horz_pos
;
440 TRACE("item %d, rect %s\n", index
, wine_dbgstr_rect(rect
));
442 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
443 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
447 /***********************************************************************
448 * LISTBOX_GetItemFromPoint
450 * Return the item nearest from point (x,y) (in client coordinates).
452 static INT
LISTBOX_GetItemFromPoint( const LB_DESCR
*descr
, INT x
, INT y
)
454 INT index
= descr
->top_item
;
456 if (!descr
->nb_items
) return -1; /* No items */
457 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
462 while (index
< descr
->nb_items
)
464 if ((pos
+= descr
->items
[index
].height
) > y
) break;
473 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
477 else if (descr
->style
& LBS_MULTICOLUMN
)
479 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
480 if (y
>= 0) index
+= y
/ descr
->item_height
;
481 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
482 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
486 index
+= (y
/ descr
->item_height
);
488 if (index
< 0) return 0;
489 if (index
>= descr
->nb_items
) return -1;
494 /***********************************************************************
499 static void LISTBOX_PaintItem( LB_DESCR
*descr
, HDC hdc
, const RECT
*rect
,
500 INT index
, UINT action
, BOOL ignoreFocus
)
502 LB_ITEMDATA
*item
= NULL
;
503 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
505 if (IS_OWNERDRAW(descr
))
513 if (action
== ODA_FOCUS
)
515 if (!(descr
->UIState
& UISF_HIDEFOCUS
))
516 DrawFocusRect( hdc
, rect
);
519 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
523 /* some programs mess with the clipping region when
524 drawing the item, *and* restore the previous region
525 after they are done, so a region has better to exist
526 else everything ends clipped */
527 GetClientRect(descr
->self
, &r
);
528 hrgn
= set_control_clipping( hdc
, &r
);
530 dis
.CtlType
= ODT_LISTBOX
;
531 dis
.CtlID
= GetWindowLongPtrW( descr
->self
, GWLP_ID
);
532 dis
.hwndItem
= descr
->self
;
533 dis
.itemAction
= action
;
537 if (item
->selected
) dis
.itemState
|= ODS_SELECTED
;
538 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
540 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
541 if (!IsWindowEnabled(descr
->self
)) dis
.itemState
|= ODS_DISABLED
;
542 dis
.itemData
= item
->data
;
544 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
545 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
546 dis
.itemState
, wine_dbgstr_rect(rect
) );
547 SendMessageW(descr
->owner
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
548 SelectClipRgn( hdc
, hrgn
);
549 if (hrgn
) DeleteObject( hrgn
);
553 COLORREF oldText
= 0, oldBk
= 0;
555 if (action
== ODA_FOCUS
)
557 if (!(descr
->UIState
& UISF_HIDEFOCUS
)) // REACTOS
558 DrawFocusRect( hdc
, rect
);
561 if (item
&& item
->selected
)
563 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
564 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
567 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
568 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
569 wine_dbgstr_rect(rect
) );
571 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
572 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
573 else if (!(descr
->style
& LBS_USETABSTOPS
))
574 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
575 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
576 strlenW(item
->str
), NULL
);
579 /* Output empty string to paint background in the full width. */
580 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
581 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
582 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
583 item
->str
, strlenW(item
->str
),
584 descr
->nb_tabs
, descr
->tabs
, 0);
586 if (item
&& item
->selected
)
588 SetBkColor( hdc
, oldBk
);
589 SetTextColor( hdc
, oldText
);
591 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
594 !(descr
->UIState
& UISF_HIDEFOCUS
)) DrawFocusRect( hdc
, rect
);
599 /***********************************************************************
602 * Change the redraw flag.
604 static void LISTBOX_SetRedraw( LB_DESCR
*descr
, BOOL on
)
608 if (!(descr
->style
& LBS_NOREDRAW
)) return;
609 descr
->style
&= ~LBS_NOREDRAW
;
610 if (descr
->style
& LBS_DISPLAYCHANGED
)
611 { /* page was changed while setredraw false, refresh automatically */
612 InvalidateRect(descr
->self
, NULL
, TRUE
);
613 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
614 { /* reset top of page if less than number of items/page */
615 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
616 if (descr
->top_item
< 0) descr
->top_item
= 0;
618 descr
->style
&= ~LBS_DISPLAYCHANGED
;
620 LISTBOX_UpdateScroll( descr
);
622 else descr
->style
|= LBS_NOREDRAW
;
626 /***********************************************************************
627 * LISTBOX_RepaintItem
629 * Repaint a single item synchronously.
631 static void LISTBOX_RepaintItem( LB_DESCR
*descr
, INT index
, UINT action
)
636 HBRUSH hbrush
, oldBrush
= 0;
638 /* Do not repaint the item if the item is not visible */
639 if (!IsWindowVisible(descr
->self
)) return;
640 if (descr
->style
& LBS_NOREDRAW
)
642 descr
->style
|= LBS_DISPLAYCHANGED
;
645 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
646 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
647 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
648 hbrush
= GetControlColor( descr
->owner
, descr
->self
, hdc
, WM_CTLCOLORLISTBOX
);
649 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
650 if (!IsWindowEnabled(descr
->self
))
651 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
652 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
653 LISTBOX_PaintItem( descr
, hdc
, &rect
, index
, action
, TRUE
);
654 if (oldFont
) SelectObject( hdc
, oldFont
);
655 if (oldBrush
) SelectObject( hdc
, oldBrush
);
656 ReleaseDC( descr
->self
, hdc
);
660 /***********************************************************************
661 * LISTBOX_DrawFocusRect
663 static void LISTBOX_DrawFocusRect( LB_DESCR
*descr
, BOOL on
)
669 /* Do not repaint the item if the item is not visible */
670 if (!IsWindowVisible(descr
->self
)) return;
672 if (descr
->focus_item
== -1) return;
673 if (!descr
->caret_on
|| !descr
->in_focus
) return;
675 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) != 1) return;
676 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
677 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
678 if (!IsWindowEnabled(descr
->self
))
679 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
680 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
681 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, on
? FALSE
: TRUE
);
682 if (oldFont
) SelectObject( hdc
, oldFont
);
683 ReleaseDC( descr
->self
, hdc
);
687 /***********************************************************************
688 * LISTBOX_InitStorage
690 static LRESULT
LISTBOX_InitStorage( LB_DESCR
*descr
, INT nb_items
)
694 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
695 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
697 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
698 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
699 nb_items
* sizeof(LB_ITEMDATA
));
702 item
= HeapAlloc( GetProcessHeap(), 0,
703 nb_items
* sizeof(LB_ITEMDATA
));
708 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
716 /***********************************************************************
717 * LISTBOX_SetTabStops
719 static BOOL
LISTBOX_SetTabStops( LB_DESCR
*descr
, INT count
, LPINT tabs
)
723 if (!(descr
->style
& LBS_USETABSTOPS
))
725 SetLastError(ERROR_LB_WITHOUT_TABSTOPS
);
729 HeapFree( GetProcessHeap(), 0, descr
->tabs
);
730 if (!(descr
->nb_tabs
= count
))
735 if (!(descr
->tabs
= HeapAlloc( GetProcessHeap(), 0,
736 descr
->nb_tabs
* sizeof(INT
) )))
738 memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
740 /* convert into "dialog units"*/
741 for (i
= 0; i
< descr
->nb_tabs
; i
++)
742 descr
->tabs
[i
] = MulDiv(descr
->tabs
[i
], descr
->avg_char_width
, 4);
748 /***********************************************************************
751 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPWSTR buffer
, BOOL unicode
)
755 if ((index
< 0) || (index
>= descr
->nb_items
))
757 SetLastError(ERROR_INVALID_INDEX
);
760 if (HAS_STRINGS(descr
))
764 len
= strlenW(descr
->items
[index
].str
);
767 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[index
].str
, len
,
768 NULL
, 0, NULL
, NULL
);
771 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
773 _SEH2_TRY
/* hide a Delphi bug that passes a read-only buffer */
777 strcpyW( buffer
, descr
->items
[index
].str
);
778 len
= strlenW(buffer
);
782 len
= WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1,
783 (LPSTR
)buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
786 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
788 WARN( "got an invalid buffer (Delphi bug?)\n" );
789 SetLastError( ERROR_INVALID_PARAMETER
);
795 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
801 static inline INT
LISTBOX_lstrcmpiW( LCID lcid
, LPCWSTR str1
, LPCWSTR str2
)
803 INT ret
= CompareStringW( lcid
, NORM_IGNORECASE
, str1
, -1, str2
, -1 );
804 if (ret
== CSTR_LESS_THAN
)
806 if (ret
== CSTR_EQUAL
)
808 if (ret
== CSTR_GREATER_THAN
)
813 /***********************************************************************
814 * LISTBOX_FindStringPos
816 * Find the nearest string located before a given string in sort order.
817 * If 'exact' is TRUE, return an error if we don't get an exact match.
819 static INT
LISTBOX_FindStringPos( LB_DESCR
*descr
, LPCWSTR str
, BOOL exact
)
821 INT index
, min
, max
, res
= -1;
823 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
825 max
= descr
->nb_items
;
828 index
= (min
+ max
) / 2;
829 if (HAS_STRINGS(descr
))
830 res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, descr
->items
[index
].str
);
833 COMPAREITEMSTRUCT cis
;
834 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
836 cis
.CtlType
= ODT_LISTBOX
;
838 cis
.hwndItem
= descr
->self
;
839 /* note that some application (MetaStock) expects the second item
840 * to be in the listbox */
842 cis
.itemData1
= (ULONG_PTR
)str
;
844 cis
.itemData2
= descr
->items
[index
].data
;
845 cis
.dwLocaleId
= descr
->locale
;
846 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
848 if (!res
) return index
;
849 if (res
< 0) max
= index
;
850 else min
= index
+ 1;
852 return exact
? -1 : max
;
856 /***********************************************************************
857 * LISTBOX_FindFileStrPos
859 * Find the nearest string located before a given string in directory
860 * sort order (i.e. first files, then directories, then drives).
862 static INT
LISTBOX_FindFileStrPos( LB_DESCR
*descr
, LPCWSTR str
)
864 INT min
, max
, res
= -1;
866 if (!HAS_STRINGS(descr
))
867 return LISTBOX_FindStringPos( descr
, str
, FALSE
);
869 max
= descr
->nb_items
;
872 INT index
= (min
+ max
) / 2;
873 LPCWSTR p
= descr
->items
[index
].str
;
874 if (*p
== '[') /* drive or directory */
876 if (*str
!= '[') res
= -1;
877 else if (p
[1] == '-') /* drive */
879 if (str
[1] == '-') res
= str
[2] - p
[2];
884 if (str
[1] == '-') res
= 1;
885 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
890 if (*str
== '[') res
= 1;
891 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
893 if (!res
) return index
;
894 if (res
< 0) max
= index
;
895 else min
= index
+ 1;
901 /***********************************************************************
904 * Find the item beginning with a given string.
906 static INT
LISTBOX_FindString( LB_DESCR
*descr
, INT start
, LPCWSTR str
, BOOL exact
)
911 if (start
>= descr
->nb_items
) start
= -1;
912 item
= descr
->items
+ start
+ 1;
913 if (HAS_STRINGS(descr
))
915 if (!str
|| ! str
[0] ) return LB_ERR
;
918 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
919 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
920 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
921 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
925 /* Special case for drives and directories: ignore prefix */
926 #define CHECK_DRIVE(item) \
927 if ((item)->str[0] == '[') \
929 if (!strncmpiW( str, (item)->str+1, len )) return i; \
930 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
934 INT len
= strlenW(str
);
935 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
937 if (!strncmpiW( str
, item
->str
, len
)) return i
;
940 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
942 if (!strncmpiW( str
, item
->str
, len
)) return i
;
950 if (exact
&& (descr
->style
& LBS_SORT
))
951 /* If sorted, use a WM_COMPAREITEM binary search */
952 return LISTBOX_FindStringPos( descr
, str
, TRUE
);
954 /* Otherwise use a linear search */
955 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
956 if (item
->data
== (ULONG_PTR
)str
) return i
;
957 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
958 if (item
->data
== (ULONG_PTR
)str
) return i
;
964 /***********************************************************************
965 * LISTBOX_GetSelCount
967 static LRESULT
LISTBOX_GetSelCount( const LB_DESCR
*descr
)
970 const LB_ITEMDATA
*item
= descr
->items
;
972 if (!(descr
->style
& LBS_MULTIPLESEL
) ||
973 (descr
->style
& LBS_NOSEL
))
975 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
976 if (item
->selected
) count
++;
981 /***********************************************************************
982 * LISTBOX_GetSelItems
984 static LRESULT
LISTBOX_GetSelItems( const LB_DESCR
*descr
, INT max
, LPINT array
)
987 const LB_ITEMDATA
*item
= descr
->items
;
989 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
990 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
991 if (item
->selected
) array
[count
++] = i
;
996 /***********************************************************************
999 static LRESULT
LISTBOX_Paint( LB_DESCR
*descr
, HDC hdc
)
1001 INT i
, col_pos
= descr
->page_size
- 1;
1003 RECT focusRect
= {-1, -1, -1, -1};
1005 HBRUSH hbrush
, oldBrush
= 0;
1007 if (descr
->style
& LBS_NOREDRAW
) return 0;
1009 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1010 if (descr
->style
& LBS_MULTICOLUMN
)
1011 rect
.right
= rect
.left
+ descr
->column_width
;
1012 else if (descr
->horz_pos
)
1014 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1015 rect
.right
+= descr
->horz_pos
;
1018 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1019 hbrush
= GetControlColor( descr
->owner
, descr
->self
, hdc
, WM_CTLCOLORLISTBOX
);
1020 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1021 if (!IsWindowEnabled(descr
->self
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1023 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1026 /* Special case for empty listbox: paint focus rect */
1027 rect
.bottom
= rect
.top
+ descr
->item_height
;
1028 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1029 &rect
, NULL
, 0, NULL
);
1030 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1031 rect
.top
= rect
.bottom
;
1034 /* Paint all the item, regarding the selection
1035 Focus state will be painted after */
1037 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1039 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1040 rect
.bottom
= rect
.top
+ descr
->item_height
;
1042 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1044 if (i
== descr
->focus_item
)
1046 /* keep the focus rect, to paint the focus item after */
1047 focusRect
.left
= rect
.left
;
1048 focusRect
.right
= rect
.right
;
1049 focusRect
.top
= rect
.top
;
1050 focusRect
.bottom
= rect
.bottom
;
1052 LISTBOX_PaintItem( descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1053 rect
.top
= rect
.bottom
;
1055 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1057 if (!IS_OWNERDRAW(descr
))
1059 /* Clear the bottom of the column */
1060 if (rect
.top
< descr
->height
)
1062 rect
.bottom
= descr
->height
;
1063 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1064 &rect
, NULL
, 0, NULL
);
1068 /* Go to the next column */
1069 rect
.left
+= descr
->column_width
;
1070 rect
.right
+= descr
->column_width
;
1072 col_pos
= descr
->page_size
- 1;
1077 if (rect
.top
>= descr
->height
) break;
1081 /* Paint the focus item now */
1082 if (focusRect
.top
!= focusRect
.bottom
&&
1083 descr
->caret_on
&& descr
->in_focus
)
1084 LISTBOX_PaintItem( descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1086 if (!IS_OWNERDRAW(descr
))
1088 /* Clear the remainder of the client area */
1089 if (rect
.top
< descr
->height
)
1091 rect
.bottom
= descr
->height
;
1092 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1093 &rect
, NULL
, 0, NULL
);
1095 if (rect
.right
< descr
->width
)
1097 rect
.left
= rect
.right
;
1098 rect
.right
= descr
->width
;
1100 rect
.bottom
= descr
->height
;
1101 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1102 &rect
, NULL
, 0, NULL
);
1105 if (oldFont
) SelectObject( hdc
, oldFont
);
1106 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1111 /***********************************************************************
1112 * LISTBOX_InvalidateItems
1114 * Invalidate all items from a given item. If the specified item is not
1115 * visible, nothing happens.
1117 static void LISTBOX_InvalidateItems( LB_DESCR
*descr
, INT index
)
1121 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1123 if (descr
->style
& LBS_NOREDRAW
)
1125 descr
->style
|= LBS_DISPLAYCHANGED
;
1128 rect
.bottom
= descr
->height
;
1129 InvalidateRect( descr
->self
, &rect
, TRUE
);
1130 if (descr
->style
& LBS_MULTICOLUMN
)
1132 /* Repaint the other columns */
1133 rect
.left
= rect
.right
;
1134 rect
.right
= descr
->width
;
1136 InvalidateRect( descr
->self
, &rect
, TRUE
);
1141 static void LISTBOX_InvalidateItemRect( LB_DESCR
*descr
, INT index
)
1145 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1146 InvalidateRect( descr
->self
, &rect
, TRUE
);
1149 /***********************************************************************
1150 * LISTBOX_GetItemHeight
1152 static LRESULT
LISTBOX_GetItemHeight( const LB_DESCR
*descr
, INT index
)
1154 if (descr
->style
& LBS_OWNERDRAWVARIABLE
&& descr
->nb_items
> 0)
1156 if ((index
< 0) || (index
>= descr
->nb_items
))
1158 SetLastError(ERROR_INVALID_INDEX
);
1161 return descr
->items
[index
].height
;
1163 else return descr
->item_height
;
1167 /***********************************************************************
1168 * LISTBOX_SetItemHeight
1170 static LRESULT
LISTBOX_SetItemHeight( LB_DESCR
*descr
, INT index
, INT height
, BOOL repaint
)
1172 if (height
> MAXBYTE
)
1175 if (!height
) height
= 1;
1177 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1179 if ((index
< 0) || (index
>= descr
->nb_items
))
1181 SetLastError(ERROR_INVALID_INDEX
);
1184 TRACE("[%p]: item %d height = %d\n", descr
->self
, index
, height
);
1185 descr
->items
[index
].height
= height
;
1186 LISTBOX_UpdateScroll( descr
);
1188 LISTBOX_InvalidateItems( descr
, index
);
1190 else if (height
!= descr
->item_height
)
1192 TRACE("[%p]: new height = %d\n", descr
->self
, height
);
1193 descr
->item_height
= height
;
1194 LISTBOX_UpdatePage( descr
);
1195 LISTBOX_UpdateScroll( descr
);
1197 InvalidateRect( descr
->self
, 0, TRUE
);
1203 /***********************************************************************
1204 * LISTBOX_SetHorizontalPos
1206 static void LISTBOX_SetHorizontalPos( LB_DESCR
*descr
, INT pos
)
1210 if (pos
> descr
->horz_extent
- descr
->width
)
1211 pos
= descr
->horz_extent
- descr
->width
;
1212 if (pos
< 0) pos
= 0;
1213 if (!(diff
= descr
->horz_pos
- pos
)) return;
1214 TRACE("[%p]: new horz pos = %d\n", descr
->self
, pos
);
1215 descr
->horz_pos
= pos
;
1216 LISTBOX_UpdateScroll( descr
);
1217 if (abs(diff
) < descr
->width
)
1220 /* Invalidate the focused item so it will be repainted correctly */
1221 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1222 InvalidateRect( descr
->self
, &rect
, TRUE
);
1223 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
1224 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1227 InvalidateRect( descr
->self
, NULL
, TRUE
);
1231 /***********************************************************************
1232 * LISTBOX_SetHorizontalExtent
1234 static LRESULT
LISTBOX_SetHorizontalExtent( LB_DESCR
*descr
, INT extent
)
1236 if (descr
->style
& LBS_MULTICOLUMN
)
1238 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1239 TRACE("[%p]: new horz extent = %d\n", descr
->self
, extent
);
1240 descr
->horz_extent
= extent
;
1241 if (descr
->style
& WS_HSCROLL
) {
1243 info
.cbSize
= sizeof(info
);
1245 info
.nMax
= descr
->horz_extent
? descr
->horz_extent
- 1 : 0;
1246 info
.fMask
= SIF_RANGE
;
1247 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
1249 if (descr
->horz_pos
> extent
- descr
->width
)
1250 LISTBOX_SetHorizontalPos( descr
, extent
- descr
->width
);
1255 /***********************************************************************
1256 * LISTBOX_SetColumnWidth
1258 static LRESULT
LISTBOX_SetColumnWidth( LB_DESCR
*descr
, INT width
)
1260 if (width
== descr
->column_width
) return LB_OKAY
;
1261 TRACE("[%p]: new column width = %d\n", descr
->self
, width
);
1262 descr
->column_width
= width
;
1263 LISTBOX_UpdatePage( descr
);
1268 /***********************************************************************
1271 * Returns the item height.
1273 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1277 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1282 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1284 ERR("unable to get DC.\n" );
1287 if (font
) oldFont
= SelectObject( hdc
, font
);
1288 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1289 if (oldFont
) SelectObject( hdc
, oldFont
);
1290 ReleaseDC( descr
->self
, hdc
);
1292 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1293 if (!IS_OWNERDRAW(descr
))
1294 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1299 /***********************************************************************
1300 * LISTBOX_MakeItemVisible
1302 * Make sure that a given item is partially or fully visible.
1304 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1308 TRACE("current top item %d, index %d, fully %d\n", descr
->top_item
, index
, fully
);
1310 if (index
<= descr
->top_item
) top
= index
;
1311 else if (descr
->style
& LBS_MULTICOLUMN
)
1313 INT cols
= descr
->width
;
1314 if (!fully
) cols
+= descr
->column_width
- 1;
1315 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1317 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1318 top
= index
- descr
->page_size
* (cols
- 1);
1320 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1322 INT height
= fully
? descr
->items
[index
].height
: 1;
1323 for (top
= index
; top
> descr
->top_item
; top
--)
1324 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1328 if (index
< descr
->top_item
+ descr
->page_size
) return;
1329 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1330 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1331 top
= index
- descr
->page_size
+ 1;
1333 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1336 /***********************************************************************
1337 * LISTBOX_SetCaretIndex
1340 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1343 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1345 INT oldfocus
= descr
->focus_item
;
1347 TRACE("old focus %d, index %d\n", oldfocus
, index
);
1349 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1350 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1351 if (index
== oldfocus
) return LB_OKAY
;
1353 LISTBOX_DrawFocusRect( descr
, FALSE
);
1354 descr
->focus_item
= index
;
1356 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1357 LISTBOX_DrawFocusRect( descr
, TRUE
);
1363 /***********************************************************************
1364 * LISTBOX_SelectItemRange
1366 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1368 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1373 /* A few sanity checks */
1375 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1376 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1378 if (!descr
->nb_items
) return LB_OKAY
;
1380 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1381 if (first
< 0) first
= 0;
1382 if (last
< first
) return LB_OKAY
;
1384 if (on
) /* Turn selection on */
1386 for (i
= first
; i
<= last
; i
++)
1388 if (descr
->items
[i
].selected
) continue;
1389 descr
->items
[i
].selected
= TRUE
;
1390 LISTBOX_InvalidateItemRect(descr
, i
);
1393 else /* Turn selection off */
1395 for (i
= first
; i
<= last
; i
++)
1397 if (!descr
->items
[i
].selected
) continue;
1398 descr
->items
[i
].selected
= FALSE
;
1399 LISTBOX_InvalidateItemRect(descr
, i
);
1405 /***********************************************************************
1406 * LISTBOX_SetSelection
1408 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1409 BOOL on
, BOOL send_notify
)
1411 TRACE( "cur_sel=%d index=%d notify=%s\n",
1412 descr
->selected_item
, index
, send_notify
? "YES" : "NO" );
1414 if (descr
->style
& LBS_NOSEL
)
1416 descr
->selected_item
= index
;
1419 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1420 if (descr
->style
& LBS_MULTIPLESEL
)
1422 if (index
== -1) /* Select all items */
1423 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1424 else /* Only one item */
1425 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1429 INT oldsel
= descr
->selected_item
;
1430 if (index
== oldsel
) return LB_OKAY
;
1431 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1432 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1433 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1434 descr
->selected_item
= index
;
1435 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1436 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1437 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1439 if( descr
->lphc
) /* set selection change flag for parent combo */
1440 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1446 /***********************************************************************
1449 * Change the caret position and extend the selection to the new caret.
1451 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1453 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1455 if ((index
< 0) || (index
>= descr
->nb_items
))
1458 /* Important, repaint needs to be done in this order if
1459 you want to mimic Windows behavior:
1460 1. Remove the focus and paint the item
1461 2. Remove the selection and paint the item(s)
1462 3. Set the selection and repaint the item(s)
1463 4. Set the focus to 'index' and repaint the item */
1465 /* 1. remove the focus and repaint the item */
1466 LISTBOX_DrawFocusRect( descr
, FALSE
);
1468 /* 2. then turn off the previous selection */
1469 /* 3. repaint the new selected item */
1470 if (descr
->style
& LBS_EXTENDEDSEL
)
1472 if (descr
->anchor_item
!= -1)
1474 INT first
= min( index
, descr
->anchor_item
);
1475 INT last
= max( index
, descr
->anchor_item
);
1477 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1478 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1479 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1482 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1484 /* Set selection to new caret item */
1485 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1488 /* 4. repaint the new item with the focus */
1489 descr
->focus_item
= index
;
1490 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1491 LISTBOX_DrawFocusRect( descr
, TRUE
);
1495 /***********************************************************************
1496 * LISTBOX_InsertItem
1498 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1499 LPWSTR str
, ULONG_PTR data
)
1503 INT oldfocus
= descr
->focus_item
;
1505 if (index
== -1) index
= descr
->nb_items
;
1506 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1507 if (!descr
->items
) max_items
= 0;
1508 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1509 if (descr
->nb_items
== max_items
)
1511 /* We need to grow the array */
1512 max_items
+= LB_ARRAY_GRANULARITY
;
1514 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1515 max_items
* sizeof(LB_ITEMDATA
) );
1517 item
= HeapAlloc( GetProcessHeap(), 0,
1518 max_items
* sizeof(LB_ITEMDATA
) );
1521 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1524 descr
->items
= item
;
1527 /* Insert the item structure */
1529 item
= &descr
->items
[index
];
1530 if (index
< descr
->nb_items
)
1531 RtlMoveMemory( item
+ 1, item
,
1532 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1536 item
->selected
= FALSE
;
1539 /* Get item height */
1541 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1543 MEASUREITEMSTRUCT mis
;
1544 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1546 mis
.CtlType
= ODT_LISTBOX
;
1549 mis
.itemData
= descr
->items
[index
].data
;
1550 mis
.itemHeight
= descr
->item_height
;
1551 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1552 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1553 TRACE("[%p]: measure item %d (%s) = %d\n",
1554 descr
->self
, index
, str
? debugstr_w(str
) : "", item
->height
);
1557 /* Repaint the items */
1559 LISTBOX_UpdateScroll( descr
);
1560 LISTBOX_InvalidateItems( descr
, index
);
1562 /* Move selection and focused item */
1563 /* If listbox was empty, set focus to the first item */
1564 if (descr
->nb_items
== 1)
1565 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1566 /* single select don't change selection index in win31 */
1567 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1569 descr
->selected_item
++;
1570 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1574 if (index
<= descr
->selected_item
)
1576 descr
->selected_item
++;
1577 descr
->focus_item
= oldfocus
; /* focus not changed */
1584 /***********************************************************************
1585 * LISTBOX_InsertString
1587 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
, LPCWSTR str
)
1589 LPWSTR new_str
= NULL
;
1593 if (HAS_STRINGS(descr
))
1595 static const WCHAR empty_stringW
[] = { 0 };
1596 if (!str
) str
= empty_stringW
;
1597 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1599 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1602 strcpyW(new_str
, str
);
1604 else data
= (ULONG_PTR
)str
;
1606 if (index
== -1) index
= descr
->nb_items
;
1607 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, data
)) != 0)
1609 HeapFree( GetProcessHeap(), 0, new_str
);
1613 TRACE("[%p]: added item %d %s\n",
1614 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1619 /***********************************************************************
1620 * LISTBOX_DeleteItem
1622 * Delete the content of an item. 'index' must be a valid index.
1624 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1626 /* save the item data before it gets freed by LB_RESETCONTENT */
1627 ULONG_PTR item_data
= descr
->items
[index
].data
;
1628 LPWSTR item_str
= descr
->items
[index
].str
;
1630 if (!descr
->nb_items
)
1631 SendMessageW( descr
->self
, LB_RESETCONTENT
, 0, 0 );
1633 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1634 * while Win95 sends it for all items with user data.
1635 * It's probably better to send it too often than not
1636 * often enough, so this is what we do here.
1638 if (IS_OWNERDRAW(descr
) || item_data
)
1640 DELETEITEMSTRUCT dis
;
1641 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1643 dis
.CtlType
= ODT_LISTBOX
;
1646 dis
.hwndItem
= descr
->self
;
1647 dis
.itemData
= item_data
;
1648 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1650 if (HAS_STRINGS(descr
))
1651 HeapFree( GetProcessHeap(), 0, item_str
);
1655 /***********************************************************************
1656 * LISTBOX_RemoveItem
1658 * Remove an item from the listbox and delete its content.
1660 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1665 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1667 /* We need to invalidate the original rect instead of the updated one. */
1668 LISTBOX_InvalidateItems( descr
, index
);
1671 LISTBOX_DeleteItem( descr
, index
);
1673 if (!descr
->nb_items
) return LB_OKAY
;
1675 /* Remove the item */
1677 item
= &descr
->items
[index
];
1678 if (index
< descr
->nb_items
)
1679 RtlMoveMemory( item
, item
+ 1,
1680 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1681 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1683 /* Shrink the item array if possible */
1685 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1686 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1688 max_items
-= LB_ARRAY_GRANULARITY
;
1689 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1690 max_items
* sizeof(LB_ITEMDATA
) );
1691 if (item
) descr
->items
= item
;
1693 /* Repaint the items */
1695 LISTBOX_UpdateScroll( descr
);
1696 /* if we removed the scrollbar, reset the top of the list
1697 (correct for owner-drawn ???) */
1698 if (descr
->nb_items
== descr
->page_size
)
1699 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1701 /* Move selection and focused item */
1702 if (!IS_MULTISELECT(descr
))
1704 if (index
== descr
->selected_item
)
1705 descr
->selected_item
= -1;
1706 else if (index
< descr
->selected_item
)
1708 descr
->selected_item
--;
1709 if (ISWIN31
) /* win 31 do not change the selected item number */
1710 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1714 if (descr
->focus_item
>= descr
->nb_items
)
1716 descr
->focus_item
= descr
->nb_items
- 1;
1717 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1723 /***********************************************************************
1724 * LISTBOX_ResetContent
1726 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1730 for(i
= descr
->nb_items
- 1; i
>=0; i
--) LISTBOX_DeleteItem( descr
, i
);
1731 HeapFree( GetProcessHeap(), 0, descr
->items
);
1732 descr
->nb_items
= 0;
1733 descr
->top_item
= 0;
1734 descr
->selected_item
= -1;
1735 descr
->focus_item
= 0;
1736 descr
->anchor_item
= -1;
1737 descr
->items
= NULL
;
1741 /***********************************************************************
1744 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, INT count
)
1748 if (HAS_STRINGS(descr
))
1750 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1754 /* FIXME: this is far from optimal... */
1755 if (count
> descr
->nb_items
)
1757 while (count
> descr
->nb_items
)
1758 if ((ret
= LISTBOX_InsertString( descr
, -1, 0 )) < 0)
1761 else if (count
< descr
->nb_items
)
1763 while (count
< descr
->nb_items
)
1764 if ((ret
= LISTBOX_RemoveItem( descr
, (descr
->nb_items
- 1) )) < 0)
1768 InvalidateRect( descr
->self
, NULL
, TRUE
);
1773 /***********************************************************************
1776 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1777 LPCWSTR filespec
, BOOL long_names
)
1780 LRESULT ret
= LB_OKAY
;
1781 WIN32_FIND_DATAW entry
;
1783 LRESULT maxinsert
= LB_ERR
;
1785 /* don't scan directory if we just want drives exclusively */
1786 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1787 /* scan directory */
1788 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1790 int le
= GetLastError();
1791 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1798 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1800 static const WCHAR bracketW
[] = { ']',0 };
1801 static const WCHAR dotW
[] = { '.',0 };
1802 if (!(attrib
& DDL_DIRECTORY
) ||
1803 !strcmpW( entry
.cFileName
, dotW
)) continue;
1805 if (!long_names
&& entry
.cAlternateFileName
[0])
1806 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1808 strcpyW( buffer
+ 1, entry
.cFileName
);
1809 strcatW(buffer
, bracketW
);
1811 else /* not a directory */
1813 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1814 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1816 if ((attrib
& DDL_EXCLUSIVE
) &&
1817 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1820 if (!long_names
&& entry
.cAlternateFileName
[0])
1821 strcpyW( buffer
, entry
.cAlternateFileName
);
1823 strcpyW( buffer
, entry
.cFileName
);
1825 if (!long_names
) CharLowerW( buffer
);
1826 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1827 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1829 if (ret
<= maxinsert
) maxinsert
++; else maxinsert
= ret
;
1830 } while (FindNextFileW( handle
, &entry
));
1831 FindClose( handle
);
1839 if (attrib
& DDL_DRIVES
)
1841 WCHAR buffer
[] = {'[','-','a','-',']',0};
1842 WCHAR root
[] = {'A',':','\\',0};
1844 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1846 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1847 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1856 /***********************************************************************
1857 * LISTBOX_HandleVScroll
1859 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1863 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1867 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1870 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1873 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1874 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1877 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1878 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1880 case SB_THUMBPOSITION
:
1881 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1884 info
.cbSize
= sizeof(info
);
1885 info
.fMask
= SIF_TRACKPOS
;
1886 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1887 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1890 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1893 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1900 /***********************************************************************
1901 * LISTBOX_HandleHScroll
1903 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1908 if (descr
->style
& LBS_MULTICOLUMN
)
1913 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
1917 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
1921 page
= descr
->width
/ descr
->column_width
;
1922 if (page
< 1) page
= 1;
1923 LISTBOX_SetTopItem( descr
,
1924 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1927 page
= descr
->width
/ descr
->column_width
;
1928 if (page
< 1) page
= 1;
1929 LISTBOX_SetTopItem( descr
,
1930 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1932 case SB_THUMBPOSITION
:
1933 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
1936 info
.cbSize
= sizeof(info
);
1937 info
.fMask
= SIF_TRACKPOS
;
1938 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1939 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
1943 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1946 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1950 else if (descr
->horz_extent
)
1955 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
1958 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
1961 LISTBOX_SetHorizontalPos( descr
,
1962 descr
->horz_pos
- descr
->width
);
1965 LISTBOX_SetHorizontalPos( descr
,
1966 descr
->horz_pos
+ descr
->width
);
1968 case SB_THUMBPOSITION
:
1969 LISTBOX_SetHorizontalPos( descr
, pos
);
1972 info
.cbSize
= sizeof(info
);
1973 info
.fMask
= SIF_TRACKPOS
;
1974 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
1975 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
1978 LISTBOX_SetHorizontalPos( descr
, 0 );
1981 LISTBOX_SetHorizontalPos( descr
,
1982 descr
->horz_extent
- descr
->width
);
1989 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
1991 UINT pulScrollLines
= 3;
1993 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1995 /* if scrolling changes direction, ignore left overs */
1996 if ((delta
< 0 && descr
->wheel_remain
< 0) ||
1997 (delta
> 0 && descr
->wheel_remain
> 0))
1998 descr
->wheel_remain
+= delta
;
2000 descr
->wheel_remain
= delta
;
2002 if (descr
->wheel_remain
&& pulScrollLines
)
2005 pulScrollLines
= min((UINT
) descr
->page_size
, pulScrollLines
);
2006 cLineScroll
= pulScrollLines
* (float)descr
->wheel_remain
/ WHEEL_DELTA
;
2007 descr
->wheel_remain
-= WHEEL_DELTA
* cLineScroll
/ (int)pulScrollLines
;
2008 LISTBOX_SetTopItem( descr
, descr
->top_item
- cLineScroll
, TRUE
);
2013 /***********************************************************************
2014 * LISTBOX_HandleLButtonDown
2016 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
2018 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2020 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2021 descr
->self
, x
, y
, index
, descr
->focus_item
);
2023 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2025 if (!descr
->in_focus
)
2027 if( !descr
->lphc
) SetFocus( descr
->self
);
2028 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2031 if (index
== -1) return 0;
2035 if (descr
->style
& LBS_NOTIFY
)
2036 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2037 MAKELPARAM( x
, y
) );
2040 descr
->captured
= TRUE
;
2041 SetCapture( descr
->self
);
2043 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2045 /* we should perhaps make sure that all items are deselected
2046 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2047 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2048 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2051 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2052 if (keys
& MK_CONTROL
)
2054 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2055 LISTBOX_SetSelection( descr
, index
,
2056 !descr
->items
[index
].selected
,
2057 (descr
->style
& LBS_NOTIFY
) != 0);
2061 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2063 if (descr
->style
& LBS_EXTENDEDSEL
)
2065 LISTBOX_SetSelection( descr
, index
,
2066 descr
->items
[index
].selected
,
2067 (descr
->style
& LBS_NOTIFY
) != 0 );
2071 LISTBOX_SetSelection( descr
, index
,
2072 !descr
->items
[index
].selected
,
2073 (descr
->style
& LBS_NOTIFY
) != 0 );
2079 descr
->anchor_item
= index
;
2080 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2081 LISTBOX_SetSelection( descr
, index
,
2082 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2086 { // See rev 40864 use Ptr for 64 bit.
2087 if (GetWindowLongPtrW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2094 if (DragDetect( descr
->self
, pt
))
2095 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2102 /*************************************************************************
2103 * LISTBOX_HandleLButtonDownCombo [Internal]
2105 * Process LButtonDown message for the ComboListBox
2108 * pWnd [I] The windows internal structure
2109 * pDescr [I] The ListBox internal structure
2110 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2111 * x [I] X Mouse Coordinate
2112 * y [I] Y Mouse Coordinate
2115 * 0 since we are processing the WM_LBUTTONDOWN Message
2118 * This function is only to be used when a ListBox is a ComboListBox
2121 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2123 RECT clientRect
, screenRect
;
2129 GetClientRect(descr
->self
, &clientRect
);
2131 if(PtInRect(&clientRect
, mousePos
))
2133 /* MousePos is in client, resume normal processing */
2134 if (msg
== WM_LBUTTONDOWN
)
2136 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2137 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2139 else if (descr
->style
& LBS_NOTIFY
)
2140 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2144 POINT screenMousePos
;
2145 HWND hWndOldCapture
;
2147 /* Check the Non-Client Area */
2148 screenMousePos
= mousePos
;
2149 hWndOldCapture
= GetCapture();
2151 GetWindowRect(descr
->self
, &screenRect
);
2152 ClientToScreen(descr
->self
, &screenMousePos
);
2154 if(!PtInRect(&screenRect
, screenMousePos
))
2156 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2157 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2158 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2162 /* Check to see the NC is a scrollbar */
2164 LONG style
= GetWindowLongPtrW( descr
->self
, GWL_STYLE
);
2165 /* Check Vertical scroll bar */
2166 if (style
& WS_VSCROLL
)
2168 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2169 if (PtInRect( &clientRect
, mousePos
))
2170 nHitTestType
= HTVSCROLL
;
2172 /* Check horizontal scroll bar */
2173 if (style
& WS_HSCROLL
)
2175 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2176 if (PtInRect( &clientRect
, mousePos
))
2177 nHitTestType
= HTHSCROLL
;
2179 /* Windows sends this message when a scrollbar is clicked
2182 if(nHitTestType
!= 0)
2184 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2185 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2187 /* Resume the Capture after scrolling is complete
2189 if(hWndOldCapture
!= 0)
2190 SetCapture(hWndOldCapture
);
2196 /***********************************************************************
2197 * LISTBOX_HandleLButtonUp
2199 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2201 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2202 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2203 LISTBOX_Timer
= LB_TIMER_NONE
;
2204 if (descr
->captured
)
2206 descr
->captured
= FALSE
;
2207 if (GetCapture() == descr
->self
) ReleaseCapture();
2208 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2209 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2215 /***********************************************************************
2216 * LISTBOX_HandleTimer
2218 * Handle scrolling upon a timer event.
2219 * Return TRUE if scrolling should continue.
2221 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2226 if (descr
->top_item
) index
= descr
->top_item
- 1;
2230 if (descr
->top_item
) index
-= descr
->page_size
;
2233 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2234 if (index
== descr
->focus_item
) index
++;
2235 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2237 case LB_TIMER_RIGHT
:
2238 if (index
+ descr
->page_size
< descr
->nb_items
)
2239 index
+= descr
->page_size
;
2244 if (index
== descr
->focus_item
) return FALSE
;
2245 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2250 /***********************************************************************
2251 * LISTBOX_HandleSystemTimer
2253 * WM_SYSTIMER handler.
2255 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2257 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2259 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2260 LISTBOX_Timer
= LB_TIMER_NONE
;
2266 /***********************************************************************
2267 * LISTBOX_HandleMouseMove
2269 * WM_MOUSEMOVE handler.
2271 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2275 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2277 if (!descr
->captured
) return;
2279 if (descr
->style
& LBS_MULTICOLUMN
)
2282 else if (y
>= descr
->item_height
* descr
->page_size
)
2283 y
= descr
->item_height
* descr
->page_size
- 1;
2287 dir
= LB_TIMER_LEFT
;
2290 else if (x
>= descr
->width
)
2292 dir
= LB_TIMER_RIGHT
;
2293 x
= descr
->width
- 1;
2298 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2299 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2302 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2303 if (index
== -1) index
= descr
->focus_item
;
2304 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2306 /* Start/stop the system timer */
2308 if (dir
!= LB_TIMER_NONE
)
2309 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2310 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2311 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2312 LISTBOX_Timer
= dir
;
2316 /***********************************************************************
2317 * LISTBOX_HandleKeyDown
2319 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2322 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2323 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2324 bForceSelection
= FALSE
; /* only for single select list */
2326 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2328 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2329 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2330 (LPARAM
)descr
->self
);
2331 if (caret
== -2) return 0;
2333 if (caret
== -1) switch(key
)
2336 if (descr
->style
& LBS_MULTICOLUMN
)
2338 bForceSelection
= FALSE
;
2339 if (descr
->focus_item
>= descr
->page_size
)
2340 caret
= descr
->focus_item
- descr
->page_size
;
2345 caret
= descr
->focus_item
- 1;
2346 if (caret
< 0) caret
= 0;
2349 if (descr
->style
& LBS_MULTICOLUMN
)
2351 bForceSelection
= FALSE
;
2352 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2353 caret
= descr
->focus_item
+ descr
->page_size
;
2358 caret
= descr
->focus_item
+ 1;
2359 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2363 if (descr
->style
& LBS_MULTICOLUMN
)
2365 INT page
= descr
->width
/ descr
->column_width
;
2366 if (page
< 1) page
= 1;
2367 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2369 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2370 if (caret
< 0) caret
= 0;
2373 if (descr
->style
& LBS_MULTICOLUMN
)
2375 INT page
= descr
->width
/ descr
->column_width
;
2376 if (page
< 1) page
= 1;
2377 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2379 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2380 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2386 caret
= descr
->nb_items
- 1;
2389 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2390 else if (descr
->style
& LBS_MULTIPLESEL
)
2392 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2393 !descr
->items
[descr
->focus_item
].selected
,
2394 (descr
->style
& LBS_NOTIFY
) != 0 );
2398 bForceSelection
= FALSE
;
2400 if (bForceSelection
) /* focused item is used instead of key */
2401 caret
= descr
->focus_item
;
2404 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2405 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2406 !IS_MULTISELECT(descr
))
2407 descr
->anchor_item
= caret
;
2408 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2410 if (descr
->style
& LBS_MULTIPLESEL
)
2411 descr
->selected_item
= caret
;
2413 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2414 if (descr
->style
& LBS_NOTIFY
)
2416 if (descr
->lphc
&& IsWindowVisible( descr
->self
))
2418 /* make sure that combo parent doesn't hide us */
2419 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2421 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2428 /***********************************************************************
2429 * LISTBOX_HandleChar
2431 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2439 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2441 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2442 MAKEWPARAM(charW
, descr
->focus_item
),
2443 (LPARAM
)descr
->self
);
2444 if (caret
== -2) return 0;
2447 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2450 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2451 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2452 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2453 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2454 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2459 /* ReactOS Retrieve the UI state for the control */
2460 static BOOL
LISTBOX_update_uistate(LB_DESCR
*descr
)
2464 prev_flags
= descr
->UIState
;
2465 descr
->UIState
= DefWindowProcW(descr
->self
, WM_QUERYUISTATE
, 0, 0);
2466 return prev_flags
!= descr
->UIState
;
2470 /***********************************************************************
2473 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2476 MEASUREITEMSTRUCT mis
;
2479 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2482 GetClientRect( hwnd
, &rect
);
2484 descr
->owner
= GetParent( descr
->self
);
2485 descr
->style
= GetWindowLongPtrW( descr
->self
, GWL_STYLE
);
2486 descr
->width
= rect
.right
- rect
.left
;
2487 descr
->height
= rect
.bottom
- rect
.top
;
2488 descr
->items
= NULL
;
2489 descr
->nb_items
= 0;
2490 descr
->top_item
= 0;
2491 descr
->selected_item
= -1;
2492 descr
->focus_item
= 0;
2493 descr
->anchor_item
= -1;
2494 descr
->item_height
= 1;
2495 descr
->page_size
= 1;
2496 descr
->column_width
= 150;
2497 descr
->horz_extent
= 0;
2498 descr
->horz_pos
= 0;
2501 descr
->wheel_remain
= 0;
2502 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2503 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2504 descr
->in_focus
= FALSE
;
2505 descr
->captured
= FALSE
;
2507 descr
->locale
= GetUserDefaultLCID();
2512 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2513 descr
->owner
= lphc
->self
;
2516 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2518 LISTBOX_update_uistate(descr
); // ReactOS
2520 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2522 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2523 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2524 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2527 /* A no-data list box must also have the LBS_OWNERDRAWFIXED style, but must
2528 not have the LBS_SORT or LBS_HASSTRINGS style. */
2529 if ( descr
->style
& LBS_NODATA
&&
2530 (!(descr
->style
& LBS_OWNERDRAWFIXED
) || descr
->style
& (LBS_HASSTRINGS
|LBS_SORT
) ) )
2531 descr
->style
&= ~LBS_NODATA
;
2533 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2535 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2537 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2539 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2540 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2544 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2545 mis
.CtlType
= ODT_LISTBOX
;
2550 mis
.itemHeight
= descr
->item_height
;
2551 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2552 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2556 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2561 /***********************************************************************
2564 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2566 LISTBOX_ResetContent( descr
);
2567 SetWindowLongPtrW( descr
->self
, 0, 0 );
2568 HeapFree( GetProcessHeap(), 0, descr
);
2573 /***********************************************************************
2574 * ListBoxWndProc_common
2576 LRESULT WINAPI
ListBoxWndProc_common( HWND hwnd
, UINT msg
,
2577 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2579 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2580 LPHEADCOMBO lphc
= 0;
2585 pWnd
= ValidateHwnd(hwnd
);
2590 NtUserSetWindowFNID(hwnd
, FNID_LISTBOX
); // Could be FNID_COMBOLBOX by class.
2594 if (pWnd
->fnid
!= FNID_LISTBOX
)
2596 ERR("Wrong window class for listbox! fnId 0x%x\n",pWnd
->fnid
);
2605 if (!IsWindow(hwnd
)) return 0;
2607 if (msg
== WM_CREATE
)
2609 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2610 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= lpcs
->lpCreateParams
;
2611 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2612 TRACE("creating hwnd %p descr %p\n", hwnd
, (void *)GetWindowLongPtrW( hwnd
, 0 ) );
2615 /* Ignore all other messages before we get a WM_CREATE */
2616 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2617 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2619 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2621 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2622 descr
->self
, SPY_GetMsgName(msg
, descr
->self
), wParam
, lParam
);
2626 case LB_RESETCONTENT
:
2627 LISTBOX_ResetContent( descr
);
2628 LISTBOX_UpdateScroll( descr
);
2629 InvalidateRect( descr
->self
, NULL
, TRUE
);
2633 case LB_ADDSTRING_LOWER
:
2634 case LB_ADDSTRING_UPPER
:
2638 if(unicode
|| !HAS_STRINGS(descr
))
2639 textW
= (LPWSTR
)lParam
;
2642 LPSTR textA
= (LPSTR
)lParam
;
2643 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2644 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2645 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2649 /* in the unicode the version, the string is really overwritten
2650 during the converting case */
2651 if (msg
== LB_ADDSTRING_LOWER
)
2653 else if (msg
== LB_ADDSTRING_UPPER
)
2655 wParam
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2656 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2657 if (!unicode
&& HAS_STRINGS(descr
))
2658 HeapFree(GetProcessHeap(), 0, textW
);
2662 case LB_INSERTSTRING
:
2663 case LB_INSERTSTRING_UPPER
:
2664 case LB_INSERTSTRING_LOWER
:
2668 if(unicode
|| !HAS_STRINGS(descr
))
2669 textW
= (LPWSTR
)lParam
;
2672 LPSTR textA
= (LPSTR
)lParam
;
2673 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2674 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2675 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2679 /* in the unicode the version, the string is really overwritten
2680 during the converting case */
2681 if (msg
== LB_INSERTSTRING_LOWER
)
2683 else if (msg
== LB_INSERTSTRING_UPPER
)
2685 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2686 if(!unicode
&& HAS_STRINGS(descr
))
2687 HeapFree(GetProcessHeap(), 0, textW
);
2695 if(unicode
|| !HAS_STRINGS(descr
))
2696 textW
= (LPWSTR
)lParam
;
2699 LPSTR textA
= (LPSTR
)lParam
;
2700 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2701 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2702 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2706 wParam
= LISTBOX_FindFileStrPos( descr
, textW
);
2707 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2708 if(!unicode
&& HAS_STRINGS(descr
))
2709 HeapFree(GetProcessHeap(), 0, textW
);
2713 case LB_DELETESTRING
:
2714 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2715 return descr
->nb_items
;
2718 SetLastError(ERROR_INVALID_INDEX
);
2722 case LB_GETITEMDATA
:
2723 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2725 SetLastError(ERROR_INVALID_INDEX
);
2728 return descr
->items
[wParam
].data
;
2730 case LB_SETITEMDATA
:
2731 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2733 SetLastError(ERROR_INVALID_INDEX
);
2736 descr
->items
[wParam
].data
= lParam
;
2737 /* undocumented: returns TRUE, not LB_OKAY (0) */
2741 return descr
->nb_items
;
2744 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, unicode
);
2747 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2749 SetLastError(ERROR_INVALID_INDEX
);
2752 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2753 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2754 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2755 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2758 if (descr
->nb_items
== 0)
2760 if (!IS_MULTISELECT(descr
))
2761 return descr
->selected_item
;
2762 if (descr
->selected_item
!= -1)
2763 return descr
->selected_item
;
2764 return descr
->focus_item
;
2765 /* otherwise, if the user tries to move the selection with the */
2766 /* arrow keys, we will give the application something to choke on */
2767 case LB_GETTOPINDEX
:
2768 return descr
->top_item
;
2770 case LB_GETITEMHEIGHT
:
2771 return LISTBOX_GetItemHeight( descr
, wParam
);
2773 case LB_SETITEMHEIGHT
:
2774 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2776 case LB_ITEMFROMPOINT
:
2783 /* The hiword of the return value is not a client area
2784 hittest as suggested by MSDN, but rather a hittest on
2785 the returned listbox item. */
2787 if(descr
->nb_items
== 0)
2788 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2790 pt
.x
= (short)LOWORD(lParam
);
2791 pt
.y
= (short)HIWORD(lParam
);
2793 SetRect(&rect
, 0, 0, descr
->width
, descr
->height
);
2795 if(!PtInRect(&rect
, pt
))
2797 pt
.x
= min(pt
.x
, rect
.right
- 1);
2798 pt
.x
= max(pt
.x
, 0);
2799 pt
.y
= min(pt
.y
, rect
.bottom
- 1);
2800 pt
.y
= max(pt
.y
, 0);
2804 index
= LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
);
2808 index
= descr
->nb_items
- 1;
2811 return MAKELONG(index
, hit
? 0 : 1);
2814 case LB_SETCARETINDEX
:
2815 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2816 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2823 case LB_GETCARETINDEX
:
2824 return descr
->focus_item
;
2826 case LB_SETTOPINDEX
:
2827 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2829 case LB_SETCOLUMNWIDTH
:
2830 return LISTBOX_SetColumnWidth( descr
, wParam
);
2832 case LB_GETITEMRECT
:
2833 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2839 if(unicode
|| !HAS_STRINGS(descr
))
2840 textW
= (LPWSTR
)lParam
;
2843 LPSTR textA
= (LPSTR
)lParam
;
2844 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2845 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2846 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2848 ret
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2849 if(!unicode
&& HAS_STRINGS(descr
))
2850 HeapFree(GetProcessHeap(), 0, textW
);
2854 case LB_FINDSTRINGEXACT
:
2858 if(unicode
|| !HAS_STRINGS(descr
))
2859 textW
= (LPWSTR
)lParam
;
2862 LPSTR textA
= (LPSTR
)lParam
;
2863 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2864 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2865 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2867 ret
= LISTBOX_FindString( descr
, wParam
, textW
, TRUE
);
2868 if(!unicode
&& HAS_STRINGS(descr
))
2869 HeapFree(GetProcessHeap(), 0, textW
);
2873 case LB_SELECTSTRING
:
2878 if(HAS_STRINGS(descr
))
2879 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2880 debugstr_a((LPSTR
)lParam
));
2881 if(unicode
|| !HAS_STRINGS(descr
))
2882 textW
= (LPWSTR
)lParam
;
2885 LPSTR textA
= (LPSTR
)lParam
;
2886 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2887 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2888 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2890 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2891 if(!unicode
&& HAS_STRINGS(descr
))
2892 HeapFree(GetProcessHeap(), 0, textW
);
2893 if (index
!= LB_ERR
)
2895 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2896 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2902 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2904 return descr
->items
[wParam
].selected
;
2907 return LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2910 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2911 LISTBOX_SetCaretIndex( descr
, wParam
, FALSE
);
2912 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2913 if (ret
!= LB_ERR
) ret
= descr
->selected_item
;
2916 case LB_GETSELCOUNT
:
2917 return LISTBOX_GetSelCount( descr
);
2919 case LB_GETSELITEMS
:
2920 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2922 case LB_SELITEMRANGE
:
2923 if (LOWORD(lParam
) <= HIWORD(lParam
))
2924 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2925 HIWORD(lParam
), wParam
);
2927 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2928 LOWORD(lParam
), wParam
);
2930 case LB_SELITEMRANGEEX
:
2931 if ((INT
)lParam
>= (INT
)wParam
)
2932 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2934 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
2936 case LB_GETHORIZONTALEXTENT
:
2937 return descr
->horz_extent
;
2939 case LB_SETHORIZONTALEXTENT
:
2940 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
2942 case LB_GETANCHORINDEX
:
2943 return descr
->anchor_item
;
2945 case LB_SETANCHORINDEX
:
2946 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2948 SetLastError(ERROR_INVALID_INDEX
);
2951 descr
->anchor_item
= (INT
)wParam
;
2959 textW
= (LPWSTR
)lParam
;
2962 LPSTR textA
= (LPSTR
)lParam
;
2963 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2964 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2965 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2967 ret
= LISTBOX_Directory( descr
, wParam
, textW
, msg
== LB_DIR
);
2969 HeapFree(GetProcessHeap(), 0, textW
);
2974 return descr
->locale
;
2979 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
2981 ret
= descr
->locale
;
2982 descr
->locale
= (LCID
)wParam
;
2986 case LB_INITSTORAGE
:
2987 return LISTBOX_InitStorage( descr
, wParam
);
2990 return LISTBOX_SetCount( descr
, (INT
)wParam
);
2992 case LB_SETTABSTOPS
:
2993 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
);
2996 if (descr
->caret_on
)
2998 descr
->caret_on
= TRUE
;
2999 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3000 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3004 if (!descr
->caret_on
)
3006 descr
->caret_on
= FALSE
;
3007 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3008 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3011 case LB_GETLISTBOXINFO
:
3012 if (descr
->style
& LBS_MULTICOLUMN
) //// ReactOS
3013 return descr
->page_size
* descr
->column_width
;
3015 return descr
->page_size
;
3018 return LISTBOX_Destroy( descr
);
3021 InvalidateRect( descr
->self
, NULL
, TRUE
);
3025 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
3029 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
3031 case WM_PRINTCLIENT
:
3035 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
3036 ret
= LISTBOX_Paint( descr
, hdc
);
3037 if( !wParam
) EndPaint( descr
->self
, &ps
);
3041 LISTBOX_UpdateSize( descr
);
3044 return (LRESULT
)descr
->font
;
3046 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
3047 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
3050 descr
->in_focus
= TRUE
;
3051 descr
->caret_on
= TRUE
;
3052 if (descr
->focus_item
!= -1)
3053 LISTBOX_DrawFocusRect( descr
, TRUE
);
3054 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
3057 LISTBOX_HandleLButtonUp( descr
); /* Release capture if we have it */
3058 descr
->in_focus
= FALSE
;
3059 descr
->wheel_remain
= 0;
3060 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
3061 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3062 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
3065 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3067 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3069 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
3070 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
3071 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
3072 case WM_LBUTTONDOWN
:
3074 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3075 (INT16
)LOWORD(lParam
),
3076 (INT16
)HIWORD(lParam
) );
3077 return LISTBOX_HandleLButtonDown( descr
, wParam
,
3078 (INT16
)LOWORD(lParam
),
3079 (INT16
)HIWORD(lParam
) );
3080 case WM_LBUTTONDBLCLK
:
3082 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3083 (INT16
)LOWORD(lParam
),
3084 (INT16
)HIWORD(lParam
) );
3085 if (descr
->style
& LBS_NOTIFY
)
3086 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
3089 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
3091 BOOL captured
= descr
->captured
;
3095 mousePos
.x
= (INT16
)LOWORD(lParam
);
3096 mousePos
.y
= (INT16
)HIWORD(lParam
);
3099 * If we are in a dropdown combobox, we simulate that
3100 * the mouse is captured to show the tracking of the item.
3102 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
3103 descr
->captured
= TRUE
;
3105 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
3107 descr
->captured
= captured
;
3109 else if (GetCapture() == descr
->self
)
3111 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
3112 (INT16
)HIWORD(lParam
) );
3122 * If the mouse button "up" is not in the listbox,
3123 * we make sure there is no selection by re-selecting the
3124 * item that was selected when the listbox was made visible.
3126 mousePos
.x
= (INT16
)LOWORD(lParam
);
3127 mousePos
.y
= (INT16
)HIWORD(lParam
);
3129 GetClientRect(descr
->self
, &clientRect
);
3132 * When the user clicks outside the combobox and the focus
3133 * is lost, the owning combobox will send a fake buttonup with
3134 * 0xFFFFFFF as the mouse location, we must also revert the
3135 * selection to the original selection.
3137 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
3138 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
3140 return LISTBOX_HandleLButtonUp( descr
);
3142 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3144 /* for some reason Windows makes it possible to
3145 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3147 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3148 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3149 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3151 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3155 return LISTBOX_HandleKeyDown( descr
, wParam
);
3160 charW
= (WCHAR
)wParam
;
3163 CHAR charA
= (CHAR
)wParam
;
3164 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
3166 return LISTBOX_HandleChar( descr
, charW
);
3169 return LISTBOX_HandleSystemTimer( descr
);
3171 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3174 HBRUSH hbrush
= GetControlColor( descr
->owner
, descr
->self
, (HDC
)wParam
, WM_CTLCOLORLISTBOX
);
3175 TRACE("hbrush = %p\n", hbrush
);
3177 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3180 GetClientRect(descr
->self
, &rect
);
3181 FillRect((HDC
)wParam
, &rect
, hbrush
);
3186 if( lphc
) return 0;
3187 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3188 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3191 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3194 NtUserSetWindowFNID(hwnd
, FNID_DESTROY
);
3202 case WM_UPDATEUISTATE
:
3204 DefWindowProcW(descr
->self
, msg
, wParam
, lParam
);
3206 DefWindowProcA(descr
->self
, msg
, wParam
, lParam
);
3208 if (LISTBOX_update_uistate(descr
))
3211 if (descr
->focus_item
!= -1)
3212 LISTBOX_DrawFocusRect( descr
, descr
->in_focus
);
3217 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3218 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3219 hwnd
, msg
, wParam
, lParam
);
3222 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3223 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3226 /***********************************************************************
3229 LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3231 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3234 /***********************************************************************
3237 LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3239 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);