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 * - GetListBoxInfo() ReactOS
31 * - LB_GETLISTBOXINFO ReactOS
32 * - LBS_NODATA ReactOS
37 #include <wine/debug.h>
39 WINE_DEFAULT_DEBUG_CHANNEL(listbox
);
41 /* Items array granularity */
42 #define LB_ARRAY_GRANULARITY 16
44 /* Scrolling timeout in ms */
45 #define LB_SCROLL_TIMEOUT 50
47 /* Listbox system timer id */
50 /* flag listbox changed while setredraw false - internal style */
51 #define LBS_DISPLAYCHANGED 0x80000000
56 LPWSTR str
; /* Item text */
57 BOOL selected
; /* Is item selected? */
58 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
59 ULONG_PTR data
; /* User data */
62 /* Listbox structure */
65 HWND self
; /* Our own window handle */
66 HWND owner
; /* Owner window to send notifications to */
67 UINT style
; /* Window style */
68 INT width
; /* Window width */
69 INT height
; /* Window height */
70 LB_ITEMDATA
*items
; /* Array of items */
71 INT nb_items
; /* Number of items */
72 INT top_item
; /* Top visible item */
73 INT selected_item
; /* Selected item */
74 INT focus_item
; /* Item that has the focus */
75 INT anchor_item
; /* Anchor item for extended selection */
76 INT item_height
; /* Default item height */
77 INT page_size
; /* Items per listbox page */
78 INT column_width
; /* Column width for multi-column listboxes */
79 INT horz_extent
; /* Horizontal extent (0 if no hscroll) */
80 INT horz_pos
; /* Horizontal position */
81 INT nb_tabs
; /* Number of tabs in array */
82 INT
*tabs
; /* Array of tabs */
83 INT avg_char_width
; /* Average width of characters */
84 BOOL caret_on
; /* Is caret on? */
85 BOOL captured
; /* Is mouse captured? */
87 HFONT font
; /* Current font */
88 LCID locale
; /* Current locale for string comparisons */
89 LPHEADCOMBO lphc
; /* ComboLBox */
90 LONG UIState
; // REACTOS
94 #define IS_OWNERDRAW(descr) \
95 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
97 #define HAS_STRINGS(descr) \
98 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
101 #define IS_MULTISELECT(descr) \
102 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
103 !((descr)->style & LBS_NOSEL))
105 #define SEND_NOTIFICATION(descr,code) \
106 (SendMessageW( (descr)->owner, WM_COMMAND, \
107 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
109 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
111 /* Current timer status */
121 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
123 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
);
125 /*********************************************************************
126 * listbox class descriptor
128 static const WCHAR listboxW
[] = {'L','i','s','t','B','o','x',0};
129 const struct builtin_class_descr LISTBOX_builtin_class
=
132 CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
133 ListBoxWndProcA
, /* procA */
134 ListBoxWndProcW
, /* procW */
135 sizeof(LB_DESCR
*), /* extra */
136 IDC_ARROW
, /* cursor */
141 /*********************************************************************
142 * combolbox class descriptor
144 static const WCHAR combolboxW
[] = {'C','o','m','b','o','L','B','o','x',0};
145 const struct builtin_class_descr COMBOLBOX_builtin_class
=
147 combolboxW
, /* name */
148 CS_DBLCLKS
| CS_SAVEBITS
, /* style */
149 ListBoxWndProcA
, /* procA */
150 ListBoxWndProcW
, /* procW */
151 sizeof(LB_DESCR
*), /* extra */
152 IDC_ARROW
, /* cursor */
157 /***********************************************************************
158 * LISTBOX_GetCurrentPageSize
160 * Return the current page size
162 static INT
LISTBOX_GetCurrentPageSize( const LB_DESCR
*descr
)
165 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
166 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
168 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
170 if (i
== descr
->top_item
) return 1;
171 else return i
- descr
->top_item
;
175 /***********************************************************************
176 * LISTBOX_GetMaxTopIndex
178 * Return the maximum possible index for the top of the listbox.
180 static INT
LISTBOX_GetMaxTopIndex( const LB_DESCR
*descr
)
184 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
186 page
= descr
->height
;
187 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
188 if ((page
-= descr
->items
[max
].height
) < 0) break;
189 if (max
< descr
->nb_items
- 1) max
++;
191 else if (descr
->style
& LBS_MULTICOLUMN
)
193 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
194 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
195 max
= (max
- page
) * descr
->page_size
;
199 max
= descr
->nb_items
- descr
->page_size
;
201 if (max
< 0) max
= 0;
206 /***********************************************************************
207 * LISTBOX_UpdateScroll
209 * Update the scrollbars. Should be called whenever the content
210 * of the listbox changes.
212 static void LISTBOX_UpdateScroll( LB_DESCR
*descr
)
216 /* Check the listbox scroll bar flags individually before we call
217 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
218 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
219 scroll bar when we do not need one.
220 if (!(descr->style & WS_VSCROLL)) return;
223 /* It is important that we check descr->style, and not wnd->dwStyle,
224 for WS_VSCROLL, as the former is exactly the one passed in
225 argument to CreateWindow.
226 In Windows (and from now on in Wine :) a listbox created
227 with such a style (no WS_SCROLL) does not update
228 the scrollbar with listbox-related data, thus letting
229 the programmer use it for his/her own purposes. */
231 if (descr
->style
& LBS_NOREDRAW
) return;
232 info
.cbSize
= sizeof(info
);
234 if (descr
->style
& LBS_MULTICOLUMN
)
237 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
238 info
.nPos
= descr
->top_item
/ descr
->page_size
;
239 info
.nPage
= descr
->width
/ descr
->column_width
;
240 if (info
.nPage
< 1) info
.nPage
= 1;
241 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
242 if (descr
->style
& LBS_DISABLENOSCROLL
)
243 info
.fMask
|= SIF_DISABLENOSCROLL
;
244 if (descr
->style
& WS_HSCROLL
)
245 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
247 info
.fMask
= SIF_RANGE
;
248 if (descr
->style
& WS_VSCROLL
)
249 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
254 info
.nMax
= descr
->nb_items
- 1;
255 info
.nPos
= descr
->top_item
;
256 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
257 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
258 if (descr
->style
& LBS_DISABLENOSCROLL
)
259 info
.fMask
|= SIF_DISABLENOSCROLL
;
260 if (descr
->style
& WS_VSCROLL
)
261 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
263 if (descr
->horz_extent
)
266 info
.nMax
= descr
->horz_extent
- 1;
267 info
.nPos
= descr
->horz_pos
;
268 info
.nPage
= descr
->width
;
269 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
270 if (descr
->style
& LBS_DISABLENOSCROLL
)
271 info
.fMask
|= SIF_DISABLENOSCROLL
;
272 if (descr
->style
& WS_HSCROLL
)
273 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
279 /***********************************************************************
282 * Set the top item of the listbox, scrolling up or down if necessary.
284 static LRESULT
LISTBOX_SetTopItem( LB_DESCR
*descr
, INT index
, BOOL scroll
)
286 INT max
= LISTBOX_GetMaxTopIndex( descr
);
288 TRACE("setting top item %d, scroll %d\n", index
, scroll
);
290 if (index
> max
) index
= max
;
291 if (index
< 0) index
= 0;
292 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
293 if (descr
->top_item
== index
) return LB_OKAY
;
294 if (descr
->style
& LBS_MULTICOLUMN
)
296 INT diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
297 if (scroll
&& (abs(diff
) < descr
->width
))
298 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
299 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
307 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 if (abs(diff
) < descr
->height
)
326 ScrollWindowEx( descr
->self
, 0, diff
, NULL
, NULL
, 0, NULL
,
327 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
331 if (!scroll
) InvalidateRect( descr
->self
, NULL
, TRUE
);
332 descr
->top_item
= index
;
333 LISTBOX_UpdateScroll( descr
);
338 /***********************************************************************
341 * Update the page size. Should be called when the size of
342 * the client area or the item height changes.
344 static void LISTBOX_UpdatePage( LB_DESCR
*descr
)
348 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
350 if (page_size
== descr
->page_size
) return;
351 descr
->page_size
= page_size
;
352 if (descr
->style
& LBS_MULTICOLUMN
)
353 InvalidateRect( descr
->self
, NULL
, TRUE
);
354 LISTBOX_SetTopItem( descr
, descr
->top_item
, FALSE
);
358 /***********************************************************************
361 * Update the size of the listbox. Should be called when the size of
362 * the client area changes.
364 static void LISTBOX_UpdateSize( LB_DESCR
*descr
)
368 GetClientRect( descr
->self
, &rect
);
369 descr
->width
= rect
.right
- rect
.left
;
370 descr
->height
= rect
.bottom
- rect
.top
;
371 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
376 GetWindowRect( descr
->self
, &rect
);
377 if(descr
->item_height
!= 0)
378 remaining
= descr
->height
% descr
->item_height
;
381 if ((descr
->height
> descr
->item_height
) && remaining
)
383 TRACE("[%p]: changing height %d -> %d\n",
384 descr
->self
, descr
->height
, descr
->height
- remaining
);
385 SetWindowPos( descr
->self
, 0, 0, 0, rect
.right
- rect
.left
,
386 rect
.bottom
- rect
.top
- remaining
,
387 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
391 TRACE("[%p]: new size = %d,%d\n", descr
->self
, descr
->width
, descr
->height
);
392 LISTBOX_UpdatePage( descr
);
393 LISTBOX_UpdateScroll( descr
);
395 /* Invalidate the focused item so it will be repainted correctly */
396 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
398 InvalidateRect( descr
->self
, &rect
, FALSE
);
403 /***********************************************************************
404 * LISTBOX_GetItemRect
406 * Get the rectangle enclosing an item, in listbox client coordinates.
407 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
409 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
)
411 /* Index <= 0 is legal even on empty listboxes */
412 if (index
&& (index
>= descr
->nb_items
))
414 memset(rect
, 0, sizeof(*rect
));
415 SetLastError(ERROR_INVALID_INDEX
);
418 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
419 if (descr
->style
& LBS_MULTICOLUMN
)
421 INT col
= (index
/ descr
->page_size
) -
422 (descr
->top_item
/ descr
->page_size
);
423 rect
->left
+= col
* descr
->column_width
;
424 rect
->right
= rect
->left
+ descr
->column_width
;
425 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
426 rect
->bottom
= rect
->top
+ descr
->item_height
;
428 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
431 rect
->right
+= descr
->horz_pos
;
432 if ((index
>= 0) && (index
< descr
->nb_items
))
434 if (index
< descr
->top_item
)
436 for (i
= descr
->top_item
-1; i
>= index
; i
--)
437 rect
->top
-= descr
->items
[i
].height
;
441 for (i
= descr
->top_item
; i
< index
; i
++)
442 rect
->top
+= descr
->items
[i
].height
;
444 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
450 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
451 rect
->bottom
= rect
->top
+ descr
->item_height
;
452 rect
->right
+= descr
->horz_pos
;
455 TRACE("item %d, rect %s\n", index
, wine_dbgstr_rect(rect
));
457 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
458 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
462 /***********************************************************************
463 * LISTBOX_GetItemFromPoint
465 * Return the item nearest from point (x,y) (in client coordinates).
467 static INT
LISTBOX_GetItemFromPoint( const LB_DESCR
*descr
, INT x
, INT y
)
469 INT index
= descr
->top_item
;
471 if (!descr
->nb_items
) return -1; /* No items */
472 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
477 while (index
< descr
->nb_items
)
479 if ((pos
+= descr
->items
[index
].height
) > y
) break;
488 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
492 else if (descr
->style
& LBS_MULTICOLUMN
)
494 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
495 if (y
>= 0) index
+= y
/ descr
->item_height
;
496 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
497 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
501 index
+= (y
/ descr
->item_height
);
503 if (index
< 0) return 0;
504 if (index
>= descr
->nb_items
) return -1;
509 /***********************************************************************
514 static void LISTBOX_PaintItem( LB_DESCR
*descr
, HDC hdc
, const RECT
*rect
,
515 INT index
, UINT action
, BOOL ignoreFocus
)
517 LB_ITEMDATA
*item
= NULL
;
518 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
520 if (IS_OWNERDRAW(descr
))
528 if (action
== ODA_FOCUS
)
530 if (!(descr
->UIState
& UISF_HIDEFOCUS
))
531 DrawFocusRect( hdc
, rect
);
534 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
538 /* some programs mess with the clipping region when
539 drawing the item, *and* restore the previous region
540 after they are done, so a region has better to exist
541 else everything ends clipped */
542 GetClientRect(descr
->self
, &r
);
543 hrgn
= set_control_clipping( hdc
, &r
);
545 dis
.CtlType
= ODT_LISTBOX
;
546 dis
.CtlID
= GetWindowLongPtrW( descr
->self
, GWLP_ID
);
547 dis
.hwndItem
= descr
->self
;
548 dis
.itemAction
= action
;
552 if (item
->selected
) dis
.itemState
|= ODS_SELECTED
;
553 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
555 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
556 if (!IsWindowEnabled(descr
->self
)) dis
.itemState
|= ODS_DISABLED
;
557 dis
.itemData
= item
->data
;
559 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
560 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
561 dis
.itemState
, wine_dbgstr_rect(rect
) );
562 SendMessageW(descr
->owner
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
563 SelectClipRgn( hdc
, hrgn
);
564 if (hrgn
) DeleteObject( hrgn
);
568 COLORREF oldText
= 0, oldBk
= 0;
570 if (action
== ODA_FOCUS
)
572 if (!(descr
->UIState
& UISF_HIDEFOCUS
)) // REACTOS
573 DrawFocusRect( hdc
, rect
);
576 if (item
&& item
->selected
)
578 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
579 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
582 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
583 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
584 wine_dbgstr_rect(rect
) );
586 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
587 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
588 else if (!(descr
->style
& LBS_USETABSTOPS
))
589 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
590 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
591 strlenW(item
->str
), NULL
);
594 /* Output empty string to paint background in the full width. */
595 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
596 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
597 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
598 item
->str
, strlenW(item
->str
),
599 descr
->nb_tabs
, descr
->tabs
, 0);
601 if (item
&& item
->selected
)
603 SetBkColor( hdc
, oldBk
);
604 SetTextColor( hdc
, oldText
);
606 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
609 !(descr
->UIState
& UISF_HIDEFOCUS
)) DrawFocusRect( hdc
, rect
);
614 /***********************************************************************
617 * Change the redraw flag.
619 static void LISTBOX_SetRedraw( LB_DESCR
*descr
, BOOL on
)
623 if (!(descr
->style
& LBS_NOREDRAW
)) return;
624 descr
->style
&= ~LBS_NOREDRAW
;
625 if (descr
->style
& LBS_DISPLAYCHANGED
)
626 { /* page was changed while setredraw false, refresh automatically */
627 InvalidateRect(descr
->self
, NULL
, TRUE
);
628 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
629 { /* reset top of page if less than number of items/page */
630 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
631 if (descr
->top_item
< 0) descr
->top_item
= 0;
633 descr
->style
&= ~LBS_DISPLAYCHANGED
;
635 LISTBOX_UpdateScroll( descr
);
637 else descr
->style
|= LBS_NOREDRAW
;
641 /***********************************************************************
642 * LISTBOX_RepaintItem
644 * Repaint a single item synchronously.
646 static void LISTBOX_RepaintItem( LB_DESCR
*descr
, INT index
, UINT action
)
651 HBRUSH hbrush
, oldBrush
= 0;
653 /* Do not repaint the item if the item is not visible */
654 if (!IsWindowVisible(descr
->self
)) return;
655 if (descr
->style
& LBS_NOREDRAW
)
657 descr
->style
|= LBS_DISPLAYCHANGED
;
660 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
661 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
662 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
663 hbrush
= GetControlColor( descr
->owner
, descr
->self
, hdc
, WM_CTLCOLORLISTBOX
);
664 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
665 if (!IsWindowEnabled(descr
->self
))
666 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
667 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
668 LISTBOX_PaintItem( descr
, hdc
, &rect
, index
, action
, TRUE
);
669 if (oldFont
) SelectObject( hdc
, oldFont
);
670 if (oldBrush
) SelectObject( hdc
, oldBrush
);
671 ReleaseDC( descr
->self
, hdc
);
675 /***********************************************************************
676 * LISTBOX_DrawFocusRect
678 static void LISTBOX_DrawFocusRect( LB_DESCR
*descr
, BOOL on
)
684 /* Do not repaint the item if the item is not visible */
685 if (!IsWindowVisible(descr
->self
)) return;
687 if (descr
->focus_item
== -1) return;
688 if (!descr
->caret_on
|| !descr
->in_focus
) return;
690 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) != 1) return;
691 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
692 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
693 if (!IsWindowEnabled(descr
->self
))
694 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
695 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
696 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, on
? FALSE
: TRUE
);
697 if (oldFont
) SelectObject( hdc
, oldFont
);
698 ReleaseDC( descr
->self
, hdc
);
702 /***********************************************************************
703 * LISTBOX_InitStorage
705 static LRESULT
LISTBOX_InitStorage( LB_DESCR
*descr
, INT nb_items
)
709 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
710 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
712 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
713 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
714 nb_items
* sizeof(LB_ITEMDATA
));
717 item
= HeapAlloc( GetProcessHeap(), 0,
718 nb_items
* sizeof(LB_ITEMDATA
));
723 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
731 /***********************************************************************
732 * LISTBOX_SetTabStops
734 static BOOL
LISTBOX_SetTabStops( LB_DESCR
*descr
, INT count
, LPINT tabs
)
738 if (!(descr
->style
& LBS_USETABSTOPS
))
740 SetLastError(ERROR_LB_WITHOUT_TABSTOPS
);
744 HeapFree( GetProcessHeap(), 0, descr
->tabs
);
745 if (!(descr
->nb_tabs
= count
))
750 if (!(descr
->tabs
= HeapAlloc( GetProcessHeap(), 0,
751 descr
->nb_tabs
* sizeof(INT
) )))
753 memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
755 /* convert into "dialog units"*/
756 for (i
= 0; i
< descr
->nb_tabs
; i
++)
757 descr
->tabs
[i
] = MulDiv(descr
->tabs
[i
], descr
->avg_char_width
, 4);
763 /***********************************************************************
766 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPWSTR buffer
, BOOL unicode
)
770 if ((index
< 0) || (index
>= descr
->nb_items
))
772 SetLastError(ERROR_INVALID_INDEX
);
775 if (HAS_STRINGS(descr
))
779 len
= strlenW(descr
->items
[index
].str
);
782 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[index
].str
, len
,
783 NULL
, 0, NULL
, NULL
);
786 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
788 _SEH2_TRY
/* hide a Delphi bug that passes a read-only buffer */
792 strcpyW( buffer
, descr
->items
[index
].str
);
793 len
= strlenW(buffer
);
797 len
= WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1,
798 (LPSTR
)buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
801 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
803 WARN( "got an invalid buffer (Delphi bug?)\n" );
804 SetLastError( ERROR_INVALID_PARAMETER
);
810 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
816 static inline INT
LISTBOX_lstrcmpiW( LCID lcid
, LPCWSTR str1
, LPCWSTR str2
)
818 INT ret
= CompareStringW( lcid
, NORM_IGNORECASE
, str1
, -1, str2
, -1 );
819 if (ret
== CSTR_LESS_THAN
)
821 if (ret
== CSTR_EQUAL
)
823 if (ret
== CSTR_GREATER_THAN
)
828 /***********************************************************************
829 * LISTBOX_FindStringPos
831 * Find the nearest string located before a given string in sort order.
832 * If 'exact' is TRUE, return an error if we don't get an exact match.
834 static INT
LISTBOX_FindStringPos( LB_DESCR
*descr
, LPCWSTR str
, BOOL exact
)
836 INT index
, min
, max
, res
= -1;
838 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
840 max
= descr
->nb_items
;
843 index
= (min
+ max
) / 2;
844 if (HAS_STRINGS(descr
))
845 res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, descr
->items
[index
].str
);
848 COMPAREITEMSTRUCT cis
;
849 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
851 cis
.CtlType
= ODT_LISTBOX
;
853 cis
.hwndItem
= descr
->self
;
854 /* note that some application (MetaStock) expects the second item
855 * to be in the listbox */
857 cis
.itemData1
= (ULONG_PTR
)str
;
859 cis
.itemData2
= descr
->items
[index
].data
;
860 cis
.dwLocaleId
= descr
->locale
;
861 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
863 if (!res
) return index
;
864 if (res
< 0) max
= index
;
865 else min
= index
+ 1;
867 return exact
? -1 : max
;
871 /***********************************************************************
872 * LISTBOX_FindFileStrPos
874 * Find the nearest string located before a given string in directory
875 * sort order (i.e. first files, then directories, then drives).
877 static INT
LISTBOX_FindFileStrPos( LB_DESCR
*descr
, LPCWSTR str
)
879 INT min
, max
, res
= -1;
881 if (!HAS_STRINGS(descr
))
882 return LISTBOX_FindStringPos( descr
, str
, FALSE
);
884 max
= descr
->nb_items
;
887 INT index
= (min
+ max
) / 2;
888 LPCWSTR p
= descr
->items
[index
].str
;
889 if (*p
== '[') /* drive or directory */
891 if (*str
!= '[') res
= -1;
892 else if (p
[1] == '-') /* drive */
894 if (str
[1] == '-') res
= str
[2] - p
[2];
899 if (str
[1] == '-') res
= 1;
900 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
905 if (*str
== '[') res
= 1;
906 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
908 if (!res
) return index
;
909 if (res
< 0) max
= index
;
910 else min
= index
+ 1;
916 /***********************************************************************
919 * Find the item beginning with a given string.
921 static INT
LISTBOX_FindString( LB_DESCR
*descr
, INT start
, LPCWSTR str
, BOOL exact
)
926 if (start
>= descr
->nb_items
) start
= -1;
927 item
= descr
->items
+ start
+ 1;
928 if (HAS_STRINGS(descr
))
930 if (!str
|| ! str
[0] ) return LB_ERR
;
933 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
934 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
935 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
936 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
940 /* Special case for drives and directories: ignore prefix */
941 #define CHECK_DRIVE(item) \
942 if ((item)->str[0] == '[') \
944 if (!strncmpiW( str, (item)->str+1, len )) return i; \
945 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
949 INT len
= strlenW(str
);
950 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
952 if (!strncmpiW( str
, item
->str
, len
)) return i
;
955 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
957 if (!strncmpiW( str
, item
->str
, len
)) return i
;
965 if (exact
&& (descr
->style
& LBS_SORT
))
966 /* If sorted, use a WM_COMPAREITEM binary search */
967 return LISTBOX_FindStringPos( descr
, str
, TRUE
);
969 /* Otherwise use a linear search */
970 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
971 if (item
->data
== (ULONG_PTR
)str
) return i
;
972 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
973 if (item
->data
== (ULONG_PTR
)str
) return i
;
979 /***********************************************************************
980 * LISTBOX_GetSelCount
982 static LRESULT
LISTBOX_GetSelCount( const LB_DESCR
*descr
)
985 const LB_ITEMDATA
*item
= descr
->items
;
987 if (!(descr
->style
& LBS_MULTIPLESEL
) ||
988 (descr
->style
& LBS_NOSEL
))
990 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
991 if (item
->selected
) count
++;
996 /***********************************************************************
997 * LISTBOX_GetSelItems
999 static LRESULT
LISTBOX_GetSelItems( const LB_DESCR
*descr
, INT max
, LPINT array
)
1002 const LB_ITEMDATA
*item
= descr
->items
;
1004 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1005 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
1006 if (item
->selected
) array
[count
++] = i
;
1011 /***********************************************************************
1014 static LRESULT
LISTBOX_Paint( LB_DESCR
*descr
, HDC hdc
)
1016 INT i
, col_pos
= descr
->page_size
- 1;
1018 RECT focusRect
= {-1, -1, -1, -1};
1020 HBRUSH hbrush
, oldBrush
= 0;
1022 if (descr
->style
& LBS_NOREDRAW
) return 0;
1024 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1025 if (descr
->style
& LBS_MULTICOLUMN
)
1026 rect
.right
= rect
.left
+ descr
->column_width
;
1027 else if (descr
->horz_pos
)
1029 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1030 rect
.right
+= descr
->horz_pos
;
1033 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1034 hbrush
= GetControlColor( descr
->owner
, descr
->self
, hdc
, WM_CTLCOLORLISTBOX
);
1035 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1036 if (!IsWindowEnabled(descr
->self
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1038 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1041 /* Special case for empty listbox: paint focus rect */
1042 rect
.bottom
= rect
.top
+ descr
->item_height
;
1043 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1044 &rect
, NULL
, 0, NULL
);
1045 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1046 rect
.top
= rect
.bottom
;
1049 /* Paint all the item, regarding the selection
1050 Focus state will be painted after */
1052 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1054 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1055 rect
.bottom
= rect
.top
+ descr
->item_height
;
1057 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1059 if (i
== descr
->focus_item
)
1061 /* keep the focus rect, to paint the focus item after */
1062 focusRect
.left
= rect
.left
;
1063 focusRect
.right
= rect
.right
;
1064 focusRect
.top
= rect
.top
;
1065 focusRect
.bottom
= rect
.bottom
;
1067 LISTBOX_PaintItem( descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1068 rect
.top
= rect
.bottom
;
1070 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1072 if (!IS_OWNERDRAW(descr
))
1074 /* Clear the bottom of the column */
1075 if (rect
.top
< descr
->height
)
1077 rect
.bottom
= descr
->height
;
1078 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1079 &rect
, NULL
, 0, NULL
);
1083 /* Go to the next column */
1084 rect
.left
+= descr
->column_width
;
1085 rect
.right
+= descr
->column_width
;
1087 col_pos
= descr
->page_size
- 1;
1092 if (rect
.top
>= descr
->height
) break;
1096 /* Paint the focus item now */
1097 if (focusRect
.top
!= focusRect
.bottom
&&
1098 descr
->caret_on
&& descr
->in_focus
)
1099 LISTBOX_PaintItem( descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1101 if (!IS_OWNERDRAW(descr
))
1103 /* Clear the remainder of the client area */
1104 if (rect
.top
< descr
->height
)
1106 rect
.bottom
= descr
->height
;
1107 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1108 &rect
, NULL
, 0, NULL
);
1110 if (rect
.right
< descr
->width
)
1112 rect
.left
= rect
.right
;
1113 rect
.right
= descr
->width
;
1115 rect
.bottom
= descr
->height
;
1116 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1117 &rect
, NULL
, 0, NULL
);
1120 if (oldFont
) SelectObject( hdc
, oldFont
);
1121 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1126 /***********************************************************************
1127 * LISTBOX_InvalidateItems
1129 * Invalidate all items from a given item. If the specified item is not
1130 * visible, nothing happens.
1132 static void LISTBOX_InvalidateItems( LB_DESCR
*descr
, INT index
)
1136 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1138 if (descr
->style
& LBS_NOREDRAW
)
1140 descr
->style
|= LBS_DISPLAYCHANGED
;
1143 rect
.bottom
= descr
->height
;
1144 InvalidateRect( descr
->self
, &rect
, TRUE
);
1145 if (descr
->style
& LBS_MULTICOLUMN
)
1147 /* Repaint the other columns */
1148 rect
.left
= rect
.right
;
1149 rect
.right
= descr
->width
;
1151 InvalidateRect( descr
->self
, &rect
, TRUE
);
1156 static void LISTBOX_InvalidateItemRect( LB_DESCR
*descr
, INT index
)
1160 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1161 InvalidateRect( descr
->self
, &rect
, TRUE
);
1164 /***********************************************************************
1165 * LISTBOX_GetItemHeight
1167 static LRESULT
LISTBOX_GetItemHeight( const LB_DESCR
*descr
, INT index
)
1169 if (descr
->style
& LBS_OWNERDRAWVARIABLE
&& descr
->nb_items
> 0)
1171 if ((index
< 0) || (index
>= descr
->nb_items
))
1173 SetLastError(ERROR_INVALID_INDEX
);
1176 return descr
->items
[index
].height
;
1178 else return descr
->item_height
;
1182 /***********************************************************************
1183 * LISTBOX_SetItemHeight
1185 static LRESULT
LISTBOX_SetItemHeight( LB_DESCR
*descr
, INT index
, INT height
, BOOL repaint
)
1187 if (height
> MAXBYTE
)
1190 if (!height
) height
= 1;
1192 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1194 if ((index
< 0) || (index
>= descr
->nb_items
))
1196 SetLastError(ERROR_INVALID_INDEX
);
1199 TRACE("[%p]: item %d height = %d\n", descr
->self
, index
, height
);
1200 descr
->items
[index
].height
= height
;
1201 LISTBOX_UpdateScroll( descr
);
1203 LISTBOX_InvalidateItems( descr
, index
);
1205 else if (height
!= descr
->item_height
)
1207 TRACE("[%p]: new height = %d\n", descr
->self
, height
);
1208 descr
->item_height
= height
;
1209 LISTBOX_UpdatePage( descr
);
1210 LISTBOX_UpdateScroll( descr
);
1212 InvalidateRect( descr
->self
, 0, TRUE
);
1218 /***********************************************************************
1219 * LISTBOX_SetHorizontalPos
1221 static void LISTBOX_SetHorizontalPos( LB_DESCR
*descr
, INT pos
)
1225 if (pos
> descr
->horz_extent
- descr
->width
)
1226 pos
= descr
->horz_extent
- descr
->width
;
1227 if (pos
< 0) pos
= 0;
1228 if (!(diff
= descr
->horz_pos
- pos
)) return;
1229 TRACE("[%p]: new horz pos = %d\n", descr
->self
, pos
);
1230 descr
->horz_pos
= pos
;
1231 LISTBOX_UpdateScroll( descr
);
1232 if (abs(diff
) < descr
->width
)
1235 /* Invalidate the focused item so it will be repainted correctly */
1236 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1237 InvalidateRect( descr
->self
, &rect
, TRUE
);
1238 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
1239 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1242 InvalidateRect( descr
->self
, NULL
, TRUE
);
1246 /***********************************************************************
1247 * LISTBOX_SetHorizontalExtent
1249 static LRESULT
LISTBOX_SetHorizontalExtent( LB_DESCR
*descr
, INT extent
)
1251 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1253 if (extent
<= 0) extent
= 1;
1254 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1255 TRACE("[%p]: new horz extent = %d\n", descr
->self
, extent
);
1256 descr
->horz_extent
= extent
;
1257 if (descr
->horz_pos
> extent
- descr
->width
)
1258 LISTBOX_SetHorizontalPos( descr
, extent
- descr
->width
);
1260 LISTBOX_UpdateScroll( descr
);
1265 /***********************************************************************
1266 * LISTBOX_SetColumnWidth
1268 static LRESULT
LISTBOX_SetColumnWidth( LB_DESCR
*descr
, INT width
)
1270 if (width
== descr
->column_width
) return LB_OKAY
;
1271 TRACE("[%p]: new column width = %d\n", descr
->self
, width
);
1272 descr
->column_width
= width
;
1273 LISTBOX_UpdatePage( descr
);
1278 /***********************************************************************
1281 * Returns the item height.
1283 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1287 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1292 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1294 ERR("unable to get DC.\n" );
1297 if (font
) oldFont
= SelectObject( hdc
, font
);
1298 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1299 if (oldFont
) SelectObject( hdc
, oldFont
);
1300 ReleaseDC( descr
->self
, hdc
);
1302 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1303 if (!IS_OWNERDRAW(descr
))
1304 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1309 /***********************************************************************
1310 * LISTBOX_MakeItemVisible
1312 * Make sure that a given item is partially or fully visible.
1314 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1318 TRACE("current top item %d, index %d, fully %d\n", descr
->top_item
, index
, fully
);
1320 if (index
<= descr
->top_item
) top
= index
;
1321 else if (descr
->style
& LBS_MULTICOLUMN
)
1323 INT cols
= descr
->width
;
1324 if (!fully
) cols
+= descr
->column_width
- 1;
1325 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1327 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1328 top
= index
- descr
->page_size
* (cols
- 1);
1330 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1332 INT height
= fully
? descr
->items
[index
].height
: 1;
1333 for (top
= index
; top
> descr
->top_item
; top
--)
1334 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1338 if (index
< descr
->top_item
+ descr
->page_size
) return;
1339 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1340 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1341 top
= index
- descr
->page_size
+ 1;
1343 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1346 /***********************************************************************
1347 * LISTBOX_SetCaretIndex
1350 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1353 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1355 INT oldfocus
= descr
->focus_item
;
1357 TRACE("old focus %d, index %d\n", oldfocus
, index
);
1359 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1360 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1361 if (index
== oldfocus
) return LB_OKAY
;
1363 LISTBOX_DrawFocusRect( descr
, FALSE
);
1364 descr
->focus_item
= index
;
1366 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1367 LISTBOX_DrawFocusRect( descr
, TRUE
);
1373 /***********************************************************************
1374 * LISTBOX_SelectItemRange
1376 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1378 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1383 /* A few sanity checks */
1385 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1386 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1388 if (!descr
->nb_items
) return LB_OKAY
;
1390 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1391 if (first
< 0) first
= 0;
1392 if (last
< first
) return LB_OKAY
;
1394 if (on
) /* Turn selection on */
1396 for (i
= first
; i
<= last
; i
++)
1398 if (descr
->items
[i
].selected
) continue;
1399 descr
->items
[i
].selected
= TRUE
;
1400 LISTBOX_InvalidateItemRect(descr
, i
);
1403 else /* Turn selection off */
1405 for (i
= first
; i
<= last
; i
++)
1407 if (!descr
->items
[i
].selected
) continue;
1408 descr
->items
[i
].selected
= FALSE
;
1409 LISTBOX_InvalidateItemRect(descr
, i
);
1415 /***********************************************************************
1416 * LISTBOX_SetSelection
1418 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1419 BOOL on
, BOOL send_notify
)
1421 TRACE( "cur_sel=%d index=%d notify=%s\n",
1422 descr
->selected_item
, index
, send_notify
? "YES" : "NO" );
1424 if (descr
->style
& LBS_NOSEL
)
1426 descr
->selected_item
= index
;
1429 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1430 if (descr
->style
& LBS_MULTIPLESEL
)
1432 if (index
== -1) /* Select all items */
1433 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1434 else /* Only one item */
1435 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1439 INT oldsel
= descr
->selected_item
;
1440 if (index
== oldsel
) return LB_OKAY
;
1441 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1442 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1443 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1444 descr
->selected_item
= index
;
1445 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1446 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1447 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1449 if( descr
->lphc
) /* set selection change flag for parent combo */
1450 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1456 /***********************************************************************
1459 * Change the caret position and extend the selection to the new caret.
1461 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1463 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1465 if ((index
< 0) || (index
>= descr
->nb_items
))
1468 /* Important, repaint needs to be done in this order if
1469 you want to mimic Windows behavior:
1470 1. Remove the focus and paint the item
1471 2. Remove the selection and paint the item(s)
1472 3. Set the selection and repaint the item(s)
1473 4. Set the focus to 'index' and repaint the item */
1475 /* 1. remove the focus and repaint the item */
1476 LISTBOX_DrawFocusRect( descr
, FALSE
);
1478 /* 2. then turn off the previous selection */
1479 /* 3. repaint the new selected item */
1480 if (descr
->style
& LBS_EXTENDEDSEL
)
1482 if (descr
->anchor_item
!= -1)
1484 INT first
= min( index
, descr
->anchor_item
);
1485 INT last
= max( index
, descr
->anchor_item
);
1487 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1488 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1489 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1492 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1494 /* Set selection to new caret item */
1495 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1498 /* 4. repaint the new item with the focus */
1499 descr
->focus_item
= index
;
1500 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1501 LISTBOX_DrawFocusRect( descr
, TRUE
);
1505 /***********************************************************************
1506 * LISTBOX_InsertItem
1508 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1509 LPWSTR str
, ULONG_PTR data
)
1513 INT oldfocus
= descr
->focus_item
;
1515 if (index
== -1) index
= descr
->nb_items
;
1516 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1517 if (!descr
->items
) max_items
= 0;
1518 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1519 if (descr
->nb_items
== max_items
)
1521 /* We need to grow the array */
1522 max_items
+= LB_ARRAY_GRANULARITY
;
1524 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1525 max_items
* sizeof(LB_ITEMDATA
) );
1527 item
= HeapAlloc( GetProcessHeap(), 0,
1528 max_items
* sizeof(LB_ITEMDATA
) );
1531 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1534 descr
->items
= item
;
1537 /* Insert the item structure */
1539 item
= &descr
->items
[index
];
1540 if (index
< descr
->nb_items
)
1541 RtlMoveMemory( item
+ 1, item
,
1542 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1546 item
->selected
= FALSE
;
1549 /* Get item height */
1551 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1553 MEASUREITEMSTRUCT mis
;
1554 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1556 mis
.CtlType
= ODT_LISTBOX
;
1559 mis
.itemData
= descr
->items
[index
].data
;
1560 mis
.itemHeight
= descr
->item_height
;
1561 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1562 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1563 TRACE("[%p]: measure item %d (%s) = %d\n",
1564 descr
->self
, index
, str
? debugstr_w(str
) : "", item
->height
);
1567 /* Repaint the items */
1569 LISTBOX_UpdateScroll( descr
);
1570 LISTBOX_InvalidateItems( descr
, index
);
1572 /* Move selection and focused item */
1573 /* If listbox was empty, set focus to the first item */
1574 if (descr
->nb_items
== 1)
1575 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1576 /* single select don't change selection index in win31 */
1577 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1579 descr
->selected_item
++;
1580 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1584 if (index
<= descr
->selected_item
)
1586 descr
->selected_item
++;
1587 descr
->focus_item
= oldfocus
; /* focus not changed */
1594 /***********************************************************************
1595 * LISTBOX_InsertString
1597 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
, LPCWSTR str
)
1599 LPWSTR new_str
= NULL
;
1603 if (HAS_STRINGS(descr
))
1605 static const WCHAR empty_stringW
[] = { 0 };
1606 if (!str
) str
= empty_stringW
;
1607 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1609 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1612 strcpyW(new_str
, str
);
1614 else data
= (ULONG_PTR
)str
;
1616 if (index
== -1) index
= descr
->nb_items
;
1617 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, data
)) != 0)
1619 HeapFree( GetProcessHeap(), 0, new_str
);
1623 TRACE("[%p]: added item %d %s\n",
1624 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1629 /***********************************************************************
1630 * LISTBOX_DeleteItem
1632 * Delete the content of an item. 'index' must be a valid index.
1634 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1636 /* save the item data before it gets freed by LB_RESETCONTENT */
1637 ULONG_PTR item_data
= descr
->items
[index
].data
;
1638 LPWSTR item_str
= descr
->items
[index
].str
;
1640 if (!descr
->nb_items
)
1641 SendMessageW( descr
->self
, LB_RESETCONTENT
, 0, 0 );
1643 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1644 * while Win95 sends it for all items with user data.
1645 * It's probably better to send it too often than not
1646 * often enough, so this is what we do here.
1648 if (IS_OWNERDRAW(descr
) || item_data
)
1650 DELETEITEMSTRUCT dis
;
1651 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1653 dis
.CtlType
= ODT_LISTBOX
;
1656 dis
.hwndItem
= descr
->self
;
1657 dis
.itemData
= item_data
;
1658 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1660 if (HAS_STRINGS(descr
))
1661 HeapFree( GetProcessHeap(), 0, item_str
);
1665 /***********************************************************************
1666 * LISTBOX_RemoveItem
1668 * Remove an item from the listbox and delete its content.
1670 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1675 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1677 /* We need to invalidate the original rect instead of the updated one. */
1678 LISTBOX_InvalidateItems( descr
, index
);
1681 LISTBOX_DeleteItem( descr
, index
);
1683 if (!descr
->nb_items
) return LB_OKAY
;
1685 /* Remove the item */
1687 item
= &descr
->items
[index
];
1688 if (index
< descr
->nb_items
)
1689 RtlMoveMemory( item
, item
+ 1,
1690 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1691 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1693 /* Shrink the item array if possible */
1695 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1696 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1698 max_items
-= LB_ARRAY_GRANULARITY
;
1699 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1700 max_items
* sizeof(LB_ITEMDATA
) );
1701 if (item
) descr
->items
= item
;
1703 /* Repaint the items */
1705 LISTBOX_UpdateScroll( descr
);
1706 /* if we removed the scrollbar, reset the top of the list
1707 (correct for owner-drawn ???) */
1708 if (descr
->nb_items
== descr
->page_size
)
1709 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1711 /* Move selection and focused item */
1712 if (!IS_MULTISELECT(descr
))
1714 if (index
== descr
->selected_item
)
1715 descr
->selected_item
= -1;
1716 else if (index
< descr
->selected_item
)
1718 descr
->selected_item
--;
1719 if (ISWIN31
) /* win 31 do not change the selected item number */
1720 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1724 if (descr
->focus_item
>= descr
->nb_items
)
1726 descr
->focus_item
= descr
->nb_items
- 1;
1727 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1733 /***********************************************************************
1734 * LISTBOX_ResetContent
1736 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1740 for(i
= descr
->nb_items
- 1; i
>=0; i
--) LISTBOX_DeleteItem( descr
, i
);
1741 HeapFree( GetProcessHeap(), 0, descr
->items
);
1742 descr
->nb_items
= 0;
1743 descr
->top_item
= 0;
1744 descr
->selected_item
= -1;
1745 descr
->focus_item
= 0;
1746 descr
->anchor_item
= -1;
1747 descr
->items
= NULL
;
1751 /***********************************************************************
1754 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, INT count
)
1758 if (HAS_STRINGS(descr
))
1760 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1764 /* FIXME: this is far from optimal... */
1765 if (count
> descr
->nb_items
)
1767 while (count
> descr
->nb_items
)
1768 if ((ret
= LISTBOX_InsertString( descr
, -1, 0 )) < 0)
1771 else if (count
< descr
->nb_items
)
1773 while (count
< descr
->nb_items
)
1774 if ((ret
= LISTBOX_RemoveItem( descr
, (descr
->nb_items
- 1) )) < 0)
1781 /***********************************************************************
1784 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1785 LPCWSTR filespec
, BOOL long_names
)
1788 LRESULT ret
= LB_OKAY
;
1789 WIN32_FIND_DATAW entry
;
1791 LRESULT maxinsert
= LB_ERR
;
1793 /* don't scan directory if we just want drives exclusively */
1794 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1795 /* scan directory */
1796 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1798 int le
= GetLastError();
1799 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1806 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1808 static const WCHAR bracketW
[] = { ']',0 };
1809 static const WCHAR dotW
[] = { '.',0 };
1810 if (!(attrib
& DDL_DIRECTORY
) ||
1811 !strcmpW( entry
.cFileName
, dotW
)) continue;
1813 if (!long_names
&& entry
.cAlternateFileName
[0])
1814 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1816 strcpyW( buffer
+ 1, entry
.cFileName
);
1817 strcatW(buffer
, bracketW
);
1819 else /* not a directory */
1821 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1822 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1824 if ((attrib
& DDL_EXCLUSIVE
) &&
1825 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1828 if (!long_names
&& entry
.cAlternateFileName
[0])
1829 strcpyW( buffer
, entry
.cAlternateFileName
);
1831 strcpyW( buffer
, entry
.cFileName
);
1833 if (!long_names
) CharLowerW( buffer
);
1834 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1835 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1837 if (ret
<= maxinsert
) maxinsert
++; else maxinsert
= ret
;
1838 } while (FindNextFileW( handle
, &entry
));
1839 FindClose( handle
);
1847 if (attrib
& DDL_DRIVES
)
1849 WCHAR buffer
[] = {'[','-','a','-',']',0};
1850 WCHAR root
[] = {'A',':','\\',0};
1852 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1854 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1855 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1864 /***********************************************************************
1865 * LISTBOX_HandleVScroll
1867 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1871 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1875 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1878 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1881 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1882 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1885 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1886 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1888 case SB_THUMBPOSITION
:
1889 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1892 info
.cbSize
= sizeof(info
);
1893 info
.fMask
= SIF_TRACKPOS
;
1894 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1895 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1898 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1901 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1908 /***********************************************************************
1909 * LISTBOX_HandleHScroll
1911 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1916 if (descr
->style
& LBS_MULTICOLUMN
)
1921 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
1925 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
1929 page
= descr
->width
/ descr
->column_width
;
1930 if (page
< 1) page
= 1;
1931 LISTBOX_SetTopItem( descr
,
1932 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1935 page
= descr
->width
/ descr
->column_width
;
1936 if (page
< 1) page
= 1;
1937 LISTBOX_SetTopItem( descr
,
1938 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1940 case SB_THUMBPOSITION
:
1941 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
1944 info
.cbSize
= sizeof(info
);
1945 info
.fMask
= SIF_TRACKPOS
;
1946 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1947 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
1951 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1954 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1958 else if (descr
->horz_extent
)
1963 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
1966 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
1969 LISTBOX_SetHorizontalPos( descr
,
1970 descr
->horz_pos
- descr
->width
);
1973 LISTBOX_SetHorizontalPos( descr
,
1974 descr
->horz_pos
+ descr
->width
);
1976 case SB_THUMBPOSITION
:
1977 LISTBOX_SetHorizontalPos( descr
, pos
);
1980 info
.cbSize
= sizeof(info
);
1981 info
.fMask
= SIF_TRACKPOS
;
1982 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
1983 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
1986 LISTBOX_SetHorizontalPos( descr
, 0 );
1989 LISTBOX_SetHorizontalPos( descr
,
1990 descr
->horz_extent
- descr
->width
);
1997 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
1999 short gcWheelDelta
= 0;
2000 UINT pulScrollLines
= 3;
2002 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
2004 gcWheelDelta
-= delta
;
2006 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
2008 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
2009 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
2010 LISTBOX_SetTopItem( descr
, descr
->top_item
+ cLineScroll
, TRUE
);
2015 /***********************************************************************
2016 * LISTBOX_HandleLButtonDown
2018 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
2020 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2022 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2023 descr
->self
, x
, y
, index
, descr
->focus_item
);
2025 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2027 if (!descr
->in_focus
)
2029 if( !descr
->lphc
) SetFocus( descr
->self
);
2030 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2033 if (index
== -1) return 0;
2037 if (descr
->style
& LBS_NOTIFY
)
2038 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2039 MAKELPARAM( x
, y
) );
2042 descr
->captured
= TRUE
;
2043 SetCapture( descr
->self
);
2045 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2047 /* we should perhaps make sure that all items are deselected
2048 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2049 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2050 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2053 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2054 if (keys
& MK_CONTROL
)
2056 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2057 LISTBOX_SetSelection( descr
, index
,
2058 !descr
->items
[index
].selected
,
2059 (descr
->style
& LBS_NOTIFY
) != 0);
2063 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2065 if (descr
->style
& LBS_EXTENDEDSEL
)
2067 LISTBOX_SetSelection( descr
, index
,
2068 descr
->items
[index
].selected
,
2069 (descr
->style
& LBS_NOTIFY
) != 0 );
2073 LISTBOX_SetSelection( descr
, index
,
2074 !descr
->items
[index
].selected
,
2075 (descr
->style
& LBS_NOTIFY
) != 0 );
2081 descr
->anchor_item
= index
;
2082 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2083 LISTBOX_SetSelection( descr
, index
,
2084 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2088 { // See rev 40864 use Ptr for 64 bit.
2089 if (GetWindowLongPtrW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2096 if (DragDetect( descr
->self
, pt
))
2097 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2104 /*************************************************************************
2105 * LISTBOX_HandleLButtonDownCombo [Internal]
2107 * Process LButtonDown message for the ComboListBox
2110 * pWnd [I] The windows internal structure
2111 * pDescr [I] The ListBox internal structure
2112 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2113 * x [I] X Mouse Coordinate
2114 * y [I] Y Mouse Coordinate
2117 * 0 since we are processing the WM_LBUTTONDOWN Message
2120 * This function is only to be used when a ListBox is a ComboListBox
2123 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2125 RECT clientRect
, screenRect
;
2131 GetClientRect(descr
->self
, &clientRect
);
2133 if(PtInRect(&clientRect
, mousePos
))
2135 /* MousePos is in client, resume normal processing */
2136 if (msg
== WM_LBUTTONDOWN
)
2138 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2139 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2141 else if (descr
->style
& LBS_NOTIFY
)
2142 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2146 POINT screenMousePos
;
2147 HWND hWndOldCapture
;
2149 /* Check the Non-Client Area */
2150 screenMousePos
= mousePos
;
2151 hWndOldCapture
= GetCapture();
2153 GetWindowRect(descr
->self
, &screenRect
);
2154 ClientToScreen(descr
->self
, &screenMousePos
);
2156 if(!PtInRect(&screenRect
, screenMousePos
))
2158 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2159 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2160 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2164 /* Check to see the NC is a scrollbar */
2166 LONG style
= GetWindowLongPtrW( descr
->self
, GWL_STYLE
);
2167 /* Check Vertical scroll bar */
2168 if (style
& WS_VSCROLL
)
2170 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2171 if (PtInRect( &clientRect
, mousePos
))
2172 nHitTestType
= HTVSCROLL
;
2174 /* Check horizontal scroll bar */
2175 if (style
& WS_HSCROLL
)
2177 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2178 if (PtInRect( &clientRect
, mousePos
))
2179 nHitTestType
= HTHSCROLL
;
2181 /* Windows sends this message when a scrollbar is clicked
2184 if(nHitTestType
!= 0)
2186 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2187 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2189 /* Resume the Capture after scrolling is complete
2191 if(hWndOldCapture
!= 0)
2192 SetCapture(hWndOldCapture
);
2198 /***********************************************************************
2199 * LISTBOX_HandleLButtonUp
2201 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2203 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2204 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2205 LISTBOX_Timer
= LB_TIMER_NONE
;
2206 if (descr
->captured
)
2208 descr
->captured
= FALSE
;
2209 if (GetCapture() == descr
->self
) ReleaseCapture();
2210 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2211 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2217 /***********************************************************************
2218 * LISTBOX_HandleTimer
2220 * Handle scrolling upon a timer event.
2221 * Return TRUE if scrolling should continue.
2223 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2228 if (descr
->top_item
) index
= descr
->top_item
- 1;
2232 if (descr
->top_item
) index
-= descr
->page_size
;
2235 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2236 if (index
== descr
->focus_item
) index
++;
2237 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2239 case LB_TIMER_RIGHT
:
2240 if (index
+ descr
->page_size
< descr
->nb_items
)
2241 index
+= descr
->page_size
;
2246 if (index
== descr
->focus_item
) return FALSE
;
2247 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2252 /***********************************************************************
2253 * LISTBOX_HandleSystemTimer
2255 * WM_SYSTIMER handler.
2257 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2259 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2261 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2262 LISTBOX_Timer
= LB_TIMER_NONE
;
2268 /***********************************************************************
2269 * LISTBOX_HandleMouseMove
2271 * WM_MOUSEMOVE handler.
2273 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2277 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2279 if (!descr
->captured
) return;
2281 if (descr
->style
& LBS_MULTICOLUMN
)
2284 else if (y
>= descr
->item_height
* descr
->page_size
)
2285 y
= descr
->item_height
* descr
->page_size
- 1;
2289 dir
= LB_TIMER_LEFT
;
2292 else if (x
>= descr
->width
)
2294 dir
= LB_TIMER_RIGHT
;
2295 x
= descr
->width
- 1;
2300 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2301 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2304 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2305 if (index
== -1) index
= descr
->focus_item
;
2306 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2308 /* Start/stop the system timer */
2310 if (dir
!= LB_TIMER_NONE
)
2311 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2312 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2313 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2314 LISTBOX_Timer
= dir
;
2318 /***********************************************************************
2319 * LISTBOX_HandleKeyDown
2321 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2324 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2325 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2326 bForceSelection
= FALSE
; /* only for single select list */
2328 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2330 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2331 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2332 (LPARAM
)descr
->self
);
2333 if (caret
== -2) return 0;
2335 if (caret
== -1) switch(key
)
2338 if (descr
->style
& LBS_MULTICOLUMN
)
2340 bForceSelection
= FALSE
;
2341 if (descr
->focus_item
>= descr
->page_size
)
2342 caret
= descr
->focus_item
- descr
->page_size
;
2347 caret
= descr
->focus_item
- 1;
2348 if (caret
< 0) caret
= 0;
2351 if (descr
->style
& LBS_MULTICOLUMN
)
2353 bForceSelection
= FALSE
;
2354 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2355 caret
= descr
->focus_item
+ descr
->page_size
;
2360 caret
= descr
->focus_item
+ 1;
2361 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2365 if (descr
->style
& LBS_MULTICOLUMN
)
2367 INT page
= descr
->width
/ descr
->column_width
;
2368 if (page
< 1) page
= 1;
2369 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2371 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2372 if (caret
< 0) caret
= 0;
2375 if (descr
->style
& LBS_MULTICOLUMN
)
2377 INT page
= descr
->width
/ descr
->column_width
;
2378 if (page
< 1) page
= 1;
2379 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2381 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2382 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2388 caret
= descr
->nb_items
- 1;
2391 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2392 else if (descr
->style
& LBS_MULTIPLESEL
)
2394 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2395 !descr
->items
[descr
->focus_item
].selected
,
2396 (descr
->style
& LBS_NOTIFY
) != 0 );
2400 bForceSelection
= FALSE
;
2402 if (bForceSelection
) /* focused item is used instead of key */
2403 caret
= descr
->focus_item
;
2406 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2407 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2408 !IS_MULTISELECT(descr
))
2409 descr
->anchor_item
= caret
;
2410 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2412 if (descr
->style
& LBS_MULTIPLESEL
)
2413 descr
->selected_item
= caret
;
2415 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2416 if (descr
->style
& LBS_NOTIFY
)
2418 if (descr
->lphc
&& IsWindowVisible( descr
->self
))
2420 /* make sure that combo parent doesn't hide us */
2421 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2423 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2430 /***********************************************************************
2431 * LISTBOX_HandleChar
2433 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2441 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2443 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2444 MAKEWPARAM(charW
, descr
->focus_item
),
2445 (LPARAM
)descr
->self
);
2446 if (caret
== -2) return 0;
2449 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2452 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2453 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2454 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2455 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2456 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2461 /* ReactOS Retrieve the UI state for the control */
2462 static BOOL
LISTBOX_update_uistate(LB_DESCR
*descr
)
2466 prev_flags
= descr
->UIState
;
2467 descr
->UIState
= DefWindowProcW(descr
->self
, WM_QUERYUISTATE
, 0, 0);
2468 return prev_flags
!= descr
->UIState
;
2472 /***********************************************************************
2475 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2478 MEASUREITEMSTRUCT mis
;
2481 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2484 GetClientRect( hwnd
, &rect
);
2486 descr
->owner
= GetParent( descr
->self
);
2487 descr
->style
= GetWindowLongPtrW( descr
->self
, GWL_STYLE
);
2488 descr
->width
= rect
.right
- rect
.left
;
2489 descr
->height
= rect
.bottom
- rect
.top
;
2490 descr
->items
= NULL
;
2491 descr
->nb_items
= 0;
2492 descr
->top_item
= 0;
2493 descr
->selected_item
= -1;
2494 descr
->focus_item
= 0;
2495 descr
->anchor_item
= -1;
2496 descr
->item_height
= 1;
2497 descr
->page_size
= 1;
2498 descr
->column_width
= 150;
2499 descr
->horz_extent
= (descr
->style
& WS_HSCROLL
) ? 1 : 0;
2500 descr
->horz_pos
= 0;
2503 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2504 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2505 descr
->in_focus
= FALSE
;
2506 descr
->captured
= FALSE
;
2508 descr
->locale
= GetUserDefaultLCID();
2513 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2514 descr
->owner
= lphc
->self
;
2517 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2519 LISTBOX_update_uistate(descr
); // ReactOS
2521 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2523 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2524 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2525 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2528 /* A no-data list box must also have the LBS_OWNERDRAWFIXED style, but must
2529 not have the LBS_SORT or LBS_HASSTRINGS style. */
2530 if ( descr
->style
& LBS_NODATA
&&
2531 (!(descr
->style
& LBS_OWNERDRAWFIXED
) || descr
->style
& (LBS_HASSTRINGS
|LBS_SORT
) ) )
2532 descr
->style
&= ~LBS_NODATA
;
2534 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2536 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2538 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2540 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2541 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2545 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2546 mis
.CtlType
= ODT_LISTBOX
;
2551 mis
.itemHeight
= descr
->item_height
;
2552 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2553 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2557 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2562 /***********************************************************************
2565 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2567 LISTBOX_ResetContent( descr
);
2568 SetWindowLongPtrW( descr
->self
, 0, 0 );
2569 HeapFree( GetProcessHeap(), 0, descr
);
2574 /***********************************************************************
2575 * ListBoxWndProc_common
2577 LRESULT WINAPI
ListBoxWndProc_common( HWND hwnd
, UINT msg
,
2578 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2580 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2581 LPHEADCOMBO lphc
= 0;
2586 pWnd
= ValidateHwnd(hwnd
);
2591 NtUserSetWindowFNID(hwnd
, FNID_LISTBOX
); // Could be FNID_COMBOLBOX by class.
2595 if (pWnd
->fnid
!= FNID_LISTBOX
)
2597 ERR("Wrong window class for listbox! fnId 0x%x\n",pWnd
->fnid
);
2606 if (!IsWindow(hwnd
)) return 0;
2608 if (msg
== WM_CREATE
)
2610 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2611 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= lpcs
->lpCreateParams
;
2612 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2613 TRACE("creating hwnd %p descr %p\n", hwnd
, (void *)GetWindowLongPtrW( hwnd
, 0 ) );
2616 /* Ignore all other messages before we get a WM_CREATE */
2617 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2618 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2620 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2622 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2623 descr
->self
, SPY_GetMsgName(msg
, descr
->self
), wParam
, lParam
);
2627 case LB_RESETCONTENT
:
2628 LISTBOX_ResetContent( descr
);
2629 LISTBOX_UpdateScroll( descr
);
2630 InvalidateRect( descr
->self
, NULL
, TRUE
);
2634 case LB_ADDSTRING_LOWER
:
2635 case LB_ADDSTRING_UPPER
:
2639 if(unicode
|| !HAS_STRINGS(descr
))
2640 textW
= (LPWSTR
)lParam
;
2643 LPSTR textA
= (LPSTR
)lParam
;
2644 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2645 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2646 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2650 /* in the unicode the version, the string is really overwritten
2651 during the converting case */
2652 if (msg
== LB_ADDSTRING_LOWER
)
2654 else if (msg
== LB_ADDSTRING_UPPER
)
2656 wParam
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2657 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2658 if (!unicode
&& HAS_STRINGS(descr
))
2659 HeapFree(GetProcessHeap(), 0, textW
);
2663 case LB_INSERTSTRING
:
2664 case LB_INSERTSTRING_UPPER
:
2665 case LB_INSERTSTRING_LOWER
:
2669 if(unicode
|| !HAS_STRINGS(descr
))
2670 textW
= (LPWSTR
)lParam
;
2673 LPSTR textA
= (LPSTR
)lParam
;
2674 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2675 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2676 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2680 /* in the unicode the version, the string is really overwritten
2681 during the converting case */
2682 if (msg
== LB_INSERTSTRING_LOWER
)
2684 else if (msg
== LB_INSERTSTRING_UPPER
)
2686 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2687 if(!unicode
&& HAS_STRINGS(descr
))
2688 HeapFree(GetProcessHeap(), 0, textW
);
2696 if(unicode
|| !HAS_STRINGS(descr
))
2697 textW
= (LPWSTR
)lParam
;
2700 LPSTR textA
= (LPSTR
)lParam
;
2701 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2702 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2703 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2707 wParam
= LISTBOX_FindFileStrPos( descr
, textW
);
2708 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2709 if(!unicode
&& HAS_STRINGS(descr
))
2710 HeapFree(GetProcessHeap(), 0, textW
);
2714 case LB_DELETESTRING
:
2715 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2716 return descr
->nb_items
;
2719 SetLastError(ERROR_INVALID_INDEX
);
2723 case LB_GETITEMDATA
:
2724 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2726 SetLastError(ERROR_INVALID_INDEX
);
2729 return descr
->items
[wParam
].data
;
2731 case LB_SETITEMDATA
:
2732 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2734 SetLastError(ERROR_INVALID_INDEX
);
2737 descr
->items
[wParam
].data
= lParam
;
2738 /* undocumented: returns TRUE, not LB_OKAY (0) */
2742 return descr
->nb_items
;
2745 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, unicode
);
2748 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2750 SetLastError(ERROR_INVALID_INDEX
);
2753 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2754 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2755 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2756 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2759 if (descr
->nb_items
== 0)
2761 if (!IS_MULTISELECT(descr
))
2762 return descr
->selected_item
;
2763 if (descr
->selected_item
!= -1)
2764 return descr
->selected_item
;
2765 return descr
->focus_item
;
2766 /* otherwise, if the user tries to move the selection with the */
2767 /* arrow keys, we will give the application something to choke on */
2768 case LB_GETTOPINDEX
:
2769 return descr
->top_item
;
2771 case LB_GETITEMHEIGHT
:
2772 return LISTBOX_GetItemHeight( descr
, wParam
);
2774 case LB_SETITEMHEIGHT
:
2775 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2777 case LB_ITEMFROMPOINT
:
2784 /* The hiword of the return value is not a client area
2785 hittest as suggested by MSDN, but rather a hittest on
2786 the returned listbox item. */
2788 if(descr
->nb_items
== 0)
2789 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2791 pt
.x
= (short)LOWORD(lParam
);
2792 pt
.y
= (short)HIWORD(lParam
);
2794 SetRect(&rect
, 0, 0, descr
->width
, descr
->height
);
2796 if(!PtInRect(&rect
, pt
))
2798 pt
.x
= min(pt
.x
, rect
.right
- 1);
2799 pt
.x
= max(pt
.x
, 0);
2800 pt
.y
= min(pt
.y
, rect
.bottom
- 1);
2801 pt
.y
= max(pt
.y
, 0);
2805 index
= LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
);
2809 index
= descr
->nb_items
- 1;
2812 return MAKELONG(index
, hit
? 0 : 1);
2815 case LB_SETCARETINDEX
:
2816 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2817 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2824 case LB_GETCARETINDEX
:
2825 return descr
->focus_item
;
2827 case LB_SETTOPINDEX
:
2828 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2830 case LB_SETCOLUMNWIDTH
:
2831 return LISTBOX_SetColumnWidth( descr
, wParam
);
2833 case LB_GETITEMRECT
:
2834 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2840 if(unicode
|| !HAS_STRINGS(descr
))
2841 textW
= (LPWSTR
)lParam
;
2844 LPSTR textA
= (LPSTR
)lParam
;
2845 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2846 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2847 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2849 ret
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2850 if(!unicode
&& HAS_STRINGS(descr
))
2851 HeapFree(GetProcessHeap(), 0, textW
);
2855 case LB_FINDSTRINGEXACT
:
2859 if(unicode
|| !HAS_STRINGS(descr
))
2860 textW
= (LPWSTR
)lParam
;
2863 LPSTR textA
= (LPSTR
)lParam
;
2864 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2865 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2866 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2868 ret
= LISTBOX_FindString( descr
, wParam
, textW
, TRUE
);
2869 if(!unicode
&& HAS_STRINGS(descr
))
2870 HeapFree(GetProcessHeap(), 0, textW
);
2874 case LB_SELECTSTRING
:
2879 if(HAS_STRINGS(descr
))
2880 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2881 debugstr_a((LPSTR
)lParam
));
2882 if(unicode
|| !HAS_STRINGS(descr
))
2883 textW
= (LPWSTR
)lParam
;
2886 LPSTR textA
= (LPSTR
)lParam
;
2887 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2888 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2889 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2891 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2892 if(!unicode
&& HAS_STRINGS(descr
))
2893 HeapFree(GetProcessHeap(), 0, textW
);
2894 if (index
!= LB_ERR
)
2896 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2897 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2903 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2905 return descr
->items
[wParam
].selected
;
2908 return LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2911 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2912 LISTBOX_SetCaretIndex( descr
, wParam
, FALSE
);
2913 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2914 if (ret
!= LB_ERR
) ret
= descr
->selected_item
;
2917 case LB_GETSELCOUNT
:
2918 return LISTBOX_GetSelCount( descr
);
2920 case LB_GETSELITEMS
:
2921 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2923 case LB_SELITEMRANGE
:
2924 if (LOWORD(lParam
) <= HIWORD(lParam
))
2925 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2926 HIWORD(lParam
), wParam
);
2928 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2929 LOWORD(lParam
), wParam
);
2931 case LB_SELITEMRANGEEX
:
2932 if ((INT
)lParam
>= (INT
)wParam
)
2933 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2935 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
2937 case LB_GETHORIZONTALEXTENT
:
2938 return descr
->horz_extent
;
2940 case LB_SETHORIZONTALEXTENT
:
2941 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
2943 case LB_GETANCHORINDEX
:
2944 return descr
->anchor_item
;
2946 case LB_SETANCHORINDEX
:
2947 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2949 SetLastError(ERROR_INVALID_INDEX
);
2952 descr
->anchor_item
= (INT
)wParam
;
2960 textW
= (LPWSTR
)lParam
;
2963 LPSTR textA
= (LPSTR
)lParam
;
2964 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2965 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2966 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2968 ret
= LISTBOX_Directory( descr
, wParam
, textW
, msg
== LB_DIR
);
2970 HeapFree(GetProcessHeap(), 0, textW
);
2975 return descr
->locale
;
2980 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
2982 ret
= descr
->locale
;
2983 descr
->locale
= (LCID
)wParam
;
2987 case LB_INITSTORAGE
:
2988 return LISTBOX_InitStorage( descr
, wParam
);
2991 return LISTBOX_SetCount( descr
, (INT
)wParam
);
2993 case LB_SETTABSTOPS
:
2994 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
);
2997 if (descr
->caret_on
)
2999 descr
->caret_on
= TRUE
;
3000 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3001 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3005 if (!descr
->caret_on
)
3007 descr
->caret_on
= FALSE
;
3008 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3009 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3012 case LB_GETLISTBOXINFO
: //// ReactOS
3013 if (descr
->style
& LBS_MULTICOLUMN
)
3014 return descr
->column_width
;
3016 return descr
->nb_items
;
3019 return LISTBOX_Destroy( descr
);
3022 InvalidateRect( descr
->self
, NULL
, TRUE
);
3026 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
3030 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
3032 case WM_PRINTCLIENT
:
3036 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
3037 ret
= LISTBOX_Paint( descr
, hdc
);
3038 if( !wParam
) EndPaint( descr
->self
, &ps
);
3042 LISTBOX_UpdateSize( descr
);
3045 return (LRESULT
)descr
->font
;
3047 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
3048 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
3051 descr
->in_focus
= TRUE
;
3052 descr
->caret_on
= TRUE
;
3053 if (descr
->focus_item
!= -1)
3054 LISTBOX_DrawFocusRect( descr
, TRUE
);
3055 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
3058 descr
->in_focus
= FALSE
;
3059 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
3060 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3061 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
3064 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3066 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3068 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
3069 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
3070 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
3071 case WM_LBUTTONDOWN
:
3073 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3074 (INT16
)LOWORD(lParam
),
3075 (INT16
)HIWORD(lParam
) );
3076 return LISTBOX_HandleLButtonDown( descr
, wParam
,
3077 (INT16
)LOWORD(lParam
),
3078 (INT16
)HIWORD(lParam
) );
3079 case WM_LBUTTONDBLCLK
:
3081 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3082 (INT16
)LOWORD(lParam
),
3083 (INT16
)HIWORD(lParam
) );
3084 if (descr
->style
& LBS_NOTIFY
)
3085 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
3088 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
3090 BOOL captured
= descr
->captured
;
3094 mousePos
.x
= (INT16
)LOWORD(lParam
);
3095 mousePos
.y
= (INT16
)HIWORD(lParam
);
3098 * If we are in a dropdown combobox, we simulate that
3099 * the mouse is captured to show the tracking of the item.
3101 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
3102 descr
->captured
= TRUE
;
3104 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
3106 descr
->captured
= captured
;
3108 else if (GetCapture() == descr
->self
)
3110 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
3111 (INT16
)HIWORD(lParam
) );
3121 * If the mouse button "up" is not in the listbox,
3122 * we make sure there is no selection by re-selecting the
3123 * item that was selected when the listbox was made visible.
3125 mousePos
.x
= (INT16
)LOWORD(lParam
);
3126 mousePos
.y
= (INT16
)HIWORD(lParam
);
3128 GetClientRect(descr
->self
, &clientRect
);
3131 * When the user clicks outside the combobox and the focus
3132 * is lost, the owning combobox will send a fake buttonup with
3133 * 0xFFFFFFF as the mouse location, we must also revert the
3134 * selection to the original selection.
3136 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
3137 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
3139 return LISTBOX_HandleLButtonUp( descr
);
3141 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3143 /* for some reason Windows makes it possible to
3144 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3146 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3147 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3148 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3150 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3154 return LISTBOX_HandleKeyDown( descr
, wParam
);
3159 charW
= (WCHAR
)wParam
;
3162 CHAR charA
= (CHAR
)wParam
;
3163 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
3165 return LISTBOX_HandleChar( descr
, charW
);
3168 return LISTBOX_HandleSystemTimer( descr
);
3170 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3173 HBRUSH hbrush
= GetControlColor( descr
->owner
, descr
->self
, (HDC
)wParam
, WM_CTLCOLORLISTBOX
);
3174 TRACE("hbrush = %p\n", hbrush
);
3176 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3179 GetClientRect(descr
->self
, &rect
);
3180 FillRect((HDC
)wParam
, &rect
, hbrush
);
3185 if( lphc
) return 0;
3186 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3187 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3190 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3193 NtUserSetWindowFNID(hwnd
, FNID_DESTROY
);
3201 case WM_UPDATEUISTATE
:
3203 DefWindowProcW(descr
->self
, msg
, wParam
, lParam
);
3205 DefWindowProcA(descr
->self
, msg
, wParam
, lParam
);
3207 if (LISTBOX_update_uistate(descr
))
3210 if (descr
->focus_item
!= -1)
3211 LISTBOX_DrawFocusRect( descr
, descr
->in_focus
);
3216 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3217 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3218 hwnd
, msg
, wParam
, lParam
);
3221 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3222 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3225 /***********************************************************************
3228 LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3230 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3233 /***********************************************************************
3236 LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3238 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);