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 (0 if no hscroll) */
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
->horz_extent
)
265 info
.nMax
= descr
->horz_extent
- 1;
266 info
.nPos
= descr
->horz_pos
;
267 info
.nPage
= descr
->width
;
268 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
269 if (descr
->style
& LBS_DISABLENOSCROLL
)
270 info
.fMask
|= SIF_DISABLENOSCROLL
;
271 if (descr
->style
& WS_HSCROLL
)
272 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
278 /***********************************************************************
281 * Set the top item of the listbox, scrolling up or down if necessary.
283 static LRESULT
LISTBOX_SetTopItem( LB_DESCR
*descr
, INT index
, BOOL scroll
)
285 INT max
= LISTBOX_GetMaxTopIndex( descr
);
287 TRACE("setting top item %d, scroll %d\n", index
, scroll
);
289 if (index
> max
) index
= max
;
290 if (index
< 0) index
= 0;
291 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
292 if (descr
->top_item
== index
) return LB_OKAY
;
296 if (descr
->style
& LBS_MULTICOLUMN
)
297 diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
298 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
302 if (index
> descr
->top_item
)
304 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
305 diff
-= descr
->items
[i
].height
;
309 for (i
= index
; i
< descr
->top_item
; i
++)
310 diff
+= descr
->items
[i
].height
;
314 diff
= (descr
->top_item
- index
) * descr
->item_height
;
316 ScrollWindowEx( descr
->self
, 0, diff
, NULL
, NULL
, 0, NULL
,
317 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
319 if (!scroll
) InvalidateRect( descr
->self
, NULL
, TRUE
);
320 descr
->top_item
= index
;
321 LISTBOX_UpdateScroll( descr
);
326 /***********************************************************************
329 * Update the page size. Should be called when the size of
330 * the client area or the item height changes.
332 static void LISTBOX_UpdatePage( LB_DESCR
*descr
)
336 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
338 if (page_size
== descr
->page_size
) return;
339 descr
->page_size
= page_size
;
340 if (descr
->style
& LBS_MULTICOLUMN
)
341 InvalidateRect( descr
->self
, NULL
, TRUE
);
342 LISTBOX_SetTopItem( descr
, descr
->top_item
, FALSE
);
346 /***********************************************************************
349 * Update the size of the listbox. Should be called when the size of
350 * the client area changes.
352 static void LISTBOX_UpdateSize( LB_DESCR
*descr
)
356 GetClientRect( descr
->self
, &rect
);
357 descr
->width
= rect
.right
- rect
.left
;
358 descr
->height
= rect
.bottom
- rect
.top
;
359 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
364 GetWindowRect( descr
->self
, &rect
);
365 if(descr
->item_height
!= 0)
366 remaining
= descr
->height
% descr
->item_height
;
369 if ((descr
->height
> descr
->item_height
) && remaining
)
371 TRACE("[%p]: changing height %d -> %d\n",
372 descr
->self
, descr
->height
, descr
->height
- remaining
);
373 SetWindowPos( descr
->self
, 0, 0, 0, rect
.right
- rect
.left
,
374 rect
.bottom
- rect
.top
- remaining
,
375 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
379 TRACE("[%p]: new size = %d,%d\n", descr
->self
, descr
->width
, descr
->height
);
380 LISTBOX_UpdatePage( descr
);
381 LISTBOX_UpdateScroll( descr
);
383 /* Invalidate the focused item so it will be repainted correctly */
384 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
386 InvalidateRect( descr
->self
, &rect
, FALSE
);
391 /***********************************************************************
392 * LISTBOX_GetItemRect
394 * Get the rectangle enclosing an item, in listbox client coordinates.
395 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
397 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
)
399 /* Index <= 0 is legal even on empty listboxes */
400 if (index
&& (index
>= descr
->nb_items
))
402 memset(rect
, 0, sizeof(*rect
));
403 SetLastError(ERROR_INVALID_INDEX
);
406 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
407 if (descr
->style
& LBS_MULTICOLUMN
)
409 INT col
= (index
/ descr
->page_size
) -
410 (descr
->top_item
/ descr
->page_size
);
411 rect
->left
+= col
* descr
->column_width
;
412 rect
->right
= rect
->left
+ descr
->column_width
;
413 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
414 rect
->bottom
= rect
->top
+ descr
->item_height
;
416 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
419 rect
->right
+= descr
->horz_pos
;
420 if ((index
>= 0) && (index
< descr
->nb_items
))
422 if (index
< descr
->top_item
)
424 for (i
= descr
->top_item
-1; i
>= index
; i
--)
425 rect
->top
-= descr
->items
[i
].height
;
429 for (i
= descr
->top_item
; i
< index
; i
++)
430 rect
->top
+= descr
->items
[i
].height
;
432 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
438 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
439 rect
->bottom
= rect
->top
+ descr
->item_height
;
440 rect
->right
+= descr
->horz_pos
;
443 TRACE("item %d, rect %s\n", index
, wine_dbgstr_rect(rect
));
445 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
446 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
450 /***********************************************************************
451 * LISTBOX_GetItemFromPoint
453 * Return the item nearest from point (x,y) (in client coordinates).
455 static INT
LISTBOX_GetItemFromPoint( const LB_DESCR
*descr
, INT x
, INT y
)
457 INT index
= descr
->top_item
;
459 if (!descr
->nb_items
) return -1; /* No items */
460 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
465 while (index
< descr
->nb_items
)
467 if ((pos
+= descr
->items
[index
].height
) > y
) break;
476 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
480 else if (descr
->style
& LBS_MULTICOLUMN
)
482 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
483 if (y
>= 0) index
+= y
/ descr
->item_height
;
484 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
485 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
489 index
+= (y
/ descr
->item_height
);
491 if (index
< 0) return 0;
492 if (index
>= descr
->nb_items
) return -1;
497 /***********************************************************************
502 static void LISTBOX_PaintItem( LB_DESCR
*descr
, HDC hdc
, const RECT
*rect
,
503 INT index
, UINT action
, BOOL ignoreFocus
)
505 LB_ITEMDATA
*item
= NULL
;
506 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
508 if (IS_OWNERDRAW(descr
))
516 if (action
== ODA_FOCUS
)
518 if (!(descr
->UIState
& UISF_HIDEFOCUS
))
519 DrawFocusRect( hdc
, rect
);
522 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
526 /* some programs mess with the clipping region when
527 drawing the item, *and* restore the previous region
528 after they are done, so a region has better to exist
529 else everything ends clipped */
530 GetClientRect(descr
->self
, &r
);
531 hrgn
= set_control_clipping( hdc
, &r
);
533 dis
.CtlType
= ODT_LISTBOX
;
534 dis
.CtlID
= GetWindowLongPtrW( descr
->self
, GWLP_ID
);
535 dis
.hwndItem
= descr
->self
;
536 dis
.itemAction
= action
;
540 if (item
->selected
) dis
.itemState
|= ODS_SELECTED
;
541 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
543 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
544 if (!IsWindowEnabled(descr
->self
)) dis
.itemState
|= ODS_DISABLED
;
545 dis
.itemData
= item
->data
;
547 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
548 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
549 dis
.itemState
, wine_dbgstr_rect(rect
) );
550 SendMessageW(descr
->owner
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
551 SelectClipRgn( hdc
, hrgn
);
552 if (hrgn
) DeleteObject( hrgn
);
556 COLORREF oldText
= 0, oldBk
= 0;
558 if (action
== ODA_FOCUS
)
560 if (!(descr
->UIState
& UISF_HIDEFOCUS
)) // REACTOS
561 DrawFocusRect( hdc
, rect
);
564 if (item
&& item
->selected
)
566 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
567 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
570 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
571 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
572 wine_dbgstr_rect(rect
) );
574 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
575 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
576 else if (!(descr
->style
& LBS_USETABSTOPS
))
577 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
578 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
579 strlenW(item
->str
), NULL
);
582 /* Output empty string to paint background in the full width. */
583 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
584 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
585 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
586 item
->str
, strlenW(item
->str
),
587 descr
->nb_tabs
, descr
->tabs
, 0);
589 if (item
&& item
->selected
)
591 SetBkColor( hdc
, oldBk
);
592 SetTextColor( hdc
, oldText
);
594 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
597 !(descr
->UIState
& UISF_HIDEFOCUS
)) DrawFocusRect( hdc
, rect
);
602 /***********************************************************************
605 * Change the redraw flag.
607 static void LISTBOX_SetRedraw( LB_DESCR
*descr
, BOOL on
)
611 if (!(descr
->style
& LBS_NOREDRAW
)) return;
612 descr
->style
&= ~LBS_NOREDRAW
;
613 if (descr
->style
& LBS_DISPLAYCHANGED
)
614 { /* page was changed while setredraw false, refresh automatically */
615 InvalidateRect(descr
->self
, NULL
, TRUE
);
616 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
617 { /* reset top of page if less than number of items/page */
618 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
619 if (descr
->top_item
< 0) descr
->top_item
= 0;
621 descr
->style
&= ~LBS_DISPLAYCHANGED
;
623 LISTBOX_UpdateScroll( descr
);
625 else descr
->style
|= LBS_NOREDRAW
;
629 /***********************************************************************
630 * LISTBOX_RepaintItem
632 * Repaint a single item synchronously.
634 static void LISTBOX_RepaintItem( LB_DESCR
*descr
, INT index
, UINT action
)
639 HBRUSH hbrush
, oldBrush
= 0;
641 /* Do not repaint the item if the item is not visible */
642 if (!IsWindowVisible(descr
->self
)) return;
643 if (descr
->style
& LBS_NOREDRAW
)
645 descr
->style
|= LBS_DISPLAYCHANGED
;
648 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
649 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
650 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
651 hbrush
= GetControlColor( descr
->owner
, descr
->self
, hdc
, WM_CTLCOLORLISTBOX
);
652 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
653 if (!IsWindowEnabled(descr
->self
))
654 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
655 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
656 LISTBOX_PaintItem( descr
, hdc
, &rect
, index
, action
, TRUE
);
657 if (oldFont
) SelectObject( hdc
, oldFont
);
658 if (oldBrush
) SelectObject( hdc
, oldBrush
);
659 ReleaseDC( descr
->self
, hdc
);
663 /***********************************************************************
664 * LISTBOX_DrawFocusRect
666 static void LISTBOX_DrawFocusRect( LB_DESCR
*descr
, BOOL on
)
672 /* Do not repaint the item if the item is not visible */
673 if (!IsWindowVisible(descr
->self
)) return;
675 if (descr
->focus_item
== -1) return;
676 if (!descr
->caret_on
|| !descr
->in_focus
) return;
678 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) != 1) return;
679 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
680 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
681 if (!IsWindowEnabled(descr
->self
))
682 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
683 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
684 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, on
? FALSE
: TRUE
);
685 if (oldFont
) SelectObject( hdc
, oldFont
);
686 ReleaseDC( descr
->self
, hdc
);
690 /***********************************************************************
691 * LISTBOX_InitStorage
693 static LRESULT
LISTBOX_InitStorage( LB_DESCR
*descr
, INT nb_items
)
697 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
698 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
700 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
701 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
702 nb_items
* sizeof(LB_ITEMDATA
));
705 item
= HeapAlloc( GetProcessHeap(), 0,
706 nb_items
* sizeof(LB_ITEMDATA
));
711 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
719 /***********************************************************************
720 * LISTBOX_SetTabStops
722 static BOOL
LISTBOX_SetTabStops( LB_DESCR
*descr
, INT count
, LPINT tabs
)
726 if (!(descr
->style
& LBS_USETABSTOPS
))
728 SetLastError(ERROR_LB_WITHOUT_TABSTOPS
);
732 HeapFree( GetProcessHeap(), 0, descr
->tabs
);
733 if (!(descr
->nb_tabs
= count
))
738 if (!(descr
->tabs
= HeapAlloc( GetProcessHeap(), 0,
739 descr
->nb_tabs
* sizeof(INT
) )))
741 memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
743 /* convert into "dialog units"*/
744 for (i
= 0; i
< descr
->nb_tabs
; i
++)
745 descr
->tabs
[i
] = MulDiv(descr
->tabs
[i
], descr
->avg_char_width
, 4);
751 /***********************************************************************
754 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPWSTR buffer
, BOOL unicode
)
758 if ((index
< 0) || (index
>= descr
->nb_items
))
760 SetLastError(ERROR_INVALID_INDEX
);
763 if (HAS_STRINGS(descr
))
767 len
= strlenW(descr
->items
[index
].str
);
770 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[index
].str
, len
,
771 NULL
, 0, NULL
, NULL
);
774 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
776 _SEH2_TRY
/* hide a Delphi bug that passes a read-only buffer */
780 strcpyW( buffer
, descr
->items
[index
].str
);
781 len
= strlenW(buffer
);
785 len
= WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1,
786 (LPSTR
)buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
789 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
791 WARN( "got an invalid buffer (Delphi bug?)\n" );
792 SetLastError( ERROR_INVALID_PARAMETER
);
798 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
804 static inline INT
LISTBOX_lstrcmpiW( LCID lcid
, LPCWSTR str1
, LPCWSTR str2
)
806 INT ret
= CompareStringW( lcid
, NORM_IGNORECASE
, str1
, -1, str2
, -1 );
807 if (ret
== CSTR_LESS_THAN
)
809 if (ret
== CSTR_EQUAL
)
811 if (ret
== CSTR_GREATER_THAN
)
816 /***********************************************************************
817 * LISTBOX_FindStringPos
819 * Find the nearest string located before a given string in sort order.
820 * If 'exact' is TRUE, return an error if we don't get an exact match.
822 static INT
LISTBOX_FindStringPos( LB_DESCR
*descr
, LPCWSTR str
, BOOL exact
)
824 INT index
, min
, max
, res
= -1;
826 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
828 max
= descr
->nb_items
;
831 index
= (min
+ max
) / 2;
832 if (HAS_STRINGS(descr
))
833 res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, descr
->items
[index
].str
);
836 COMPAREITEMSTRUCT cis
;
837 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
839 cis
.CtlType
= ODT_LISTBOX
;
841 cis
.hwndItem
= descr
->self
;
842 /* note that some application (MetaStock) expects the second item
843 * to be in the listbox */
845 cis
.itemData1
= (ULONG_PTR
)str
;
847 cis
.itemData2
= descr
->items
[index
].data
;
848 cis
.dwLocaleId
= descr
->locale
;
849 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
851 if (!res
) return index
;
852 if (res
< 0) max
= index
;
853 else min
= index
+ 1;
855 return exact
? -1 : max
;
859 /***********************************************************************
860 * LISTBOX_FindFileStrPos
862 * Find the nearest string located before a given string in directory
863 * sort order (i.e. first files, then directories, then drives).
865 static INT
LISTBOX_FindFileStrPos( LB_DESCR
*descr
, LPCWSTR str
)
867 INT min
, max
, res
= -1;
869 if (!HAS_STRINGS(descr
))
870 return LISTBOX_FindStringPos( descr
, str
, FALSE
);
872 max
= descr
->nb_items
;
875 INT index
= (min
+ max
) / 2;
876 LPCWSTR p
= descr
->items
[index
].str
;
877 if (*p
== '[') /* drive or directory */
879 if (*str
!= '[') res
= -1;
880 else if (p
[1] == '-') /* drive */
882 if (str
[1] == '-') res
= str
[2] - p
[2];
887 if (str
[1] == '-') res
= 1;
888 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
893 if (*str
== '[') res
= 1;
894 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
896 if (!res
) return index
;
897 if (res
< 0) max
= index
;
898 else min
= index
+ 1;
904 /***********************************************************************
907 * Find the item beginning with a given string.
909 static INT
LISTBOX_FindString( LB_DESCR
*descr
, INT start
, LPCWSTR str
, BOOL exact
)
914 if (start
>= descr
->nb_items
) start
= -1;
915 item
= descr
->items
+ start
+ 1;
916 if (HAS_STRINGS(descr
))
918 if (!str
|| ! str
[0] ) return LB_ERR
;
921 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
922 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
923 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
924 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
928 /* Special case for drives and directories: ignore prefix */
929 #define CHECK_DRIVE(item) \
930 if ((item)->str[0] == '[') \
932 if (!strncmpiW( str, (item)->str+1, len )) return i; \
933 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
937 INT len
= strlenW(str
);
938 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
940 if (!strncmpiW( str
, item
->str
, len
)) return i
;
943 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
945 if (!strncmpiW( str
, item
->str
, len
)) return i
;
953 if (exact
&& (descr
->style
& LBS_SORT
))
954 /* If sorted, use a WM_COMPAREITEM binary search */
955 return LISTBOX_FindStringPos( descr
, str
, TRUE
);
957 /* Otherwise use a linear search */
958 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
959 if (item
->data
== (ULONG_PTR
)str
) return i
;
960 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
961 if (item
->data
== (ULONG_PTR
)str
) return i
;
967 /***********************************************************************
968 * LISTBOX_GetSelCount
970 static LRESULT
LISTBOX_GetSelCount( const LB_DESCR
*descr
)
973 const LB_ITEMDATA
*item
= descr
->items
;
975 if (!(descr
->style
& LBS_MULTIPLESEL
) ||
976 (descr
->style
& LBS_NOSEL
))
978 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
979 if (item
->selected
) count
++;
984 /***********************************************************************
985 * LISTBOX_GetSelItems
987 static LRESULT
LISTBOX_GetSelItems( const LB_DESCR
*descr
, INT max
, LPINT array
)
990 const LB_ITEMDATA
*item
= descr
->items
;
992 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
993 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
994 if (item
->selected
) array
[count
++] = i
;
999 /***********************************************************************
1002 static LRESULT
LISTBOX_Paint( LB_DESCR
*descr
, HDC hdc
)
1004 INT i
, col_pos
= descr
->page_size
- 1;
1006 RECT focusRect
= {-1, -1, -1, -1};
1008 HBRUSH hbrush
, oldBrush
= 0;
1010 if (descr
->style
& LBS_NOREDRAW
) return 0;
1012 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1013 if (descr
->style
& LBS_MULTICOLUMN
)
1014 rect
.right
= rect
.left
+ descr
->column_width
;
1015 else if (descr
->horz_pos
)
1017 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1018 rect
.right
+= descr
->horz_pos
;
1021 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1022 hbrush
= GetControlColor( descr
->owner
, descr
->self
, hdc
, WM_CTLCOLORLISTBOX
);
1023 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1024 if (!IsWindowEnabled(descr
->self
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1026 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1029 /* Special case for empty listbox: paint focus rect */
1030 rect
.bottom
= rect
.top
+ descr
->item_height
;
1031 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1032 &rect
, NULL
, 0, NULL
);
1033 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1034 rect
.top
= rect
.bottom
;
1037 /* Paint all the item, regarding the selection
1038 Focus state will be painted after */
1040 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1042 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1043 rect
.bottom
= rect
.top
+ descr
->item_height
;
1045 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1047 if (i
== descr
->focus_item
)
1049 /* keep the focus rect, to paint the focus item after */
1050 focusRect
.left
= rect
.left
;
1051 focusRect
.right
= rect
.right
;
1052 focusRect
.top
= rect
.top
;
1053 focusRect
.bottom
= rect
.bottom
;
1055 LISTBOX_PaintItem( descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1056 rect
.top
= rect
.bottom
;
1058 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1060 if (!IS_OWNERDRAW(descr
))
1062 /* Clear the bottom of the column */
1063 if (rect
.top
< descr
->height
)
1065 rect
.bottom
= descr
->height
;
1066 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1067 &rect
, NULL
, 0, NULL
);
1071 /* Go to the next column */
1072 rect
.left
+= descr
->column_width
;
1073 rect
.right
+= descr
->column_width
;
1075 col_pos
= descr
->page_size
- 1;
1080 if (rect
.top
>= descr
->height
) break;
1084 /* Paint the focus item now */
1085 if (focusRect
.top
!= focusRect
.bottom
&&
1086 descr
->caret_on
&& descr
->in_focus
)
1087 LISTBOX_PaintItem( descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1089 if (!IS_OWNERDRAW(descr
))
1091 /* Clear the remainder of the client area */
1092 if (rect
.top
< descr
->height
)
1094 rect
.bottom
= descr
->height
;
1095 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1096 &rect
, NULL
, 0, NULL
);
1098 if (rect
.right
< descr
->width
)
1100 rect
.left
= rect
.right
;
1101 rect
.right
= descr
->width
;
1103 rect
.bottom
= descr
->height
;
1104 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1105 &rect
, NULL
, 0, NULL
);
1108 if (oldFont
) SelectObject( hdc
, oldFont
);
1109 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1114 /***********************************************************************
1115 * LISTBOX_InvalidateItems
1117 * Invalidate all items from a given item. If the specified item is not
1118 * visible, nothing happens.
1120 static void LISTBOX_InvalidateItems( LB_DESCR
*descr
, INT index
)
1124 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1126 if (descr
->style
& LBS_NOREDRAW
)
1128 descr
->style
|= LBS_DISPLAYCHANGED
;
1131 rect
.bottom
= descr
->height
;
1132 InvalidateRect( descr
->self
, &rect
, TRUE
);
1133 if (descr
->style
& LBS_MULTICOLUMN
)
1135 /* Repaint the other columns */
1136 rect
.left
= rect
.right
;
1137 rect
.right
= descr
->width
;
1139 InvalidateRect( descr
->self
, &rect
, TRUE
);
1144 static void LISTBOX_InvalidateItemRect( LB_DESCR
*descr
, INT index
)
1148 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1149 InvalidateRect( descr
->self
, &rect
, TRUE
);
1152 /***********************************************************************
1153 * LISTBOX_GetItemHeight
1155 static LRESULT
LISTBOX_GetItemHeight( const LB_DESCR
*descr
, INT index
)
1157 if (descr
->style
& LBS_OWNERDRAWVARIABLE
&& descr
->nb_items
> 0)
1159 if ((index
< 0) || (index
>= descr
->nb_items
))
1161 SetLastError(ERROR_INVALID_INDEX
);
1164 return descr
->items
[index
].height
;
1166 else return descr
->item_height
;
1170 /***********************************************************************
1171 * LISTBOX_SetItemHeight
1173 static LRESULT
LISTBOX_SetItemHeight( LB_DESCR
*descr
, INT index
, INT height
, BOOL repaint
)
1175 if (height
> MAXBYTE
)
1178 if (!height
) height
= 1;
1180 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1182 if ((index
< 0) || (index
>= descr
->nb_items
))
1184 SetLastError(ERROR_INVALID_INDEX
);
1187 TRACE("[%p]: item %d height = %d\n", descr
->self
, index
, height
);
1188 descr
->items
[index
].height
= height
;
1189 LISTBOX_UpdateScroll( descr
);
1191 LISTBOX_InvalidateItems( descr
, index
);
1193 else if (height
!= descr
->item_height
)
1195 TRACE("[%p]: new height = %d\n", descr
->self
, height
);
1196 descr
->item_height
= height
;
1197 LISTBOX_UpdatePage( descr
);
1198 LISTBOX_UpdateScroll( descr
);
1200 InvalidateRect( descr
->self
, 0, TRUE
);
1206 /***********************************************************************
1207 * LISTBOX_SetHorizontalPos
1209 static void LISTBOX_SetHorizontalPos( LB_DESCR
*descr
, INT pos
)
1213 if (pos
> descr
->horz_extent
- descr
->width
)
1214 pos
= descr
->horz_extent
- descr
->width
;
1215 if (pos
< 0) pos
= 0;
1216 if (!(diff
= descr
->horz_pos
- pos
)) return;
1217 TRACE("[%p]: new horz pos = %d\n", descr
->self
, pos
);
1218 descr
->horz_pos
= pos
;
1219 LISTBOX_UpdateScroll( descr
);
1220 if (abs(diff
) < descr
->width
)
1223 /* Invalidate the focused item so it will be repainted correctly */
1224 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1225 InvalidateRect( descr
->self
, &rect
, TRUE
);
1226 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
1227 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1230 InvalidateRect( descr
->self
, NULL
, TRUE
);
1234 /***********************************************************************
1235 * LISTBOX_SetHorizontalExtent
1237 static LRESULT
LISTBOX_SetHorizontalExtent( LB_DESCR
*descr
, INT extent
)
1239 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1241 if (extent
<= 0) extent
= 1;
1242 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1243 TRACE("[%p]: new horz extent = %d\n", descr
->self
, extent
);
1244 descr
->horz_extent
= extent
;
1245 if (descr
->horz_pos
> extent
- descr
->width
)
1246 LISTBOX_SetHorizontalPos( descr
, extent
- descr
->width
);
1248 LISTBOX_UpdateScroll( descr
);
1253 /***********************************************************************
1254 * LISTBOX_SetColumnWidth
1256 static LRESULT
LISTBOX_SetColumnWidth( LB_DESCR
*descr
, INT width
)
1258 if (width
== descr
->column_width
) return LB_OKAY
;
1259 TRACE("[%p]: new column width = %d\n", descr
->self
, width
);
1260 descr
->column_width
= width
;
1261 LISTBOX_UpdatePage( descr
);
1266 /***********************************************************************
1269 * Returns the item height.
1271 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1275 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1280 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1282 ERR("unable to get DC.\n" );
1285 if (font
) oldFont
= SelectObject( hdc
, font
);
1286 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1287 if (oldFont
) SelectObject( hdc
, oldFont
);
1288 ReleaseDC( descr
->self
, hdc
);
1290 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1291 if (!IS_OWNERDRAW(descr
))
1292 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1297 /***********************************************************************
1298 * LISTBOX_MakeItemVisible
1300 * Make sure that a given item is partially or fully visible.
1302 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1306 TRACE("current top item %d, index %d, fully %d\n", descr
->top_item
, index
, fully
);
1308 if (index
<= descr
->top_item
) top
= index
;
1309 else if (descr
->style
& LBS_MULTICOLUMN
)
1311 INT cols
= descr
->width
;
1312 if (!fully
) cols
+= descr
->column_width
- 1;
1313 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1315 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1316 top
= index
- descr
->page_size
* (cols
- 1);
1318 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1320 INT height
= fully
? descr
->items
[index
].height
: 1;
1321 for (top
= index
; top
> descr
->top_item
; top
--)
1322 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1326 if (index
< descr
->top_item
+ descr
->page_size
) return;
1327 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1328 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1329 top
= index
- descr
->page_size
+ 1;
1331 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1334 /***********************************************************************
1335 * LISTBOX_SetCaretIndex
1338 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1341 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1343 INT oldfocus
= descr
->focus_item
;
1345 TRACE("old focus %d, index %d\n", oldfocus
, index
);
1347 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1348 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1349 if (index
== oldfocus
) return LB_OKAY
;
1351 LISTBOX_DrawFocusRect( descr
, FALSE
);
1352 descr
->focus_item
= index
;
1354 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1355 LISTBOX_DrawFocusRect( descr
, TRUE
);
1361 /***********************************************************************
1362 * LISTBOX_SelectItemRange
1364 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1366 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1371 /* A few sanity checks */
1373 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1374 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1376 if (!descr
->nb_items
) return LB_OKAY
;
1378 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1379 if (first
< 0) first
= 0;
1380 if (last
< first
) return LB_OKAY
;
1382 if (on
) /* Turn selection on */
1384 for (i
= first
; i
<= last
; i
++)
1386 if (descr
->items
[i
].selected
) continue;
1387 descr
->items
[i
].selected
= TRUE
;
1388 LISTBOX_InvalidateItemRect(descr
, i
);
1391 else /* Turn selection off */
1393 for (i
= first
; i
<= last
; i
++)
1395 if (!descr
->items
[i
].selected
) continue;
1396 descr
->items
[i
].selected
= FALSE
;
1397 LISTBOX_InvalidateItemRect(descr
, i
);
1403 /***********************************************************************
1404 * LISTBOX_SetSelection
1406 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1407 BOOL on
, BOOL send_notify
)
1409 TRACE( "cur_sel=%d index=%d notify=%s\n",
1410 descr
->selected_item
, index
, send_notify
? "YES" : "NO" );
1412 if (descr
->style
& LBS_NOSEL
)
1414 descr
->selected_item
= index
;
1417 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1418 if (descr
->style
& LBS_MULTIPLESEL
)
1420 if (index
== -1) /* Select all items */
1421 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1422 else /* Only one item */
1423 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1427 INT oldsel
= descr
->selected_item
;
1428 if (index
== oldsel
) return LB_OKAY
;
1429 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1430 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1431 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1432 descr
->selected_item
= index
;
1433 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1434 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1435 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1437 if( descr
->lphc
) /* set selection change flag for parent combo */
1438 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1444 /***********************************************************************
1447 * Change the caret position and extend the selection to the new caret.
1449 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1451 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1453 if ((index
< 0) || (index
>= descr
->nb_items
))
1456 /* Important, repaint needs to be done in this order if
1457 you want to mimic Windows behavior:
1458 1. Remove the focus and paint the item
1459 2. Remove the selection and paint the item(s)
1460 3. Set the selection and repaint the item(s)
1461 4. Set the focus to 'index' and repaint the item */
1463 /* 1. remove the focus and repaint the item */
1464 LISTBOX_DrawFocusRect( descr
, FALSE
);
1466 /* 2. then turn off the previous selection */
1467 /* 3. repaint the new selected item */
1468 if (descr
->style
& LBS_EXTENDEDSEL
)
1470 if (descr
->anchor_item
!= -1)
1472 INT first
= min( index
, descr
->anchor_item
);
1473 INT last
= max( index
, descr
->anchor_item
);
1475 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1476 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1477 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1480 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1482 /* Set selection to new caret item */
1483 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1486 /* 4. repaint the new item with the focus */
1487 descr
->focus_item
= index
;
1488 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1489 LISTBOX_DrawFocusRect( descr
, TRUE
);
1493 /***********************************************************************
1494 * LISTBOX_InsertItem
1496 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1497 LPWSTR str
, ULONG_PTR data
)
1501 INT oldfocus
= descr
->focus_item
;
1503 if (index
== -1) index
= descr
->nb_items
;
1504 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1505 if (!descr
->items
) max_items
= 0;
1506 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1507 if (descr
->nb_items
== max_items
)
1509 /* We need to grow the array */
1510 max_items
+= LB_ARRAY_GRANULARITY
;
1512 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1513 max_items
* sizeof(LB_ITEMDATA
) );
1515 item
= HeapAlloc( GetProcessHeap(), 0,
1516 max_items
* sizeof(LB_ITEMDATA
) );
1519 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1522 descr
->items
= item
;
1525 /* Insert the item structure */
1527 item
= &descr
->items
[index
];
1528 if (index
< descr
->nb_items
)
1529 RtlMoveMemory( item
+ 1, item
,
1530 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1534 item
->selected
= FALSE
;
1537 /* Get item height */
1539 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1541 MEASUREITEMSTRUCT mis
;
1542 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1544 mis
.CtlType
= ODT_LISTBOX
;
1547 mis
.itemData
= descr
->items
[index
].data
;
1548 mis
.itemHeight
= descr
->item_height
;
1549 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1550 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1551 TRACE("[%p]: measure item %d (%s) = %d\n",
1552 descr
->self
, index
, str
? debugstr_w(str
) : "", item
->height
);
1555 /* Repaint the items */
1557 LISTBOX_UpdateScroll( descr
);
1558 LISTBOX_InvalidateItems( descr
, index
);
1560 /* Move selection and focused item */
1561 /* If listbox was empty, set focus to the first item */
1562 if (descr
->nb_items
== 1)
1563 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1564 /* single select don't change selection index in win31 */
1565 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1567 descr
->selected_item
++;
1568 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1572 if (index
<= descr
->selected_item
)
1574 descr
->selected_item
++;
1575 descr
->focus_item
= oldfocus
; /* focus not changed */
1582 /***********************************************************************
1583 * LISTBOX_InsertString
1585 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
, LPCWSTR str
)
1587 LPWSTR new_str
= NULL
;
1591 if (HAS_STRINGS(descr
))
1593 static const WCHAR empty_stringW
[] = { 0 };
1594 if (!str
) str
= empty_stringW
;
1595 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1597 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1600 strcpyW(new_str
, str
);
1602 else data
= (ULONG_PTR
)str
;
1604 if (index
== -1) index
= descr
->nb_items
;
1605 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, data
)) != 0)
1607 HeapFree( GetProcessHeap(), 0, new_str
);
1611 TRACE("[%p]: added item %d %s\n",
1612 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1617 /***********************************************************************
1618 * LISTBOX_DeleteItem
1620 * Delete the content of an item. 'index' must be a valid index.
1622 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1624 /* save the item data before it gets freed by LB_RESETCONTENT */
1625 ULONG_PTR item_data
= descr
->items
[index
].data
;
1626 LPWSTR item_str
= descr
->items
[index
].str
;
1628 if (!descr
->nb_items
)
1629 SendMessageW( descr
->self
, LB_RESETCONTENT
, 0, 0 );
1631 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1632 * while Win95 sends it for all items with user data.
1633 * It's probably better to send it too often than not
1634 * often enough, so this is what we do here.
1636 if (IS_OWNERDRAW(descr
) || item_data
)
1638 DELETEITEMSTRUCT dis
;
1639 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1641 dis
.CtlType
= ODT_LISTBOX
;
1644 dis
.hwndItem
= descr
->self
;
1645 dis
.itemData
= item_data
;
1646 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1648 if (HAS_STRINGS(descr
))
1649 HeapFree( GetProcessHeap(), 0, item_str
);
1653 /***********************************************************************
1654 * LISTBOX_RemoveItem
1656 * Remove an item from the listbox and delete its content.
1658 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1663 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1665 /* We need to invalidate the original rect instead of the updated one. */
1666 LISTBOX_InvalidateItems( descr
, index
);
1669 LISTBOX_DeleteItem( descr
, index
);
1671 if (!descr
->nb_items
) return LB_OKAY
;
1673 /* Remove the item */
1675 item
= &descr
->items
[index
];
1676 if (index
< descr
->nb_items
)
1677 RtlMoveMemory( item
, item
+ 1,
1678 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1679 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1681 /* Shrink the item array if possible */
1683 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1684 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1686 max_items
-= LB_ARRAY_GRANULARITY
;
1687 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1688 max_items
* sizeof(LB_ITEMDATA
) );
1689 if (item
) descr
->items
= item
;
1691 /* Repaint the items */
1693 LISTBOX_UpdateScroll( descr
);
1694 /* if we removed the scrollbar, reset the top of the list
1695 (correct for owner-drawn ???) */
1696 if (descr
->nb_items
== descr
->page_size
)
1697 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1699 /* Move selection and focused item */
1700 if (!IS_MULTISELECT(descr
))
1702 if (index
== descr
->selected_item
)
1703 descr
->selected_item
= -1;
1704 else if (index
< descr
->selected_item
)
1706 descr
->selected_item
--;
1707 if (ISWIN31
) /* win 31 do not change the selected item number */
1708 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1712 if (descr
->focus_item
>= descr
->nb_items
)
1714 descr
->focus_item
= descr
->nb_items
- 1;
1715 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1721 /***********************************************************************
1722 * LISTBOX_ResetContent
1724 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1728 for(i
= descr
->nb_items
- 1; i
>=0; i
--) LISTBOX_DeleteItem( descr
, i
);
1729 HeapFree( GetProcessHeap(), 0, descr
->items
);
1730 descr
->nb_items
= 0;
1731 descr
->top_item
= 0;
1732 descr
->selected_item
= -1;
1733 descr
->focus_item
= 0;
1734 descr
->anchor_item
= -1;
1735 descr
->items
= NULL
;
1739 /***********************************************************************
1742 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, INT count
)
1746 if (HAS_STRINGS(descr
))
1748 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1752 /* FIXME: this is far from optimal... */
1753 if (count
> descr
->nb_items
)
1755 while (count
> descr
->nb_items
)
1756 if ((ret
= LISTBOX_InsertString( descr
, -1, 0 )) < 0)
1759 else if (count
< descr
->nb_items
)
1761 while (count
< descr
->nb_items
)
1762 if ((ret
= LISTBOX_RemoveItem( descr
, (descr
->nb_items
- 1) )) < 0)
1766 InvalidateRect( descr
->self
, NULL
, TRUE
);
1771 /***********************************************************************
1774 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1775 LPCWSTR filespec
, BOOL long_names
)
1778 LRESULT ret
= LB_OKAY
;
1779 WIN32_FIND_DATAW entry
;
1781 LRESULT maxinsert
= LB_ERR
;
1783 /* don't scan directory if we just want drives exclusively */
1784 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1785 /* scan directory */
1786 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1788 int le
= GetLastError();
1789 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1796 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1798 static const WCHAR bracketW
[] = { ']',0 };
1799 static const WCHAR dotW
[] = { '.',0 };
1800 if (!(attrib
& DDL_DIRECTORY
) ||
1801 !strcmpW( entry
.cFileName
, dotW
)) continue;
1803 if (!long_names
&& entry
.cAlternateFileName
[0])
1804 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1806 strcpyW( buffer
+ 1, entry
.cFileName
);
1807 strcatW(buffer
, bracketW
);
1809 else /* not a directory */
1811 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1812 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1814 if ((attrib
& DDL_EXCLUSIVE
) &&
1815 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1818 if (!long_names
&& entry
.cAlternateFileName
[0])
1819 strcpyW( buffer
, entry
.cAlternateFileName
);
1821 strcpyW( buffer
, entry
.cFileName
);
1823 if (!long_names
) CharLowerW( buffer
);
1824 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1825 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1827 if (ret
<= maxinsert
) maxinsert
++; else maxinsert
= ret
;
1828 } while (FindNextFileW( handle
, &entry
));
1829 FindClose( handle
);
1837 if (attrib
& DDL_DRIVES
)
1839 WCHAR buffer
[] = {'[','-','a','-',']',0};
1840 WCHAR root
[] = {'A',':','\\',0};
1842 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1844 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1845 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1854 /***********************************************************************
1855 * LISTBOX_HandleVScroll
1857 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1861 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1865 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1868 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1871 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1872 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1875 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1876 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1878 case SB_THUMBPOSITION
:
1879 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1882 info
.cbSize
= sizeof(info
);
1883 info
.fMask
= SIF_TRACKPOS
;
1884 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1885 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1888 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1891 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1898 /***********************************************************************
1899 * LISTBOX_HandleHScroll
1901 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1906 if (descr
->style
& LBS_MULTICOLUMN
)
1911 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
1915 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
1919 page
= descr
->width
/ descr
->column_width
;
1920 if (page
< 1) page
= 1;
1921 LISTBOX_SetTopItem( descr
,
1922 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1925 page
= descr
->width
/ descr
->column_width
;
1926 if (page
< 1) page
= 1;
1927 LISTBOX_SetTopItem( descr
,
1928 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1930 case SB_THUMBPOSITION
:
1931 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
1934 info
.cbSize
= sizeof(info
);
1935 info
.fMask
= SIF_TRACKPOS
;
1936 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1937 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
1941 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1944 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1948 else if (descr
->horz_extent
)
1953 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
1956 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
1959 LISTBOX_SetHorizontalPos( descr
,
1960 descr
->horz_pos
- descr
->width
);
1963 LISTBOX_SetHorizontalPos( descr
,
1964 descr
->horz_pos
+ descr
->width
);
1966 case SB_THUMBPOSITION
:
1967 LISTBOX_SetHorizontalPos( descr
, pos
);
1970 info
.cbSize
= sizeof(info
);
1971 info
.fMask
= SIF_TRACKPOS
;
1972 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
1973 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
1976 LISTBOX_SetHorizontalPos( descr
, 0 );
1979 LISTBOX_SetHorizontalPos( descr
,
1980 descr
->horz_extent
- descr
->width
);
1987 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
1989 UINT pulScrollLines
= 3;
1991 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1993 /* if scrolling changes direction, ignore left overs */
1994 if ((delta
< 0 && descr
->wheel_remain
< 0) ||
1995 (delta
> 0 && descr
->wheel_remain
> 0))
1996 descr
->wheel_remain
+= delta
;
1998 descr
->wheel_remain
= delta
;
2000 if (descr
->wheel_remain
&& pulScrollLines
)
2003 pulScrollLines
= min((UINT
) descr
->page_size
, pulScrollLines
);
2004 cLineScroll
= pulScrollLines
* (float)descr
->wheel_remain
/ WHEEL_DELTA
;
2005 descr
->wheel_remain
-= WHEEL_DELTA
* cLineScroll
/ (int)pulScrollLines
;
2006 LISTBOX_SetTopItem( descr
, descr
->top_item
- cLineScroll
, TRUE
);
2011 /***********************************************************************
2012 * LISTBOX_HandleLButtonDown
2014 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
2016 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2018 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2019 descr
->self
, x
, y
, index
, descr
->focus_item
);
2021 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2023 if (!descr
->in_focus
)
2025 if( !descr
->lphc
) SetFocus( descr
->self
);
2026 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2029 if (index
== -1) return 0;
2033 if (descr
->style
& LBS_NOTIFY
)
2034 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2035 MAKELPARAM( x
, y
) );
2038 descr
->captured
= TRUE
;
2039 SetCapture( descr
->self
);
2041 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2043 /* we should perhaps make sure that all items are deselected
2044 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2045 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2046 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2049 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2050 if (keys
& MK_CONTROL
)
2052 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2053 LISTBOX_SetSelection( descr
, index
,
2054 !descr
->items
[index
].selected
,
2055 (descr
->style
& LBS_NOTIFY
) != 0);
2059 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2061 if (descr
->style
& LBS_EXTENDEDSEL
)
2063 LISTBOX_SetSelection( descr
, index
,
2064 descr
->items
[index
].selected
,
2065 (descr
->style
& LBS_NOTIFY
) != 0 );
2069 LISTBOX_SetSelection( descr
, index
,
2070 !descr
->items
[index
].selected
,
2071 (descr
->style
& LBS_NOTIFY
) != 0 );
2077 descr
->anchor_item
= index
;
2078 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2079 LISTBOX_SetSelection( descr
, index
,
2080 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2084 { // See rev 40864 use Ptr for 64 bit.
2085 if (GetWindowLongPtrW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2092 if (DragDetect( descr
->self
, pt
))
2093 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2100 /*************************************************************************
2101 * LISTBOX_HandleLButtonDownCombo [Internal]
2103 * Process LButtonDown message for the ComboListBox
2106 * pWnd [I] The windows internal structure
2107 * pDescr [I] The ListBox internal structure
2108 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2109 * x [I] X Mouse Coordinate
2110 * y [I] Y Mouse Coordinate
2113 * 0 since we are processing the WM_LBUTTONDOWN Message
2116 * This function is only to be used when a ListBox is a ComboListBox
2119 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2121 RECT clientRect
, screenRect
;
2127 GetClientRect(descr
->self
, &clientRect
);
2129 if(PtInRect(&clientRect
, mousePos
))
2131 /* MousePos is in client, resume normal processing */
2132 if (msg
== WM_LBUTTONDOWN
)
2134 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2135 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2137 else if (descr
->style
& LBS_NOTIFY
)
2138 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2142 POINT screenMousePos
;
2143 HWND hWndOldCapture
;
2145 /* Check the Non-Client Area */
2146 screenMousePos
= mousePos
;
2147 hWndOldCapture
= GetCapture();
2149 GetWindowRect(descr
->self
, &screenRect
);
2150 ClientToScreen(descr
->self
, &screenMousePos
);
2152 if(!PtInRect(&screenRect
, screenMousePos
))
2154 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2155 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2156 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2160 /* Check to see the NC is a scrollbar */
2162 LONG style
= GetWindowLongPtrW( descr
->self
, GWL_STYLE
);
2163 /* Check Vertical scroll bar */
2164 if (style
& WS_VSCROLL
)
2166 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2167 if (PtInRect( &clientRect
, mousePos
))
2168 nHitTestType
= HTVSCROLL
;
2170 /* Check horizontal scroll bar */
2171 if (style
& WS_HSCROLL
)
2173 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2174 if (PtInRect( &clientRect
, mousePos
))
2175 nHitTestType
= HTHSCROLL
;
2177 /* Windows sends this message when a scrollbar is clicked
2180 if(nHitTestType
!= 0)
2182 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2183 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2185 /* Resume the Capture after scrolling is complete
2187 if(hWndOldCapture
!= 0)
2188 SetCapture(hWndOldCapture
);
2194 /***********************************************************************
2195 * LISTBOX_HandleLButtonUp
2197 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2199 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2200 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2201 LISTBOX_Timer
= LB_TIMER_NONE
;
2202 if (descr
->captured
)
2204 descr
->captured
= FALSE
;
2205 if (GetCapture() == descr
->self
) ReleaseCapture();
2206 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2207 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2213 /***********************************************************************
2214 * LISTBOX_HandleTimer
2216 * Handle scrolling upon a timer event.
2217 * Return TRUE if scrolling should continue.
2219 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2224 if (descr
->top_item
) index
= descr
->top_item
- 1;
2228 if (descr
->top_item
) index
-= descr
->page_size
;
2231 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2232 if (index
== descr
->focus_item
) index
++;
2233 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2235 case LB_TIMER_RIGHT
:
2236 if (index
+ descr
->page_size
< descr
->nb_items
)
2237 index
+= descr
->page_size
;
2242 if (index
== descr
->focus_item
) return FALSE
;
2243 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2248 /***********************************************************************
2249 * LISTBOX_HandleSystemTimer
2251 * WM_SYSTIMER handler.
2253 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2255 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2257 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2258 LISTBOX_Timer
= LB_TIMER_NONE
;
2264 /***********************************************************************
2265 * LISTBOX_HandleMouseMove
2267 * WM_MOUSEMOVE handler.
2269 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2273 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2275 if (!descr
->captured
) return;
2277 if (descr
->style
& LBS_MULTICOLUMN
)
2280 else if (y
>= descr
->item_height
* descr
->page_size
)
2281 y
= descr
->item_height
* descr
->page_size
- 1;
2285 dir
= LB_TIMER_LEFT
;
2288 else if (x
>= descr
->width
)
2290 dir
= LB_TIMER_RIGHT
;
2291 x
= descr
->width
- 1;
2296 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2297 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2300 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2301 if (index
== -1) index
= descr
->focus_item
;
2302 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2304 /* Start/stop the system timer */
2306 if (dir
!= LB_TIMER_NONE
)
2307 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2308 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2309 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2310 LISTBOX_Timer
= dir
;
2314 /***********************************************************************
2315 * LISTBOX_HandleKeyDown
2317 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2320 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2321 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2322 bForceSelection
= FALSE
; /* only for single select list */
2324 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2326 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2327 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2328 (LPARAM
)descr
->self
);
2329 if (caret
== -2) return 0;
2331 if (caret
== -1) switch(key
)
2334 if (descr
->style
& LBS_MULTICOLUMN
)
2336 bForceSelection
= FALSE
;
2337 if (descr
->focus_item
>= descr
->page_size
)
2338 caret
= descr
->focus_item
- descr
->page_size
;
2343 caret
= descr
->focus_item
- 1;
2344 if (caret
< 0) caret
= 0;
2347 if (descr
->style
& LBS_MULTICOLUMN
)
2349 bForceSelection
= FALSE
;
2350 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2351 caret
= descr
->focus_item
+ descr
->page_size
;
2356 caret
= descr
->focus_item
+ 1;
2357 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2361 if (descr
->style
& LBS_MULTICOLUMN
)
2363 INT page
= descr
->width
/ descr
->column_width
;
2364 if (page
< 1) page
= 1;
2365 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2367 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2368 if (caret
< 0) caret
= 0;
2371 if (descr
->style
& LBS_MULTICOLUMN
)
2373 INT page
= descr
->width
/ descr
->column_width
;
2374 if (page
< 1) page
= 1;
2375 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2377 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2378 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2384 caret
= descr
->nb_items
- 1;
2387 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2388 else if (descr
->style
& LBS_MULTIPLESEL
)
2390 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2391 !descr
->items
[descr
->focus_item
].selected
,
2392 (descr
->style
& LBS_NOTIFY
) != 0 );
2396 bForceSelection
= FALSE
;
2398 if (bForceSelection
) /* focused item is used instead of key */
2399 caret
= descr
->focus_item
;
2402 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2403 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2404 !IS_MULTISELECT(descr
))
2405 descr
->anchor_item
= caret
;
2406 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2408 if (descr
->style
& LBS_MULTIPLESEL
)
2409 descr
->selected_item
= caret
;
2411 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2412 if (descr
->style
& LBS_NOTIFY
)
2414 if (descr
->lphc
&& IsWindowVisible( descr
->self
))
2416 /* make sure that combo parent doesn't hide us */
2417 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2419 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2426 /***********************************************************************
2427 * LISTBOX_HandleChar
2429 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2437 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2439 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2440 MAKEWPARAM(charW
, descr
->focus_item
),
2441 (LPARAM
)descr
->self
);
2442 if (caret
== -2) return 0;
2445 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2448 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2449 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2450 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2451 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2452 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2457 /* ReactOS Retrieve the UI state for the control */
2458 static BOOL
LISTBOX_update_uistate(LB_DESCR
*descr
)
2462 prev_flags
= descr
->UIState
;
2463 descr
->UIState
= DefWindowProcW(descr
->self
, WM_QUERYUISTATE
, 0, 0);
2464 return prev_flags
!= descr
->UIState
;
2468 /***********************************************************************
2471 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2474 MEASUREITEMSTRUCT mis
;
2477 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2480 GetClientRect( hwnd
, &rect
);
2482 descr
->owner
= GetParent( descr
->self
);
2483 descr
->style
= GetWindowLongPtrW( descr
->self
, GWL_STYLE
);
2484 descr
->width
= rect
.right
- rect
.left
;
2485 descr
->height
= rect
.bottom
- rect
.top
;
2486 descr
->items
= NULL
;
2487 descr
->nb_items
= 0;
2488 descr
->top_item
= 0;
2489 descr
->selected_item
= -1;
2490 descr
->focus_item
= 0;
2491 descr
->anchor_item
= -1;
2492 descr
->item_height
= 1;
2493 descr
->page_size
= 1;
2494 descr
->column_width
= 150;
2495 descr
->horz_extent
= (descr
->style
& WS_HSCROLL
) ? 1 : 0;
2496 descr
->horz_pos
= 0;
2499 descr
->wheel_remain
= 0;
2500 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2501 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2502 descr
->in_focus
= FALSE
;
2503 descr
->captured
= FALSE
;
2505 descr
->locale
= GetUserDefaultLCID();
2510 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2511 descr
->owner
= lphc
->self
;
2514 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2516 LISTBOX_update_uistate(descr
); // ReactOS
2518 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2520 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2521 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2522 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2525 /* A no-data list box must also have the LBS_OWNERDRAWFIXED style, but must
2526 not have the LBS_SORT or LBS_HASSTRINGS style. */
2527 if ( descr
->style
& LBS_NODATA
&&
2528 (!(descr
->style
& LBS_OWNERDRAWFIXED
) || descr
->style
& (LBS_HASSTRINGS
|LBS_SORT
) ) )
2529 descr
->style
&= ~LBS_NODATA
;
2531 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2533 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2535 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2537 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2538 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2542 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2543 mis
.CtlType
= ODT_LISTBOX
;
2548 mis
.itemHeight
= descr
->item_height
;
2549 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2550 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2554 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2559 /***********************************************************************
2562 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2564 LISTBOX_ResetContent( descr
);
2565 SetWindowLongPtrW( descr
->self
, 0, 0 );
2566 HeapFree( GetProcessHeap(), 0, descr
);
2571 /***********************************************************************
2572 * ListBoxWndProc_common
2574 LRESULT WINAPI
ListBoxWndProc_common( HWND hwnd
, UINT msg
,
2575 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2577 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2578 LPHEADCOMBO lphc
= 0;
2583 pWnd
= ValidateHwnd(hwnd
);
2588 NtUserSetWindowFNID(hwnd
, FNID_LISTBOX
); // Could be FNID_COMBOLBOX by class.
2592 if (pWnd
->fnid
!= FNID_LISTBOX
)
2594 ERR("Wrong window class for listbox! fnId 0x%x\n",pWnd
->fnid
);
2603 if (!IsWindow(hwnd
)) return 0;
2605 if (msg
== WM_CREATE
)
2607 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2608 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= lpcs
->lpCreateParams
;
2609 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2610 TRACE("creating hwnd %p descr %p\n", hwnd
, (void *)GetWindowLongPtrW( hwnd
, 0 ) );
2613 /* Ignore all other messages before we get a WM_CREATE */
2614 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2615 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2617 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2619 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2620 descr
->self
, SPY_GetMsgName(msg
, descr
->self
), wParam
, lParam
);
2624 case LB_RESETCONTENT
:
2625 LISTBOX_ResetContent( descr
);
2626 LISTBOX_UpdateScroll( descr
);
2627 InvalidateRect( descr
->self
, NULL
, TRUE
);
2631 case LB_ADDSTRING_LOWER
:
2632 case LB_ADDSTRING_UPPER
:
2636 if(unicode
|| !HAS_STRINGS(descr
))
2637 textW
= (LPWSTR
)lParam
;
2640 LPSTR textA
= (LPSTR
)lParam
;
2641 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2642 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2643 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2647 /* in the unicode the version, the string is really overwritten
2648 during the converting case */
2649 if (msg
== LB_ADDSTRING_LOWER
)
2651 else if (msg
== LB_ADDSTRING_UPPER
)
2653 wParam
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2654 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2655 if (!unicode
&& HAS_STRINGS(descr
))
2656 HeapFree(GetProcessHeap(), 0, textW
);
2660 case LB_INSERTSTRING
:
2661 case LB_INSERTSTRING_UPPER
:
2662 case LB_INSERTSTRING_LOWER
:
2666 if(unicode
|| !HAS_STRINGS(descr
))
2667 textW
= (LPWSTR
)lParam
;
2670 LPSTR textA
= (LPSTR
)lParam
;
2671 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2672 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2673 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2677 /* in the unicode the version, the string is really overwritten
2678 during the converting case */
2679 if (msg
== LB_INSERTSTRING_LOWER
)
2681 else if (msg
== LB_INSERTSTRING_UPPER
)
2683 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2684 if(!unicode
&& HAS_STRINGS(descr
))
2685 HeapFree(GetProcessHeap(), 0, textW
);
2693 if(unicode
|| !HAS_STRINGS(descr
))
2694 textW
= (LPWSTR
)lParam
;
2697 LPSTR textA
= (LPSTR
)lParam
;
2698 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2699 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2700 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2704 wParam
= LISTBOX_FindFileStrPos( descr
, textW
);
2705 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2706 if(!unicode
&& HAS_STRINGS(descr
))
2707 HeapFree(GetProcessHeap(), 0, textW
);
2711 case LB_DELETESTRING
:
2712 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2713 return descr
->nb_items
;
2716 SetLastError(ERROR_INVALID_INDEX
);
2720 case LB_GETITEMDATA
:
2721 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2723 SetLastError(ERROR_INVALID_INDEX
);
2726 return descr
->items
[wParam
].data
;
2728 case LB_SETITEMDATA
:
2729 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2731 SetLastError(ERROR_INVALID_INDEX
);
2734 descr
->items
[wParam
].data
= lParam
;
2735 /* undocumented: returns TRUE, not LB_OKAY (0) */
2739 return descr
->nb_items
;
2742 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, unicode
);
2745 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2747 SetLastError(ERROR_INVALID_INDEX
);
2750 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2751 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2752 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2753 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2756 if (descr
->nb_items
== 0)
2758 if (!IS_MULTISELECT(descr
))
2759 return descr
->selected_item
;
2760 if (descr
->selected_item
!= -1)
2761 return descr
->selected_item
;
2762 return descr
->focus_item
;
2763 /* otherwise, if the user tries to move the selection with the */
2764 /* arrow keys, we will give the application something to choke on */
2765 case LB_GETTOPINDEX
:
2766 return descr
->top_item
;
2768 case LB_GETITEMHEIGHT
:
2769 return LISTBOX_GetItemHeight( descr
, wParam
);
2771 case LB_SETITEMHEIGHT
:
2772 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2774 case LB_ITEMFROMPOINT
:
2781 /* The hiword of the return value is not a client area
2782 hittest as suggested by MSDN, but rather a hittest on
2783 the returned listbox item. */
2785 if(descr
->nb_items
== 0)
2786 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2788 pt
.x
= (short)LOWORD(lParam
);
2789 pt
.y
= (short)HIWORD(lParam
);
2791 SetRect(&rect
, 0, 0, descr
->width
, descr
->height
);
2793 if(!PtInRect(&rect
, pt
))
2795 pt
.x
= min(pt
.x
, rect
.right
- 1);
2796 pt
.x
= max(pt
.x
, 0);
2797 pt
.y
= min(pt
.y
, rect
.bottom
- 1);
2798 pt
.y
= max(pt
.y
, 0);
2802 index
= LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
);
2806 index
= descr
->nb_items
- 1;
2809 return MAKELONG(index
, hit
? 0 : 1);
2812 case LB_SETCARETINDEX
:
2813 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2814 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2821 case LB_GETCARETINDEX
:
2822 return descr
->focus_item
;
2824 case LB_SETTOPINDEX
:
2825 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2827 case LB_SETCOLUMNWIDTH
:
2828 return LISTBOX_SetColumnWidth( descr
, wParam
);
2830 case LB_GETITEMRECT
:
2831 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2837 if(unicode
|| !HAS_STRINGS(descr
))
2838 textW
= (LPWSTR
)lParam
;
2841 LPSTR textA
= (LPSTR
)lParam
;
2842 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2843 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2844 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2846 ret
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2847 if(!unicode
&& HAS_STRINGS(descr
))
2848 HeapFree(GetProcessHeap(), 0, textW
);
2852 case LB_FINDSTRINGEXACT
:
2856 if(unicode
|| !HAS_STRINGS(descr
))
2857 textW
= (LPWSTR
)lParam
;
2860 LPSTR textA
= (LPSTR
)lParam
;
2861 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2862 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2863 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2865 ret
= LISTBOX_FindString( descr
, wParam
, textW
, TRUE
);
2866 if(!unicode
&& HAS_STRINGS(descr
))
2867 HeapFree(GetProcessHeap(), 0, textW
);
2871 case LB_SELECTSTRING
:
2876 if(HAS_STRINGS(descr
))
2877 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2878 debugstr_a((LPSTR
)lParam
));
2879 if(unicode
|| !HAS_STRINGS(descr
))
2880 textW
= (LPWSTR
)lParam
;
2883 LPSTR textA
= (LPSTR
)lParam
;
2884 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2885 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2886 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2888 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2889 if(!unicode
&& HAS_STRINGS(descr
))
2890 HeapFree(GetProcessHeap(), 0, textW
);
2891 if (index
!= LB_ERR
)
2893 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2894 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2900 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2902 return descr
->items
[wParam
].selected
;
2905 return LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2908 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2909 LISTBOX_SetCaretIndex( descr
, wParam
, FALSE
);
2910 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2911 if (ret
!= LB_ERR
) ret
= descr
->selected_item
;
2914 case LB_GETSELCOUNT
:
2915 return LISTBOX_GetSelCount( descr
);
2917 case LB_GETSELITEMS
:
2918 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2920 case LB_SELITEMRANGE
:
2921 if (LOWORD(lParam
) <= HIWORD(lParam
))
2922 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2923 HIWORD(lParam
), wParam
);
2925 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2926 LOWORD(lParam
), wParam
);
2928 case LB_SELITEMRANGEEX
:
2929 if ((INT
)lParam
>= (INT
)wParam
)
2930 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2932 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
2934 case LB_GETHORIZONTALEXTENT
:
2935 return descr
->horz_extent
;
2937 case LB_SETHORIZONTALEXTENT
:
2938 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
2940 case LB_GETANCHORINDEX
:
2941 return descr
->anchor_item
;
2943 case LB_SETANCHORINDEX
:
2944 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2946 SetLastError(ERROR_INVALID_INDEX
);
2949 descr
->anchor_item
= (INT
)wParam
;
2957 textW
= (LPWSTR
)lParam
;
2960 LPSTR textA
= (LPSTR
)lParam
;
2961 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2962 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2963 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2965 ret
= LISTBOX_Directory( descr
, wParam
, textW
, msg
== LB_DIR
);
2967 HeapFree(GetProcessHeap(), 0, textW
);
2972 return descr
->locale
;
2977 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
2979 ret
= descr
->locale
;
2980 descr
->locale
= (LCID
)wParam
;
2984 case LB_INITSTORAGE
:
2985 return LISTBOX_InitStorage( descr
, wParam
);
2988 return LISTBOX_SetCount( descr
, (INT
)wParam
);
2990 case LB_SETTABSTOPS
:
2991 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
);
2994 if (descr
->caret_on
)
2996 descr
->caret_on
= TRUE
;
2997 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2998 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3002 if (!descr
->caret_on
)
3004 descr
->caret_on
= FALSE
;
3005 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3006 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3009 case LB_GETLISTBOXINFO
:
3010 if (descr
->style
& LBS_MULTICOLUMN
) //// ReactOS
3011 return descr
->page_size
* descr
->column_width
;
3013 return descr
->page_size
;
3016 return LISTBOX_Destroy( descr
);
3019 InvalidateRect( descr
->self
, NULL
, TRUE
);
3023 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
3027 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
3029 case WM_PRINTCLIENT
:
3033 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
3034 ret
= LISTBOX_Paint( descr
, hdc
);
3035 if( !wParam
) EndPaint( descr
->self
, &ps
);
3039 LISTBOX_UpdateSize( descr
);
3042 return (LRESULT
)descr
->font
;
3044 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
3045 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
3048 descr
->in_focus
= TRUE
;
3049 descr
->caret_on
= TRUE
;
3050 if (descr
->focus_item
!= -1)
3051 LISTBOX_DrawFocusRect( descr
, TRUE
);
3052 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
3055 LISTBOX_HandleLButtonUp( descr
); /* Release capture if we have it */
3056 descr
->in_focus
= FALSE
;
3057 descr
->wheel_remain
= 0;
3058 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
3059 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3060 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
3063 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3065 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3067 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
3068 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
3069 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
3070 case WM_LBUTTONDOWN
:
3072 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3073 (INT16
)LOWORD(lParam
),
3074 (INT16
)HIWORD(lParam
) );
3075 return LISTBOX_HandleLButtonDown( descr
, wParam
,
3076 (INT16
)LOWORD(lParam
),
3077 (INT16
)HIWORD(lParam
) );
3078 case WM_LBUTTONDBLCLK
:
3080 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3081 (INT16
)LOWORD(lParam
),
3082 (INT16
)HIWORD(lParam
) );
3083 if (descr
->style
& LBS_NOTIFY
)
3084 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
3087 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
3089 BOOL captured
= descr
->captured
;
3093 mousePos
.x
= (INT16
)LOWORD(lParam
);
3094 mousePos
.y
= (INT16
)HIWORD(lParam
);
3097 * If we are in a dropdown combobox, we simulate that
3098 * the mouse is captured to show the tracking of the item.
3100 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
3101 descr
->captured
= TRUE
;
3103 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
3105 descr
->captured
= captured
;
3107 else if (GetCapture() == descr
->self
)
3109 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
3110 (INT16
)HIWORD(lParam
) );
3120 * If the mouse button "up" is not in the listbox,
3121 * we make sure there is no selection by re-selecting the
3122 * item that was selected when the listbox was made visible.
3124 mousePos
.x
= (INT16
)LOWORD(lParam
);
3125 mousePos
.y
= (INT16
)HIWORD(lParam
);
3127 GetClientRect(descr
->self
, &clientRect
);
3130 * When the user clicks outside the combobox and the focus
3131 * is lost, the owning combobox will send a fake buttonup with
3132 * 0xFFFFFFF as the mouse location, we must also revert the
3133 * selection to the original selection.
3135 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
3136 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
3138 return LISTBOX_HandleLButtonUp( descr
);
3140 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3142 /* for some reason Windows makes it possible to
3143 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3145 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3146 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3147 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3149 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3153 return LISTBOX_HandleKeyDown( descr
, wParam
);
3158 charW
= (WCHAR
)wParam
;
3161 CHAR charA
= (CHAR
)wParam
;
3162 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
3164 return LISTBOX_HandleChar( descr
, charW
);
3167 return LISTBOX_HandleSystemTimer( descr
);
3169 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3172 HBRUSH hbrush
= GetControlColor( descr
->owner
, descr
->self
, (HDC
)wParam
, WM_CTLCOLORLISTBOX
);
3173 TRACE("hbrush = %p\n", hbrush
);
3175 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3178 GetClientRect(descr
->self
, &rect
);
3179 FillRect((HDC
)wParam
, &rect
, hbrush
);
3184 if( lphc
) return 0;
3185 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3186 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3189 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3192 NtUserSetWindowFNID(hwnd
, FNID_DESTROY
);
3200 case WM_UPDATEUISTATE
:
3202 DefWindowProcW(descr
->self
, msg
, wParam
, lParam
);
3204 DefWindowProcA(descr
->self
, msg
, wParam
, lParam
);
3206 if (LISTBOX_update_uistate(descr
))
3209 if (descr
->focus_item
!= -1)
3210 LISTBOX_DrawFocusRect( descr
, descr
->in_focus
);
3215 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3216 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3217 hwnd
, msg
, wParam
, lParam
);
3220 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3221 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3224 /***********************************************************************
3227 LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3229 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3232 /***********************************************************************
3235 LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3237 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);