4 * Copyright 1996 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 * This code was audited for completeness against the documented features
23 * of Comctl32.dll version 6.0 on Oct. 9, 2004, by Dimitrie O. Paun.
25 * Unless otherwise noted, we believe this code to be complete, as per
26 * the specification mentioned above.
27 * If you discover missing features, or bugs, please note them below.
40 #include "wine/unicode.h"
41 #include "user_private.h"
43 #include "wine/exception.h"
44 #include "wine/debug.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(listbox
);
48 /* Items array granularity */
49 #define LB_ARRAY_GRANULARITY 16
51 /* Scrolling timeout in ms */
52 #define LB_SCROLL_TIMEOUT 50
54 /* Listbox system timer id */
57 /* flag listbox changed while setredraw false - internal style */
58 #define LBS_DISPLAYCHANGED 0x80000000
63 LPWSTR str
; /* Item text */
64 BOOL selected
; /* Is item selected? */
65 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
66 ULONG_PTR data
; /* User data */
69 /* Listbox structure */
72 HWND self
; /* Our own window handle */
73 HWND owner
; /* Owner window to send notifications to */
74 UINT style
; /* Window style */
75 INT width
; /* Window width */
76 INT height
; /* Window height */
77 LB_ITEMDATA
*items
; /* Array of items */
78 INT nb_items
; /* Number of items */
79 INT top_item
; /* Top visible item */
80 INT selected_item
; /* Selected item */
81 INT focus_item
; /* Item that has the focus */
82 INT anchor_item
; /* Anchor item for extended selection */
83 INT item_height
; /* Default item height */
84 INT page_size
; /* Items per listbox page */
85 INT column_width
; /* Column width for multi-column listboxes */
86 INT horz_extent
; /* Horizontal extent */
87 INT horz_pos
; /* Horizontal position */
88 INT nb_tabs
; /* Number of tabs in array */
89 INT
*tabs
; /* Array of tabs */
90 INT avg_char_width
; /* Average width of characters */
91 INT wheel_remain
; /* Left over scroll amount */
92 BOOL caret_on
; /* Is caret on? */
93 BOOL captured
; /* Is mouse captured? */
95 HFONT font
; /* Current font */
96 LCID locale
; /* Current locale for string comparisons */
97 LPHEADCOMBO lphc
; /* ComboLBox */
101 #define IS_OWNERDRAW(descr) \
102 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
104 #define HAS_STRINGS(descr) \
105 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
108 #define IS_MULTISELECT(descr) \
109 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
110 !((descr)->style & LBS_NOSEL))
112 #define SEND_NOTIFICATION(descr,code) \
113 (SendMessageW( (descr)->owner, WM_COMMAND, \
114 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
116 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
118 /* Current timer status */
128 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
130 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
);
132 /*********************************************************************
133 * listbox class descriptor
135 static const WCHAR listboxW
[] = {'L','i','s','t','B','o','x',0};
136 const struct builtin_class_descr LISTBOX_builtin_class
=
139 CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
140 WINPROC_LISTBOX
, /* proc */
141 sizeof(LB_DESCR
*), /* extra */
142 IDC_ARROW
, /* cursor */
147 /*********************************************************************
148 * combolbox class descriptor
150 static const WCHAR combolboxW
[] = {'C','o','m','b','o','L','B','o','x',0};
151 const struct builtin_class_descr COMBOLBOX_builtin_class
=
153 combolboxW
, /* name */
154 CS_DBLCLKS
| CS_SAVEBITS
, /* style */
155 WINPROC_LISTBOX
, /* proc */
156 sizeof(LB_DESCR
*), /* extra */
157 IDC_ARROW
, /* cursor */
162 /***********************************************************************
163 * LISTBOX_GetCurrentPageSize
165 * Return the current page size
167 static INT
LISTBOX_GetCurrentPageSize( const LB_DESCR
*descr
)
170 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
171 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
173 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
175 if (i
== descr
->top_item
) return 1;
176 else return i
- descr
->top_item
;
180 /***********************************************************************
181 * LISTBOX_GetMaxTopIndex
183 * Return the maximum possible index for the top of the listbox.
185 static INT
LISTBOX_GetMaxTopIndex( const LB_DESCR
*descr
)
189 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
191 page
= descr
->height
;
192 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
193 if ((page
-= descr
->items
[max
].height
) < 0) break;
194 if (max
< descr
->nb_items
- 1) max
++;
196 else if (descr
->style
& LBS_MULTICOLUMN
)
198 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
199 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
200 max
= (max
- page
) * descr
->page_size
;
204 max
= descr
->nb_items
- descr
->page_size
;
206 if (max
< 0) max
= 0;
211 /***********************************************************************
212 * LISTBOX_UpdateScroll
214 * Update the scrollbars. Should be called whenever the content
215 * of the listbox changes.
217 static void LISTBOX_UpdateScroll( LB_DESCR
*descr
)
221 /* Check the listbox scroll bar flags individually before we call
222 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
223 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
224 scroll bar when we do not need one.
225 if (!(descr->style & WS_VSCROLL)) return;
228 /* It is important that we check descr->style, and not wnd->dwStyle,
229 for WS_VSCROLL, as the former is exactly the one passed in
230 argument to CreateWindow.
231 In Windows (and from now on in Wine :) a listbox created
232 with such a style (no WS_SCROLL) does not update
233 the scrollbar with listbox-related data, thus letting
234 the programmer use it for his/her own purposes. */
236 if (descr
->style
& LBS_NOREDRAW
) return;
237 info
.cbSize
= sizeof(info
);
239 if (descr
->style
& LBS_MULTICOLUMN
)
242 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
243 info
.nPos
= descr
->top_item
/ descr
->page_size
;
244 info
.nPage
= descr
->width
/ descr
->column_width
;
245 if (info
.nPage
< 1) info
.nPage
= 1;
246 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
247 if (descr
->style
& LBS_DISABLENOSCROLL
)
248 info
.fMask
|= SIF_DISABLENOSCROLL
;
249 if (descr
->style
& WS_HSCROLL
)
250 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
252 info
.fMask
= SIF_RANGE
;
253 if (descr
->style
& WS_VSCROLL
)
254 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
259 info
.nMax
= descr
->nb_items
- 1;
260 info
.nPos
= descr
->top_item
;
261 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
262 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
263 if (descr
->style
& LBS_DISABLENOSCROLL
)
264 info
.fMask
|= SIF_DISABLENOSCROLL
;
265 if (descr
->style
& WS_VSCROLL
)
266 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
268 if ((descr
->style
& WS_HSCROLL
) && descr
->horz_extent
)
270 info
.nPos
= descr
->horz_pos
;
271 info
.nPage
= descr
->width
;
272 info
.fMask
= SIF_POS
| SIF_PAGE
;
273 if (descr
->style
& LBS_DISABLENOSCROLL
)
274 info
.fMask
|= SIF_DISABLENOSCROLL
;
275 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
279 if (descr
->style
& LBS_DISABLENOSCROLL
)
283 info
.fMask
= SIF_RANGE
| SIF_DISABLENOSCROLL
;
284 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
288 ShowScrollBar( descr
->self
, SB_HORZ
, FALSE
);
295 /***********************************************************************
298 * Set the top item of the listbox, scrolling up or down if necessary.
300 static LRESULT
LISTBOX_SetTopItem( LB_DESCR
*descr
, INT index
, BOOL scroll
)
302 INT max
= LISTBOX_GetMaxTopIndex( descr
);
304 TRACE("setting top item %d, scroll %d\n", index
, scroll
);
306 if (index
> max
) index
= max
;
307 if (index
< 0) index
= 0;
308 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
309 if (descr
->top_item
== index
) return LB_OKAY
;
313 if (descr
->style
& LBS_MULTICOLUMN
)
314 diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
315 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
319 if (index
> descr
->top_item
)
321 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
322 diff
-= descr
->items
[i
].height
;
326 for (i
= index
; i
< descr
->top_item
; i
++)
327 diff
+= descr
->items
[i
].height
;
331 diff
= (descr
->top_item
- index
) * descr
->item_height
;
333 ScrollWindowEx( descr
->self
, 0, diff
, NULL
, NULL
, 0, NULL
,
334 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
337 InvalidateRect( descr
->self
, NULL
, TRUE
);
338 descr
->top_item
= index
;
339 LISTBOX_UpdateScroll( descr
);
344 /***********************************************************************
347 * Update the page size. Should be called when the size of
348 * the client area or the item height changes.
350 static void LISTBOX_UpdatePage( LB_DESCR
*descr
)
354 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
356 if (page_size
== descr
->page_size
) return;
357 descr
->page_size
= page_size
;
358 if (descr
->style
& LBS_MULTICOLUMN
)
359 InvalidateRect( descr
->self
, NULL
, TRUE
);
360 LISTBOX_SetTopItem( descr
, descr
->top_item
, FALSE
);
364 /***********************************************************************
367 * Update the size of the listbox. Should be called when the size of
368 * the client area changes.
370 static void LISTBOX_UpdateSize( LB_DESCR
*descr
)
374 GetClientRect( descr
->self
, &rect
);
375 descr
->width
= rect
.right
- rect
.left
;
376 descr
->height
= rect
.bottom
- rect
.top
;
377 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
382 GetWindowRect( descr
->self
, &rect
);
383 if(descr
->item_height
!= 0)
384 remaining
= descr
->height
% descr
->item_height
;
387 if ((descr
->height
> descr
->item_height
) && remaining
)
389 TRACE("[%p]: changing height %d -> %d\n",
390 descr
->self
, descr
->height
, descr
->height
- remaining
);
391 SetWindowPos( descr
->self
, 0, 0, 0, rect
.right
- rect
.left
,
392 rect
.bottom
- rect
.top
- remaining
,
393 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
397 TRACE("[%p]: new size = %d,%d\n", descr
->self
, descr
->width
, descr
->height
);
398 LISTBOX_UpdatePage( descr
);
399 LISTBOX_UpdateScroll( descr
);
401 /* Invalidate the focused item so it will be repainted correctly */
402 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
404 InvalidateRect( descr
->self
, &rect
, FALSE
);
409 /***********************************************************************
410 * LISTBOX_GetItemRect
412 * Get the rectangle enclosing an item, in listbox client coordinates.
413 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
415 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
)
417 /* Index <= 0 is legal even on empty listboxes */
418 if (index
&& (index
>= descr
->nb_items
))
421 SetLastError(ERROR_INVALID_INDEX
);
424 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
425 if (descr
->style
& LBS_MULTICOLUMN
)
427 INT col
= (index
/ descr
->page_size
) -
428 (descr
->top_item
/ descr
->page_size
);
429 rect
->left
+= col
* descr
->column_width
;
430 rect
->right
= rect
->left
+ descr
->column_width
;
431 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
432 rect
->bottom
= rect
->top
+ descr
->item_height
;
434 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
437 rect
->right
+= descr
->horz_pos
;
438 if ((index
>= 0) && (index
< descr
->nb_items
))
440 if (index
< descr
->top_item
)
442 for (i
= descr
->top_item
-1; i
>= index
; i
--)
443 rect
->top
-= descr
->items
[i
].height
;
447 for (i
= descr
->top_item
; i
< index
; i
++)
448 rect
->top
+= descr
->items
[i
].height
;
450 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
456 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
457 rect
->bottom
= rect
->top
+ descr
->item_height
;
458 rect
->right
+= descr
->horz_pos
;
461 TRACE("item %d, rect %s\n", index
, wine_dbgstr_rect(rect
));
463 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
464 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
468 /***********************************************************************
469 * LISTBOX_GetItemFromPoint
471 * Return the item nearest from point (x,y) (in client coordinates).
473 static INT
LISTBOX_GetItemFromPoint( const LB_DESCR
*descr
, INT x
, INT y
)
475 INT index
= descr
->top_item
;
477 if (!descr
->nb_items
) return -1; /* No items */
478 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
483 while (index
< descr
->nb_items
)
485 if ((pos
+= descr
->items
[index
].height
) > y
) break;
494 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
498 else if (descr
->style
& LBS_MULTICOLUMN
)
500 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
501 if (y
>= 0) index
+= y
/ descr
->item_height
;
502 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
503 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
507 index
+= (y
/ descr
->item_height
);
509 if (index
< 0) return 0;
510 if (index
>= descr
->nb_items
) return -1;
515 /***********************************************************************
520 static void LISTBOX_PaintItem( LB_DESCR
*descr
, HDC hdc
, const RECT
*rect
,
521 INT index
, UINT action
, BOOL ignoreFocus
)
523 LB_ITEMDATA
*item
= NULL
;
524 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
526 if (IS_OWNERDRAW(descr
))
534 if (action
== ODA_FOCUS
)
535 DrawFocusRect( hdc
, rect
);
537 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
541 /* some programs mess with the clipping region when
542 drawing the item, *and* restore the previous region
543 after they are done, so a region has better to exist
544 else everything ends clipped */
545 GetClientRect(descr
->self
, &r
);
546 hrgn
= set_control_clipping( hdc
, &r
);
548 dis
.CtlType
= ODT_LISTBOX
;
549 dis
.CtlID
= GetWindowLongPtrW( descr
->self
, GWLP_ID
);
550 dis
.hwndItem
= descr
->self
;
551 dis
.itemAction
= action
;
555 if (item
->selected
) dis
.itemState
|= ODS_SELECTED
;
556 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
558 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
559 if (!IsWindowEnabled(descr
->self
)) dis
.itemState
|= ODS_DISABLED
;
560 dis
.itemData
= item
->data
;
562 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
563 descr
->self
, index
, debugstr_w(item
->str
), action
,
564 dis
.itemState
, wine_dbgstr_rect(rect
) );
565 SendMessageW(descr
->owner
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
566 SelectClipRgn( hdc
, hrgn
);
567 if (hrgn
) DeleteObject( hrgn
);
571 COLORREF oldText
= 0, oldBk
= 0;
573 if (action
== ODA_FOCUS
)
575 DrawFocusRect( hdc
, rect
);
578 if (item
&& item
->selected
)
580 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
581 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
584 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
585 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
586 wine_dbgstr_rect(rect
) );
588 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
589 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
590 else if (!(descr
->style
& LBS_USETABSTOPS
))
591 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
592 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
593 strlenW(item
->str
), NULL
);
596 /* Output empty string to paint background in the full width. */
597 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
598 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
599 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
600 item
->str
, strlenW(item
->str
),
601 descr
->nb_tabs
, descr
->tabs
, 0);
603 if (item
&& item
->selected
)
605 SetBkColor( hdc
, oldBk
);
606 SetTextColor( hdc
, oldText
);
608 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
610 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
615 /***********************************************************************
618 * Change the redraw flag.
620 static void LISTBOX_SetRedraw( LB_DESCR
*descr
, BOOL on
)
624 if (!(descr
->style
& LBS_NOREDRAW
)) return;
625 descr
->style
&= ~LBS_NOREDRAW
;
626 if (descr
->style
& LBS_DISPLAYCHANGED
)
627 { /* page was changed while setredraw false, refresh automatically */
628 InvalidateRect(descr
->self
, NULL
, TRUE
);
629 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
630 { /* reset top of page if less than number of items/page */
631 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
632 if (descr
->top_item
< 0) descr
->top_item
= 0;
634 descr
->style
&= ~LBS_DISPLAYCHANGED
;
636 LISTBOX_UpdateScroll( descr
);
638 else descr
->style
|= LBS_NOREDRAW
;
642 /***********************************************************************
643 * LISTBOX_RepaintItem
645 * Repaint a single item synchronously.
647 static void LISTBOX_RepaintItem( LB_DESCR
*descr
, INT index
, UINT action
)
652 HBRUSH hbrush
, oldBrush
= 0;
654 /* Do not repaint the item if the item is not visible */
655 if (!IsWindowVisible(descr
->self
)) return;
656 if (descr
->style
& LBS_NOREDRAW
)
658 descr
->style
|= LBS_DISPLAYCHANGED
;
661 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
662 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
663 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
664 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
665 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
666 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
667 if (!IsWindowEnabled(descr
->self
))
668 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
669 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
670 LISTBOX_PaintItem( descr
, hdc
, &rect
, index
, action
, TRUE
);
671 if (oldFont
) SelectObject( hdc
, oldFont
);
672 if (oldBrush
) SelectObject( hdc
, oldBrush
);
673 ReleaseDC( descr
->self
, hdc
);
677 /***********************************************************************
678 * LISTBOX_DrawFocusRect
680 static void LISTBOX_DrawFocusRect( LB_DESCR
*descr
, BOOL on
)
686 /* Do not repaint the item if the item is not visible */
687 if (!IsWindowVisible(descr
->self
)) return;
689 if (descr
->focus_item
== -1) return;
690 if (!descr
->caret_on
|| !descr
->in_focus
) return;
692 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) != 1) return;
693 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
694 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
695 if (!IsWindowEnabled(descr
->self
))
696 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
697 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
698 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, !on
);
699 if (oldFont
) SelectObject( hdc
, oldFont
);
700 ReleaseDC( descr
->self
, hdc
);
704 /***********************************************************************
705 * LISTBOX_InitStorage
707 static LRESULT
LISTBOX_InitStorage( LB_DESCR
*descr
, INT nb_items
)
711 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
712 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
714 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
715 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
716 nb_items
* sizeof(LB_ITEMDATA
));
719 item
= HeapAlloc( GetProcessHeap(), 0,
720 nb_items
* sizeof(LB_ITEMDATA
));
725 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
733 /***********************************************************************
734 * LISTBOX_SetTabStops
736 static BOOL
LISTBOX_SetTabStops( LB_DESCR
*descr
, INT count
, LPINT tabs
)
740 if (!(descr
->style
& LBS_USETABSTOPS
))
742 SetLastError(ERROR_LB_WITHOUT_TABSTOPS
);
746 HeapFree( GetProcessHeap(), 0, descr
->tabs
);
747 if (!(descr
->nb_tabs
= count
))
752 if (!(descr
->tabs
= HeapAlloc( GetProcessHeap(), 0,
753 descr
->nb_tabs
* sizeof(INT
) )))
755 memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
757 /* convert into "dialog units"*/
758 for (i
= 0; i
< descr
->nb_tabs
; i
++)
759 descr
->tabs
[i
] = MulDiv(descr
->tabs
[i
], descr
->avg_char_width
, 4);
765 /***********************************************************************
768 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPWSTR buffer
, BOOL unicode
)
772 if ((index
< 0) || (index
>= descr
->nb_items
))
774 SetLastError(ERROR_INVALID_INDEX
);
777 if (HAS_STRINGS(descr
))
781 len
= strlenW(descr
->items
[index
].str
);
784 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[index
].str
, len
,
785 NULL
, 0, NULL
, NULL
);
788 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
790 __TRY
/* hide a Delphi bug that passes a read-only buffer */
794 strcpyW( buffer
, descr
->items
[index
].str
);
795 len
= strlenW(buffer
);
799 len
= WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1,
800 (LPSTR
)buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
805 WARN( "got an invalid buffer (Delphi bug?)\n" );
806 SetLastError( ERROR_INVALID_PARAMETER
);
812 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
818 static inline INT
LISTBOX_lstrcmpiW( LCID lcid
, LPCWSTR str1
, LPCWSTR str2
)
820 INT ret
= CompareStringW( lcid
, NORM_IGNORECASE
, str1
, -1, str2
, -1 );
821 if (ret
== CSTR_LESS_THAN
)
823 if (ret
== CSTR_EQUAL
)
825 if (ret
== CSTR_GREATER_THAN
)
830 /***********************************************************************
831 * LISTBOX_FindStringPos
833 * Find the nearest string located before a given string in sort order.
834 * If 'exact' is TRUE, return an error if we don't get an exact match.
836 static INT
LISTBOX_FindStringPos( LB_DESCR
*descr
, LPCWSTR str
, BOOL exact
)
838 INT index
, min
, max
, res
;
840 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
842 max
= descr
->nb_items
;
845 index
= (min
+ max
) / 2;
846 if (HAS_STRINGS(descr
))
847 res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, descr
->items
[index
].str
);
850 COMPAREITEMSTRUCT cis
;
851 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
853 cis
.CtlType
= ODT_LISTBOX
;
855 cis
.hwndItem
= descr
->self
;
856 /* note that some application (MetaStock) expects the second item
857 * to be in the listbox */
859 cis
.itemData1
= (ULONG_PTR
)str
;
861 cis
.itemData2
= descr
->items
[index
].data
;
862 cis
.dwLocaleId
= descr
->locale
;
863 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
865 if (!res
) return index
;
866 if (res
< 0) max
= index
;
867 else min
= index
+ 1;
869 return exact
? -1 : max
;
873 /***********************************************************************
874 * LISTBOX_FindFileStrPos
876 * Find the nearest string located before a given string in directory
877 * sort order (i.e. first files, then directories, then drives).
879 static INT
LISTBOX_FindFileStrPos( LB_DESCR
*descr
, LPCWSTR str
)
883 if (!HAS_STRINGS(descr
))
884 return LISTBOX_FindStringPos( descr
, str
, FALSE
);
886 max
= descr
->nb_items
;
889 INT index
= (min
+ max
) / 2;
890 LPCWSTR p
= descr
->items
[index
].str
;
891 if (*p
== '[') /* drive or directory */
893 if (*str
!= '[') res
= -1;
894 else if (p
[1] == '-') /* drive */
896 if (str
[1] == '-') res
= str
[2] - p
[2];
901 if (str
[1] == '-') res
= 1;
902 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
907 if (*str
== '[') res
= 1;
908 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
910 if (!res
) return index
;
911 if (res
< 0) max
= index
;
912 else min
= index
+ 1;
918 /***********************************************************************
921 * Find the item beginning with a given string.
923 static INT
LISTBOX_FindString( LB_DESCR
*descr
, INT start
, LPCWSTR str
, BOOL exact
)
928 if (start
>= descr
->nb_items
) start
= -1;
929 item
= descr
->items
+ start
+ 1;
930 if (HAS_STRINGS(descr
))
932 if (!str
|| ! str
[0] ) return LB_ERR
;
935 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
936 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
937 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
938 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
942 /* Special case for drives and directories: ignore prefix */
943 #define CHECK_DRIVE(item) \
944 if ((item)->str[0] == '[') \
946 if (!strncmpiW( str, (item)->str+1, len )) return i; \
947 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
951 INT len
= strlenW(str
);
952 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
954 if (!strncmpiW( str
, item
->str
, len
)) return i
;
957 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
959 if (!strncmpiW( str
, item
->str
, len
)) return i
;
967 if (exact
&& (descr
->style
& LBS_SORT
))
968 /* If sorted, use a WM_COMPAREITEM binary search */
969 return LISTBOX_FindStringPos( descr
, str
, TRUE
);
971 /* Otherwise use a linear search */
972 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
973 if (item
->data
== (ULONG_PTR
)str
) return i
;
974 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
975 if (item
->data
== (ULONG_PTR
)str
) return i
;
981 /***********************************************************************
982 * LISTBOX_GetSelCount
984 static LRESULT
LISTBOX_GetSelCount( const LB_DESCR
*descr
)
987 const LB_ITEMDATA
*item
= descr
->items
;
989 if (!(descr
->style
& LBS_MULTIPLESEL
) ||
990 (descr
->style
& LBS_NOSEL
))
992 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
993 if (item
->selected
) count
++;
998 /***********************************************************************
999 * LISTBOX_GetSelItems
1001 static LRESULT
LISTBOX_GetSelItems( const LB_DESCR
*descr
, INT max
, LPINT array
)
1004 const LB_ITEMDATA
*item
= descr
->items
;
1006 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1007 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
1008 if (item
->selected
) array
[count
++] = i
;
1013 /***********************************************************************
1016 static LRESULT
LISTBOX_Paint( LB_DESCR
*descr
, HDC hdc
)
1018 INT i
, col_pos
= descr
->page_size
- 1;
1020 RECT focusRect
= {-1, -1, -1, -1};
1022 HBRUSH hbrush
, oldBrush
= 0;
1024 if (descr
->style
& LBS_NOREDRAW
) return 0;
1026 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1027 if (descr
->style
& LBS_MULTICOLUMN
)
1028 rect
.right
= rect
.left
+ descr
->column_width
;
1029 else if (descr
->horz_pos
)
1031 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1032 rect
.right
+= descr
->horz_pos
;
1035 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1036 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
1037 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
1038 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1039 if (!IsWindowEnabled(descr
->self
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1041 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1044 /* Special case for empty listbox: paint focus rect */
1045 rect
.bottom
= rect
.top
+ descr
->item_height
;
1046 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1047 &rect
, NULL
, 0, NULL
);
1048 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1049 rect
.top
= rect
.bottom
;
1052 /* Paint all the item, regarding the selection
1053 Focus state will be painted after */
1055 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1057 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1058 rect
.bottom
= rect
.top
+ descr
->item_height
;
1060 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1062 /* keep the focus rect, to paint the focus item after */
1063 if (i
== descr
->focus_item
)
1066 LISTBOX_PaintItem( descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1067 rect
.top
= rect
.bottom
;
1069 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1071 if (!IS_OWNERDRAW(descr
))
1073 /* Clear the bottom of the column */
1074 if (rect
.top
< descr
->height
)
1076 rect
.bottom
= descr
->height
;
1077 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1078 &rect
, NULL
, 0, NULL
);
1082 /* Go to the next column */
1083 rect
.left
+= descr
->column_width
;
1084 rect
.right
+= descr
->column_width
;
1086 col_pos
= descr
->page_size
- 1;
1091 if (rect
.top
>= descr
->height
) break;
1095 /* Paint the focus item now */
1096 if (focusRect
.top
!= focusRect
.bottom
&&
1097 descr
->caret_on
&& descr
->in_focus
)
1098 LISTBOX_PaintItem( descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1100 if (!IS_OWNERDRAW(descr
))
1102 /* Clear the remainder of the client area */
1103 if (rect
.top
< descr
->height
)
1105 rect
.bottom
= descr
->height
;
1106 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1107 &rect
, NULL
, 0, NULL
);
1109 if (rect
.right
< descr
->width
)
1111 rect
.left
= rect
.right
;
1112 rect
.right
= descr
->width
;
1114 rect
.bottom
= descr
->height
;
1115 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1116 &rect
, NULL
, 0, NULL
);
1119 if (oldFont
) SelectObject( hdc
, oldFont
);
1120 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1125 /***********************************************************************
1126 * LISTBOX_InvalidateItems
1128 * Invalidate all items from a given item. If the specified item is not
1129 * visible, nothing happens.
1131 static void LISTBOX_InvalidateItems( LB_DESCR
*descr
, INT index
)
1135 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1137 if (descr
->style
& LBS_NOREDRAW
)
1139 descr
->style
|= LBS_DISPLAYCHANGED
;
1142 rect
.bottom
= descr
->height
;
1143 InvalidateRect( descr
->self
, &rect
, TRUE
);
1144 if (descr
->style
& LBS_MULTICOLUMN
)
1146 /* Repaint the other columns */
1147 rect
.left
= rect
.right
;
1148 rect
.right
= descr
->width
;
1150 InvalidateRect( descr
->self
, &rect
, TRUE
);
1155 static void LISTBOX_InvalidateItemRect( LB_DESCR
*descr
, INT index
)
1159 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1160 InvalidateRect( descr
->self
, &rect
, TRUE
);
1163 /***********************************************************************
1164 * LISTBOX_GetItemHeight
1166 static LRESULT
LISTBOX_GetItemHeight( const LB_DESCR
*descr
, INT index
)
1168 if (descr
->style
& LBS_OWNERDRAWVARIABLE
&& descr
->nb_items
> 0)
1170 if ((index
< 0) || (index
>= descr
->nb_items
))
1172 SetLastError(ERROR_INVALID_INDEX
);
1175 return descr
->items
[index
].height
;
1177 else return descr
->item_height
;
1181 /***********************************************************************
1182 * LISTBOX_SetItemHeight
1184 static LRESULT
LISTBOX_SetItemHeight( LB_DESCR
*descr
, INT index
, INT height
, BOOL repaint
)
1186 if (height
> MAXBYTE
)
1189 if (!height
) height
= 1;
1191 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1193 if ((index
< 0) || (index
>= descr
->nb_items
))
1195 SetLastError(ERROR_INVALID_INDEX
);
1198 TRACE("[%p]: item %d height = %d\n", descr
->self
, index
, height
);
1199 descr
->items
[index
].height
= height
;
1200 LISTBOX_UpdateScroll( descr
);
1202 LISTBOX_InvalidateItems( descr
, index
);
1204 else if (height
!= descr
->item_height
)
1206 TRACE("[%p]: new height = %d\n", descr
->self
, height
);
1207 descr
->item_height
= height
;
1208 LISTBOX_UpdatePage( descr
);
1209 LISTBOX_UpdateScroll( descr
);
1211 InvalidateRect( descr
->self
, 0, TRUE
);
1217 /***********************************************************************
1218 * LISTBOX_SetHorizontalPos
1220 static void LISTBOX_SetHorizontalPos( LB_DESCR
*descr
, INT pos
)
1224 if (pos
> descr
->horz_extent
- descr
->width
)
1225 pos
= descr
->horz_extent
- descr
->width
;
1226 if (pos
< 0) pos
= 0;
1227 if (!(diff
= descr
->horz_pos
- pos
)) return;
1228 TRACE("[%p]: new horz pos = %d\n", descr
->self
, pos
);
1229 descr
->horz_pos
= pos
;
1230 LISTBOX_UpdateScroll( descr
);
1231 if (abs(diff
) < descr
->width
)
1234 /* Invalidate the focused item so it will be repainted correctly */
1235 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1236 InvalidateRect( descr
->self
, &rect
, TRUE
);
1237 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
1238 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1241 InvalidateRect( descr
->self
, NULL
, TRUE
);
1245 /***********************************************************************
1246 * LISTBOX_SetHorizontalExtent
1248 static LRESULT
LISTBOX_SetHorizontalExtent( LB_DESCR
*descr
, INT extent
)
1250 if (descr
->style
& LBS_MULTICOLUMN
)
1252 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1253 TRACE("[%p]: new horz extent = %d\n", descr
->self
, extent
);
1254 descr
->horz_extent
= extent
;
1255 if (descr
->style
& WS_HSCROLL
) {
1257 info
.cbSize
= sizeof(info
);
1259 info
.nMax
= descr
->horz_extent
? descr
->horz_extent
- 1 : 0;
1260 info
.fMask
= SIF_RANGE
;
1261 if (descr
->style
& LBS_DISABLENOSCROLL
)
1262 info
.fMask
|= SIF_DISABLENOSCROLL
;
1263 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
1265 if (descr
->horz_pos
> extent
- descr
->width
)
1266 LISTBOX_SetHorizontalPos( descr
, extent
- descr
->width
);
1271 /***********************************************************************
1272 * LISTBOX_SetColumnWidth
1274 static LRESULT
LISTBOX_SetColumnWidth( LB_DESCR
*descr
, INT width
)
1276 if (width
== descr
->column_width
) return LB_OKAY
;
1277 TRACE("[%p]: new column width = %d\n", descr
->self
, width
);
1278 descr
->column_width
= width
;
1279 LISTBOX_UpdatePage( descr
);
1284 /***********************************************************************
1287 * Returns the item height.
1289 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1293 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1298 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1300 ERR("unable to get DC.\n" );
1303 if (font
) oldFont
= SelectObject( hdc
, font
);
1304 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1305 if (oldFont
) SelectObject( hdc
, oldFont
);
1306 ReleaseDC( descr
->self
, hdc
);
1308 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1309 if (!IS_OWNERDRAW(descr
))
1310 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1315 /***********************************************************************
1316 * LISTBOX_MakeItemVisible
1318 * Make sure that a given item is partially or fully visible.
1320 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1324 TRACE("current top item %d, index %d, fully %d\n", descr
->top_item
, index
, fully
);
1326 if (index
<= descr
->top_item
) top
= index
;
1327 else if (descr
->style
& LBS_MULTICOLUMN
)
1329 INT cols
= descr
->width
;
1330 if (!fully
) cols
+= descr
->column_width
- 1;
1331 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1333 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1334 top
= index
- descr
->page_size
* (cols
- 1);
1336 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1338 INT height
= fully
? descr
->items
[index
].height
: 1;
1339 for (top
= index
; top
> descr
->top_item
; top
--)
1340 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1344 if (index
< descr
->top_item
+ descr
->page_size
) return;
1345 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1346 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1347 top
= index
- descr
->page_size
+ 1;
1349 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1352 /***********************************************************************
1353 * LISTBOX_SetCaretIndex
1356 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1359 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1361 INT oldfocus
= descr
->focus_item
;
1363 TRACE("old focus %d, index %d\n", oldfocus
, index
);
1365 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1366 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1367 if (index
== oldfocus
) return LB_OKAY
;
1369 LISTBOX_DrawFocusRect( descr
, FALSE
);
1370 descr
->focus_item
= index
;
1372 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1373 LISTBOX_DrawFocusRect( descr
, TRUE
);
1379 /***********************************************************************
1380 * LISTBOX_SelectItemRange
1382 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1384 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1389 /* A few sanity checks */
1391 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1392 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1394 if (!descr
->nb_items
) return LB_OKAY
;
1396 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1397 if (first
< 0) first
= 0;
1398 if (last
< first
) return LB_OKAY
;
1400 if (on
) /* Turn selection on */
1402 for (i
= first
; i
<= last
; i
++)
1404 if (descr
->items
[i
].selected
) continue;
1405 descr
->items
[i
].selected
= TRUE
;
1406 LISTBOX_InvalidateItemRect(descr
, i
);
1409 else /* Turn selection off */
1411 for (i
= first
; i
<= last
; i
++)
1413 if (!descr
->items
[i
].selected
) continue;
1414 descr
->items
[i
].selected
= FALSE
;
1415 LISTBOX_InvalidateItemRect(descr
, i
);
1421 /***********************************************************************
1422 * LISTBOX_SetSelection
1424 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1425 BOOL on
, BOOL send_notify
)
1427 TRACE( "cur_sel=%d index=%d notify=%s\n",
1428 descr
->selected_item
, index
, send_notify
? "YES" : "NO" );
1430 if (descr
->style
& LBS_NOSEL
)
1432 descr
->selected_item
= index
;
1435 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1436 if (descr
->style
& LBS_MULTIPLESEL
)
1438 if (index
== -1) /* Select all items */
1439 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1440 else /* Only one item */
1441 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1445 INT oldsel
= descr
->selected_item
;
1446 if (index
== oldsel
) return LB_OKAY
;
1447 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1448 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1449 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1450 descr
->selected_item
= index
;
1451 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1452 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1453 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1455 if( descr
->lphc
) /* set selection change flag for parent combo */
1456 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1462 /***********************************************************************
1465 * Change the caret position and extend the selection to the new caret.
1467 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1469 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1471 if ((index
< 0) || (index
>= descr
->nb_items
))
1474 /* Important, repaint needs to be done in this order if
1475 you want to mimic Windows behavior:
1476 1. Remove the focus and paint the item
1477 2. Remove the selection and paint the item(s)
1478 3. Set the selection and repaint the item(s)
1479 4. Set the focus to 'index' and repaint the item */
1481 /* 1. remove the focus and repaint the item */
1482 LISTBOX_DrawFocusRect( descr
, FALSE
);
1484 /* 2. then turn off the previous selection */
1485 /* 3. repaint the new selected item */
1486 if (descr
->style
& LBS_EXTENDEDSEL
)
1488 if (descr
->anchor_item
!= -1)
1490 INT first
= min( index
, descr
->anchor_item
);
1491 INT last
= max( index
, descr
->anchor_item
);
1493 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1494 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1495 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1498 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1500 /* Set selection to new caret item */
1501 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1504 /* 4. repaint the new item with the focus */
1505 descr
->focus_item
= index
;
1506 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1507 LISTBOX_DrawFocusRect( descr
, TRUE
);
1511 /***********************************************************************
1512 * LISTBOX_InsertItem
1514 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1515 LPWSTR str
, ULONG_PTR data
)
1519 INT oldfocus
= descr
->focus_item
;
1521 if (index
== -1) index
= descr
->nb_items
;
1522 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1523 if (!descr
->items
) max_items
= 0;
1524 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1525 if (descr
->nb_items
== max_items
)
1527 /* We need to grow the array */
1528 max_items
+= LB_ARRAY_GRANULARITY
;
1530 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1531 max_items
* sizeof(LB_ITEMDATA
) );
1533 item
= HeapAlloc( GetProcessHeap(), 0,
1534 max_items
* sizeof(LB_ITEMDATA
) );
1537 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1540 descr
->items
= item
;
1543 /* Insert the item structure */
1545 item
= &descr
->items
[index
];
1546 if (index
< descr
->nb_items
)
1547 RtlMoveMemory( item
+ 1, item
,
1548 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1552 item
->selected
= FALSE
;
1555 /* Get item height */
1557 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1559 MEASUREITEMSTRUCT mis
;
1560 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1562 mis
.CtlType
= ODT_LISTBOX
;
1565 mis
.itemData
= descr
->items
[index
].data
;
1566 mis
.itemHeight
= descr
->item_height
;
1567 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1568 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1569 TRACE("[%p]: measure item %d (%s) = %d\n",
1570 descr
->self
, index
, str
? debugstr_w(str
) : "", item
->height
);
1573 /* Repaint the items */
1575 LISTBOX_UpdateScroll( descr
);
1576 LISTBOX_InvalidateItems( descr
, index
);
1578 /* Move selection and focused item */
1579 /* If listbox was empty, set focus to the first item */
1580 if (descr
->nb_items
== 1)
1581 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1582 /* single select don't change selection index in win31 */
1583 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1585 descr
->selected_item
++;
1586 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1590 if (index
<= descr
->selected_item
)
1592 descr
->selected_item
++;
1593 descr
->focus_item
= oldfocus
; /* focus not changed */
1600 /***********************************************************************
1601 * LISTBOX_InsertString
1603 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
, LPCWSTR str
)
1605 LPWSTR new_str
= NULL
;
1609 if (HAS_STRINGS(descr
))
1611 static const WCHAR empty_stringW
[] = { 0 };
1612 if (!str
) str
= empty_stringW
;
1613 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1615 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1618 strcpyW(new_str
, str
);
1620 else data
= (ULONG_PTR
)str
;
1622 if (index
== -1) index
= descr
->nb_items
;
1623 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, data
)) != 0)
1625 HeapFree( GetProcessHeap(), 0, new_str
);
1629 TRACE("[%p]: added item %d %s\n",
1630 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1635 /***********************************************************************
1636 * LISTBOX_DeleteItem
1638 * Delete the content of an item. 'index' must be a valid index.
1640 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1642 /* save the item data before it gets freed by LB_RESETCONTENT */
1643 ULONG_PTR item_data
= descr
->items
[index
].data
;
1644 LPWSTR item_str
= descr
->items
[index
].str
;
1646 if (!descr
->nb_items
)
1647 SendMessageW( descr
->self
, LB_RESETCONTENT
, 0, 0 );
1649 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1650 * while Win95 sends it for all items with user data.
1651 * It's probably better to send it too often than not
1652 * often enough, so this is what we do here.
1654 if (IS_OWNERDRAW(descr
) || item_data
)
1656 DELETEITEMSTRUCT dis
;
1657 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1659 dis
.CtlType
= ODT_LISTBOX
;
1662 dis
.hwndItem
= descr
->self
;
1663 dis
.itemData
= item_data
;
1664 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1666 if (HAS_STRINGS(descr
))
1667 HeapFree( GetProcessHeap(), 0, item_str
);
1671 /***********************************************************************
1672 * LISTBOX_RemoveItem
1674 * Remove an item from the listbox and delete its content.
1676 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1681 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1683 /* We need to invalidate the original rect instead of the updated one. */
1684 LISTBOX_InvalidateItems( descr
, index
);
1687 LISTBOX_DeleteItem( descr
, index
);
1689 if (!descr
->nb_items
) return LB_OKAY
;
1691 /* Remove the item */
1693 item
= &descr
->items
[index
];
1694 if (index
< descr
->nb_items
)
1695 RtlMoveMemory( item
, item
+ 1,
1696 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1697 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1699 /* Shrink the item array if possible */
1701 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1702 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1704 max_items
-= LB_ARRAY_GRANULARITY
;
1705 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1706 max_items
* sizeof(LB_ITEMDATA
) );
1707 if (item
) descr
->items
= item
;
1709 /* Repaint the items */
1711 LISTBOX_UpdateScroll( descr
);
1712 /* if we removed the scrollbar, reset the top of the list
1713 (correct for owner-drawn ???) */
1714 if (descr
->nb_items
== descr
->page_size
)
1715 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1717 /* Move selection and focused item */
1718 if (!IS_MULTISELECT(descr
))
1720 if (index
== descr
->selected_item
)
1721 descr
->selected_item
= -1;
1722 else if (index
< descr
->selected_item
)
1724 descr
->selected_item
--;
1725 if (ISWIN31
) /* win 31 do not change the selected item number */
1726 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1730 if (descr
->focus_item
>= descr
->nb_items
)
1732 descr
->focus_item
= descr
->nb_items
- 1;
1733 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1739 /***********************************************************************
1740 * LISTBOX_ResetContent
1742 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1746 for(i
= descr
->nb_items
- 1; i
>=0; i
--) LISTBOX_DeleteItem( descr
, i
);
1747 HeapFree( GetProcessHeap(), 0, descr
->items
);
1748 descr
->nb_items
= 0;
1749 descr
->top_item
= 0;
1750 descr
->selected_item
= -1;
1751 descr
->focus_item
= 0;
1752 descr
->anchor_item
= -1;
1753 descr
->items
= NULL
;
1757 /***********************************************************************
1760 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, INT count
)
1764 if (HAS_STRINGS(descr
))
1766 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1770 /* FIXME: this is far from optimal... */
1771 if (count
> descr
->nb_items
)
1773 while (count
> descr
->nb_items
)
1774 if ((ret
= LISTBOX_InsertString( descr
, -1, 0 )) < 0)
1777 else if (count
< descr
->nb_items
)
1779 while (count
< descr
->nb_items
)
1780 if ((ret
= LISTBOX_RemoveItem( descr
, (descr
->nb_items
- 1) )) < 0)
1784 InvalidateRect( descr
->self
, NULL
, TRUE
);
1789 /***********************************************************************
1792 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1793 LPCWSTR filespec
, BOOL long_names
)
1796 LRESULT ret
= LB_OKAY
;
1797 WIN32_FIND_DATAW entry
;
1799 LRESULT maxinsert
= LB_ERR
;
1801 /* don't scan directory if we just want drives exclusively */
1802 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1803 /* scan directory */
1804 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1806 int le
= GetLastError();
1807 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1814 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1816 static const WCHAR bracketW
[] = { ']',0 };
1817 static const WCHAR dotW
[] = { '.',0 };
1818 if (!(attrib
& DDL_DIRECTORY
) ||
1819 !strcmpW( entry
.cFileName
, dotW
)) continue;
1821 if (!long_names
&& entry
.cAlternateFileName
[0])
1822 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1824 strcpyW( buffer
+ 1, entry
.cFileName
);
1825 strcatW(buffer
, bracketW
);
1827 else /* not a directory */
1829 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1830 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1832 if ((attrib
& DDL_EXCLUSIVE
) &&
1833 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1836 if (!long_names
&& entry
.cAlternateFileName
[0])
1837 strcpyW( buffer
, entry
.cAlternateFileName
);
1839 strcpyW( buffer
, entry
.cFileName
);
1841 if (!long_names
) CharLowerW( buffer
);
1842 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1843 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1845 if (ret
<= maxinsert
) maxinsert
++; else maxinsert
= ret
;
1846 } while (FindNextFileW( handle
, &entry
));
1847 FindClose( handle
);
1855 if (attrib
& DDL_DRIVES
)
1857 WCHAR buffer
[] = {'[','-','a','-',']',0};
1858 WCHAR root
[] = {'A',':','\\',0};
1860 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1862 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1863 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1872 /***********************************************************************
1873 * LISTBOX_HandleVScroll
1875 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1879 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1883 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1886 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1889 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1890 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1893 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1894 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1896 case SB_THUMBPOSITION
:
1897 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1900 info
.cbSize
= sizeof(info
);
1901 info
.fMask
= SIF_TRACKPOS
;
1902 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1903 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1906 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1909 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1916 /***********************************************************************
1917 * LISTBOX_HandleHScroll
1919 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1924 if (descr
->style
& LBS_MULTICOLUMN
)
1929 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
1933 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
1937 page
= descr
->width
/ descr
->column_width
;
1938 if (page
< 1) page
= 1;
1939 LISTBOX_SetTopItem( descr
,
1940 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1943 page
= descr
->width
/ descr
->column_width
;
1944 if (page
< 1) page
= 1;
1945 LISTBOX_SetTopItem( descr
,
1946 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1948 case SB_THUMBPOSITION
:
1949 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
1952 info
.cbSize
= sizeof(info
);
1953 info
.fMask
= SIF_TRACKPOS
;
1954 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1955 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
1959 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1962 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1966 else if (descr
->horz_extent
)
1971 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
1974 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
1977 LISTBOX_SetHorizontalPos( descr
,
1978 descr
->horz_pos
- descr
->width
);
1981 LISTBOX_SetHorizontalPos( descr
,
1982 descr
->horz_pos
+ descr
->width
);
1984 case SB_THUMBPOSITION
:
1985 LISTBOX_SetHorizontalPos( descr
, pos
);
1988 info
.cbSize
= sizeof(info
);
1989 info
.fMask
= SIF_TRACKPOS
;
1990 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
1991 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
1994 LISTBOX_SetHorizontalPos( descr
, 0 );
1997 LISTBOX_SetHorizontalPos( descr
,
1998 descr
->horz_extent
- descr
->width
);
2005 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
2007 UINT pulScrollLines
= 3;
2009 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
2011 /* if scrolling changes direction, ignore left overs */
2012 if ((delta
< 0 && descr
->wheel_remain
< 0) ||
2013 (delta
> 0 && descr
->wheel_remain
> 0))
2014 descr
->wheel_remain
+= delta
;
2016 descr
->wheel_remain
= delta
;
2018 if (descr
->wheel_remain
&& pulScrollLines
)
2021 pulScrollLines
= min((UINT
) descr
->page_size
, pulScrollLines
);
2022 cLineScroll
= pulScrollLines
* (float)descr
->wheel_remain
/ WHEEL_DELTA
;
2023 descr
->wheel_remain
-= WHEEL_DELTA
* cLineScroll
/ (int)pulScrollLines
;
2024 LISTBOX_SetTopItem( descr
, descr
->top_item
- cLineScroll
, TRUE
);
2029 /***********************************************************************
2030 * LISTBOX_HandleLButtonDown
2032 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
2034 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2036 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2037 descr
->self
, x
, y
, index
, descr
->focus_item
);
2039 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2041 if (!descr
->in_focus
)
2043 if( !descr
->lphc
) SetFocus( descr
->self
);
2044 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2047 if (index
== -1) return 0;
2051 if (descr
->style
& LBS_NOTIFY
)
2052 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2053 MAKELPARAM( x
, y
) );
2056 descr
->captured
= TRUE
;
2057 SetCapture( descr
->self
);
2059 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2061 /* we should perhaps make sure that all items are deselected
2062 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2063 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2064 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2067 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2068 if (keys
& MK_CONTROL
)
2070 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2071 LISTBOX_SetSelection( descr
, index
,
2072 !descr
->items
[index
].selected
,
2073 (descr
->style
& LBS_NOTIFY
) != 0);
2077 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2079 if (descr
->style
& LBS_EXTENDEDSEL
)
2081 LISTBOX_SetSelection( descr
, index
,
2082 descr
->items
[index
].selected
,
2083 (descr
->style
& LBS_NOTIFY
) != 0 );
2087 LISTBOX_SetSelection( descr
, index
,
2088 !descr
->items
[index
].selected
,
2089 (descr
->style
& LBS_NOTIFY
) != 0 );
2095 descr
->anchor_item
= index
;
2096 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2097 LISTBOX_SetSelection( descr
, index
,
2098 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2103 if (GetWindowLongW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2110 if (DragDetect( descr
->self
, pt
))
2111 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2118 /*************************************************************************
2119 * LISTBOX_HandleLButtonDownCombo [Internal]
2121 * Process LButtonDown message for the ComboListBox
2124 * pWnd [I] The windows internal structure
2125 * pDescr [I] The ListBox internal structure
2126 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2127 * x [I] X Mouse Coordinate
2128 * y [I] Y Mouse Coordinate
2131 * 0 since we are processing the WM_LBUTTONDOWN Message
2134 * This function is only to be used when a ListBox is a ComboListBox
2137 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2139 RECT clientRect
, screenRect
;
2145 GetClientRect(descr
->self
, &clientRect
);
2147 if(PtInRect(&clientRect
, mousePos
))
2149 /* MousePos is in client, resume normal processing */
2150 if (msg
== WM_LBUTTONDOWN
)
2152 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2153 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2155 else if (descr
->style
& LBS_NOTIFY
)
2156 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2160 POINT screenMousePos
;
2161 HWND hWndOldCapture
;
2163 /* Check the Non-Client Area */
2164 screenMousePos
= mousePos
;
2165 hWndOldCapture
= GetCapture();
2167 GetWindowRect(descr
->self
, &screenRect
);
2168 ClientToScreen(descr
->self
, &screenMousePos
);
2170 if(!PtInRect(&screenRect
, screenMousePos
))
2172 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2173 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2174 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2178 /* Check to see the NC is a scrollbar */
2180 LONG style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2181 /* Check Vertical scroll bar */
2182 if (style
& WS_VSCROLL
)
2184 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2185 if (PtInRect( &clientRect
, mousePos
))
2186 nHitTestType
= HTVSCROLL
;
2188 /* Check horizontal scroll bar */
2189 if (style
& WS_HSCROLL
)
2191 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2192 if (PtInRect( &clientRect
, mousePos
))
2193 nHitTestType
= HTHSCROLL
;
2195 /* Windows sends this message when a scrollbar is clicked
2198 if(nHitTestType
!= 0)
2200 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2201 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2203 /* Resume the Capture after scrolling is complete
2205 if(hWndOldCapture
!= 0)
2206 SetCapture(hWndOldCapture
);
2212 /***********************************************************************
2213 * LISTBOX_HandleLButtonUp
2215 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2217 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2218 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2219 LISTBOX_Timer
= LB_TIMER_NONE
;
2220 if (descr
->captured
)
2222 descr
->captured
= FALSE
;
2223 if (GetCapture() == descr
->self
) ReleaseCapture();
2224 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2225 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2231 /***********************************************************************
2232 * LISTBOX_HandleTimer
2234 * Handle scrolling upon a timer event.
2235 * Return TRUE if scrolling should continue.
2237 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2242 if (descr
->top_item
) index
= descr
->top_item
- 1;
2246 if (descr
->top_item
) index
-= descr
->page_size
;
2249 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2250 if (index
== descr
->focus_item
) index
++;
2251 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2253 case LB_TIMER_RIGHT
:
2254 if (index
+ descr
->page_size
< descr
->nb_items
)
2255 index
+= descr
->page_size
;
2260 if (index
== descr
->focus_item
) return FALSE
;
2261 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2266 /***********************************************************************
2267 * LISTBOX_HandleSystemTimer
2269 * WM_SYSTIMER handler.
2271 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2273 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2275 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2276 LISTBOX_Timer
= LB_TIMER_NONE
;
2282 /***********************************************************************
2283 * LISTBOX_HandleMouseMove
2285 * WM_MOUSEMOVE handler.
2287 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2291 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2293 if (!descr
->captured
) return;
2295 if (descr
->style
& LBS_MULTICOLUMN
)
2298 else if (y
>= descr
->item_height
* descr
->page_size
)
2299 y
= descr
->item_height
* descr
->page_size
- 1;
2303 dir
= LB_TIMER_LEFT
;
2306 else if (x
>= descr
->width
)
2308 dir
= LB_TIMER_RIGHT
;
2309 x
= descr
->width
- 1;
2314 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2315 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2318 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2319 if (index
== -1) index
= descr
->focus_item
;
2320 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2322 /* Start/stop the system timer */
2324 if (dir
!= LB_TIMER_NONE
)
2325 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2326 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2327 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2328 LISTBOX_Timer
= dir
;
2332 /***********************************************************************
2333 * LISTBOX_HandleKeyDown
2335 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2338 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2339 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2340 bForceSelection
= FALSE
; /* only for single select list */
2342 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2344 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2345 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2346 (LPARAM
)descr
->self
);
2347 if (caret
== -2) return 0;
2349 if (caret
== -1) switch(key
)
2352 if (descr
->style
& LBS_MULTICOLUMN
)
2354 bForceSelection
= FALSE
;
2355 if (descr
->focus_item
>= descr
->page_size
)
2356 caret
= descr
->focus_item
- descr
->page_size
;
2361 caret
= descr
->focus_item
- 1;
2362 if (caret
< 0) caret
= 0;
2365 if (descr
->style
& LBS_MULTICOLUMN
)
2367 bForceSelection
= FALSE
;
2368 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2369 caret
= descr
->focus_item
+ descr
->page_size
;
2374 caret
= descr
->focus_item
+ 1;
2375 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2379 if (descr
->style
& LBS_MULTICOLUMN
)
2381 INT page
= descr
->width
/ descr
->column_width
;
2382 if (page
< 1) page
= 1;
2383 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2385 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2386 if (caret
< 0) caret
= 0;
2389 if (descr
->style
& LBS_MULTICOLUMN
)
2391 INT page
= descr
->width
/ descr
->column_width
;
2392 if (page
< 1) page
= 1;
2393 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2395 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2396 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2402 caret
= descr
->nb_items
- 1;
2405 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2406 else if (descr
->style
& LBS_MULTIPLESEL
)
2408 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2409 !descr
->items
[descr
->focus_item
].selected
,
2410 (descr
->style
& LBS_NOTIFY
) != 0 );
2414 bForceSelection
= FALSE
;
2416 if (bForceSelection
) /* focused item is used instead of key */
2417 caret
= descr
->focus_item
;
2420 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2421 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2422 !IS_MULTISELECT(descr
))
2423 descr
->anchor_item
= caret
;
2424 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2426 if (descr
->style
& LBS_MULTIPLESEL
)
2427 descr
->selected_item
= caret
;
2429 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2430 if (descr
->style
& LBS_NOTIFY
)
2432 if (descr
->lphc
&& IsWindowVisible( descr
->self
))
2434 /* make sure that combo parent doesn't hide us */
2435 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2437 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2444 /***********************************************************************
2445 * LISTBOX_HandleChar
2447 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2455 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2457 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2458 MAKEWPARAM(charW
, descr
->focus_item
),
2459 (LPARAM
)descr
->self
);
2460 if (caret
== -2) return 0;
2463 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2466 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2467 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2468 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2469 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2470 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2476 /***********************************************************************
2479 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2482 MEASUREITEMSTRUCT mis
;
2485 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2488 GetClientRect( hwnd
, &rect
);
2490 descr
->owner
= GetParent( descr
->self
);
2491 descr
->style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2492 descr
->width
= rect
.right
- rect
.left
;
2493 descr
->height
= rect
.bottom
- rect
.top
;
2494 descr
->items
= NULL
;
2495 descr
->nb_items
= 0;
2496 descr
->top_item
= 0;
2497 descr
->selected_item
= -1;
2498 descr
->focus_item
= 0;
2499 descr
->anchor_item
= -1;
2500 descr
->item_height
= 1;
2501 descr
->page_size
= 1;
2502 descr
->column_width
= 150;
2503 descr
->horz_extent
= 0;
2504 descr
->horz_pos
= 0;
2507 descr
->wheel_remain
= 0;
2508 descr
->caret_on
= !lphc
;
2509 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2510 descr
->in_focus
= FALSE
;
2511 descr
->captured
= FALSE
;
2513 descr
->locale
= GetUserDefaultLCID();
2518 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2519 descr
->owner
= lphc
->self
;
2522 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2524 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2526 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2527 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2528 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2529 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2531 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2533 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2535 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2536 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2540 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2541 mis
.CtlType
= ODT_LISTBOX
;
2546 mis
.itemHeight
= descr
->item_height
;
2547 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2548 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2552 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2557 /***********************************************************************
2560 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2562 LISTBOX_ResetContent( descr
);
2563 SetWindowLongPtrW( descr
->self
, 0, 0 );
2564 HeapFree( GetProcessHeap(), 0, descr
);
2569 /***********************************************************************
2570 * ListBoxWndProc_common
2572 LRESULT
ListBoxWndProc_common( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2574 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2575 LPHEADCOMBO lphc
= 0;
2580 if (!IsWindow(hwnd
)) return 0;
2582 if (msg
== WM_CREATE
)
2584 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2585 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= lpcs
->lpCreateParams
;
2586 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2587 TRACE("creating hwnd %p descr %p\n", hwnd
, (void *)GetWindowLongPtrW( hwnd
, 0 ) );
2590 /* Ignore all other messages before we get a WM_CREATE */
2591 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2592 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2594 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2596 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2597 descr
->self
, SPY_GetMsgName(msg
, descr
->self
), wParam
, lParam
);
2601 case LB_RESETCONTENT
:
2602 LISTBOX_ResetContent( descr
);
2603 LISTBOX_UpdateScroll( descr
);
2604 InvalidateRect( descr
->self
, NULL
, TRUE
);
2611 if(unicode
|| !HAS_STRINGS(descr
))
2612 textW
= (LPWSTR
)lParam
;
2615 LPSTR textA
= (LPSTR
)lParam
;
2616 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2617 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2618 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2622 wParam
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2623 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2624 if (!unicode
&& HAS_STRINGS(descr
))
2625 HeapFree(GetProcessHeap(), 0, textW
);
2629 case LB_INSERTSTRING
:
2633 if(unicode
|| !HAS_STRINGS(descr
))
2634 textW
= (LPWSTR
)lParam
;
2637 LPSTR textA
= (LPSTR
)lParam
;
2638 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2639 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2640 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2644 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2645 if(!unicode
&& HAS_STRINGS(descr
))
2646 HeapFree(GetProcessHeap(), 0, textW
);
2654 if(unicode
|| !HAS_STRINGS(descr
))
2655 textW
= (LPWSTR
)lParam
;
2658 LPSTR textA
= (LPSTR
)lParam
;
2659 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2660 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2661 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2665 wParam
= LISTBOX_FindFileStrPos( descr
, textW
);
2666 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2667 if(!unicode
&& HAS_STRINGS(descr
))
2668 HeapFree(GetProcessHeap(), 0, textW
);
2672 case LB_DELETESTRING
:
2673 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2674 return descr
->nb_items
;
2677 SetLastError(ERROR_INVALID_INDEX
);
2681 case LB_GETITEMDATA
:
2682 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2684 SetLastError(ERROR_INVALID_INDEX
);
2687 return descr
->items
[wParam
].data
;
2689 case LB_SETITEMDATA
:
2690 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2692 SetLastError(ERROR_INVALID_INDEX
);
2695 descr
->items
[wParam
].data
= lParam
;
2696 /* undocumented: returns TRUE, not LB_OKAY (0) */
2700 return descr
->nb_items
;
2703 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, unicode
);
2706 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2708 SetLastError(ERROR_INVALID_INDEX
);
2711 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2712 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2713 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2714 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2717 if (descr
->nb_items
== 0)
2719 if (!IS_MULTISELECT(descr
))
2720 return descr
->selected_item
;
2721 if (descr
->selected_item
!= -1)
2722 return descr
->selected_item
;
2723 return descr
->focus_item
;
2724 /* otherwise, if the user tries to move the selection with the */
2725 /* arrow keys, we will give the application something to choke on */
2726 case LB_GETTOPINDEX
:
2727 return descr
->top_item
;
2729 case LB_GETITEMHEIGHT
:
2730 return LISTBOX_GetItemHeight( descr
, wParam
);
2732 case LB_SETITEMHEIGHT
:
2733 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2735 case LB_ITEMFROMPOINT
:
2742 /* The hiword of the return value is not a client area
2743 hittest as suggested by MSDN, but rather a hittest on
2744 the returned listbox item. */
2746 if(descr
->nb_items
== 0)
2747 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2749 pt
.x
= (short)LOWORD(lParam
);
2750 pt
.y
= (short)HIWORD(lParam
);
2752 SetRect(&rect
, 0, 0, descr
->width
, descr
->height
);
2754 if(!PtInRect(&rect
, pt
))
2756 pt
.x
= min(pt
.x
, rect
.right
- 1);
2757 pt
.x
= max(pt
.x
, 0);
2758 pt
.y
= min(pt
.y
, rect
.bottom
- 1);
2759 pt
.y
= max(pt
.y
, 0);
2763 index
= LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
);
2767 index
= descr
->nb_items
- 1;
2770 return MAKELONG(index
, hit
? 0 : 1);
2773 case LB_SETCARETINDEX
:
2774 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2775 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2782 case LB_GETCARETINDEX
:
2783 return descr
->focus_item
;
2785 case LB_SETTOPINDEX
:
2786 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2788 case LB_SETCOLUMNWIDTH
:
2789 return LISTBOX_SetColumnWidth( descr
, wParam
);
2791 case LB_GETITEMRECT
:
2792 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2798 if(unicode
|| !HAS_STRINGS(descr
))
2799 textW
= (LPWSTR
)lParam
;
2802 LPSTR textA
= (LPSTR
)lParam
;
2803 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2804 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2805 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2807 ret
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2808 if(!unicode
&& HAS_STRINGS(descr
))
2809 HeapFree(GetProcessHeap(), 0, textW
);
2813 case LB_FINDSTRINGEXACT
:
2817 if(unicode
|| !HAS_STRINGS(descr
))
2818 textW
= (LPWSTR
)lParam
;
2821 LPSTR textA
= (LPSTR
)lParam
;
2822 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2823 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2824 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2826 ret
= LISTBOX_FindString( descr
, wParam
, textW
, TRUE
);
2827 if(!unicode
&& HAS_STRINGS(descr
))
2828 HeapFree(GetProcessHeap(), 0, textW
);
2832 case LB_SELECTSTRING
:
2837 if(HAS_STRINGS(descr
))
2838 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2839 debugstr_a((LPSTR
)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 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2850 if(!unicode
&& HAS_STRINGS(descr
))
2851 HeapFree(GetProcessHeap(), 0, textW
);
2852 if (index
!= LB_ERR
)
2854 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2855 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2861 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2863 return descr
->items
[wParam
].selected
;
2866 return LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2869 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2870 LISTBOX_SetCaretIndex( descr
, wParam
, TRUE
);
2871 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2872 if (ret
!= LB_ERR
) ret
= descr
->selected_item
;
2875 case LB_GETSELCOUNT
:
2876 return LISTBOX_GetSelCount( descr
);
2878 case LB_GETSELITEMS
:
2879 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2881 case LB_SELITEMRANGE
:
2882 if (LOWORD(lParam
) <= HIWORD(lParam
))
2883 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2884 HIWORD(lParam
), wParam
);
2886 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2887 LOWORD(lParam
), wParam
);
2889 case LB_SELITEMRANGEEX
:
2890 if ((INT
)lParam
>= (INT
)wParam
)
2891 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2893 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
2895 case LB_GETHORIZONTALEXTENT
:
2896 return descr
->horz_extent
;
2898 case LB_SETHORIZONTALEXTENT
:
2899 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
2901 case LB_GETANCHORINDEX
:
2902 return descr
->anchor_item
;
2904 case LB_SETANCHORINDEX
:
2905 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2907 SetLastError(ERROR_INVALID_INDEX
);
2910 descr
->anchor_item
= (INT
)wParam
;
2918 textW
= (LPWSTR
)lParam
;
2921 LPSTR textA
= (LPSTR
)lParam
;
2922 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2923 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2924 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2926 ret
= LISTBOX_Directory( descr
, wParam
, textW
, msg
== LB_DIR
);
2928 HeapFree(GetProcessHeap(), 0, textW
);
2933 return descr
->locale
;
2938 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
2940 ret
= descr
->locale
;
2941 descr
->locale
= (LCID
)wParam
;
2945 case LB_INITSTORAGE
:
2946 return LISTBOX_InitStorage( descr
, wParam
);
2949 return LISTBOX_SetCount( descr
, (INT
)wParam
);
2951 case LB_SETTABSTOPS
:
2952 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
);
2955 if (descr
->caret_on
)
2957 descr
->caret_on
= TRUE
;
2958 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2959 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2963 if (!descr
->caret_on
)
2965 descr
->caret_on
= FALSE
;
2966 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2967 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2970 case LB_GETLISTBOXINFO
:
2971 return descr
->page_size
;
2974 return LISTBOX_Destroy( descr
);
2977 InvalidateRect( descr
->self
, NULL
, TRUE
);
2981 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
2985 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2987 case WM_PRINTCLIENT
:
2991 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
2992 ret
= LISTBOX_Paint( descr
, hdc
);
2993 if( !wParam
) EndPaint( descr
->self
, &ps
);
2997 LISTBOX_UpdateSize( descr
);
3000 return (LRESULT
)descr
->font
;
3002 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
3003 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
3006 descr
->in_focus
= TRUE
;
3007 descr
->caret_on
= TRUE
;
3008 if (descr
->focus_item
!= -1)
3009 LISTBOX_DrawFocusRect( descr
, TRUE
);
3010 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
3013 LISTBOX_HandleLButtonUp( descr
); /* Release capture if we have it */
3014 descr
->in_focus
= FALSE
;
3015 descr
->wheel_remain
= 0;
3016 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
3017 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3018 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
3021 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3023 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3025 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
3026 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
3027 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
3028 case WM_LBUTTONDOWN
:
3030 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3031 (INT16
)LOWORD(lParam
),
3032 (INT16
)HIWORD(lParam
) );
3033 return LISTBOX_HandleLButtonDown( descr
, wParam
,
3034 (INT16
)LOWORD(lParam
),
3035 (INT16
)HIWORD(lParam
) );
3036 case WM_LBUTTONDBLCLK
:
3038 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3039 (INT16
)LOWORD(lParam
),
3040 (INT16
)HIWORD(lParam
) );
3041 if (descr
->style
& LBS_NOTIFY
)
3042 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
3045 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
3047 BOOL captured
= descr
->captured
;
3051 mousePos
.x
= (INT16
)LOWORD(lParam
);
3052 mousePos
.y
= (INT16
)HIWORD(lParam
);
3055 * If we are in a dropdown combobox, we simulate that
3056 * the mouse is captured to show the tracking of the item.
3058 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
3059 descr
->captured
= TRUE
;
3061 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
3063 descr
->captured
= captured
;
3065 else if (GetCapture() == descr
->self
)
3067 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
3068 (INT16
)HIWORD(lParam
) );
3078 * If the mouse button "up" is not in the listbox,
3079 * we make sure there is no selection by re-selecting the
3080 * item that was selected when the listbox was made visible.
3082 mousePos
.x
= (INT16
)LOWORD(lParam
);
3083 mousePos
.y
= (INT16
)HIWORD(lParam
);
3085 GetClientRect(descr
->self
, &clientRect
);
3088 * When the user clicks outside the combobox and the focus
3089 * is lost, the owning combobox will send a fake buttonup with
3090 * 0xFFFFFFF as the mouse location, we must also revert the
3091 * selection to the original selection.
3093 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
3094 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
3096 return LISTBOX_HandleLButtonUp( descr
);
3098 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3100 /* for some reason Windows makes it possible to
3101 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3103 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3104 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3105 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3107 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3111 return LISTBOX_HandleKeyDown( descr
, wParam
);
3116 charW
= (WCHAR
)wParam
;
3119 CHAR charA
= (CHAR
)wParam
;
3120 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
3122 return LISTBOX_HandleChar( descr
, charW
);
3125 return LISTBOX_HandleSystemTimer( descr
);
3127 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3130 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3131 wParam
, (LPARAM
)descr
->self
);
3132 TRACE("hbrush = %p\n", hbrush
);
3134 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3137 GetClientRect(descr
->self
, &rect
);
3138 FillRect((HDC
)wParam
, &rect
, hbrush
);
3143 if( lphc
) return 0;
3144 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3145 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3148 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3157 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3158 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3159 hwnd
, msg
, wParam
, lParam
);
3162 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3163 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3166 DWORD WINAPI
GetListBoxInfo(HWND hwnd
)
3168 TRACE("%p\n", hwnd
);
3169 return SendMessageW(hwnd
, LB_GETLISTBOXINFO
, 0, 0);