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
))
420 memset(rect
, 0, sizeof(*rect
));
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 if (i
== descr
->focus_item
)
1064 /* keep the focus rect, to paint the focus item after */
1065 focusRect
.left
= rect
.left
;
1066 focusRect
.right
= rect
.right
;
1067 focusRect
.top
= rect
.top
;
1068 focusRect
.bottom
= rect
.bottom
;
1070 LISTBOX_PaintItem( descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1071 rect
.top
= rect
.bottom
;
1073 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1075 if (!IS_OWNERDRAW(descr
))
1077 /* Clear the bottom of the column */
1078 if (rect
.top
< descr
->height
)
1080 rect
.bottom
= descr
->height
;
1081 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1082 &rect
, NULL
, 0, NULL
);
1086 /* Go to the next column */
1087 rect
.left
+= descr
->column_width
;
1088 rect
.right
+= descr
->column_width
;
1090 col_pos
= descr
->page_size
- 1;
1095 if (rect
.top
>= descr
->height
) break;
1099 /* Paint the focus item now */
1100 if (focusRect
.top
!= focusRect
.bottom
&&
1101 descr
->caret_on
&& descr
->in_focus
)
1102 LISTBOX_PaintItem( descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1104 if (!IS_OWNERDRAW(descr
))
1106 /* Clear the remainder of the client area */
1107 if (rect
.top
< descr
->height
)
1109 rect
.bottom
= descr
->height
;
1110 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1111 &rect
, NULL
, 0, NULL
);
1113 if (rect
.right
< descr
->width
)
1115 rect
.left
= rect
.right
;
1116 rect
.right
= descr
->width
;
1118 rect
.bottom
= descr
->height
;
1119 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1120 &rect
, NULL
, 0, NULL
);
1123 if (oldFont
) SelectObject( hdc
, oldFont
);
1124 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1129 /***********************************************************************
1130 * LISTBOX_InvalidateItems
1132 * Invalidate all items from a given item. If the specified item is not
1133 * visible, nothing happens.
1135 static void LISTBOX_InvalidateItems( LB_DESCR
*descr
, INT index
)
1139 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1141 if (descr
->style
& LBS_NOREDRAW
)
1143 descr
->style
|= LBS_DISPLAYCHANGED
;
1146 rect
.bottom
= descr
->height
;
1147 InvalidateRect( descr
->self
, &rect
, TRUE
);
1148 if (descr
->style
& LBS_MULTICOLUMN
)
1150 /* Repaint the other columns */
1151 rect
.left
= rect
.right
;
1152 rect
.right
= descr
->width
;
1154 InvalidateRect( descr
->self
, &rect
, TRUE
);
1159 static void LISTBOX_InvalidateItemRect( LB_DESCR
*descr
, INT index
)
1163 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1164 InvalidateRect( descr
->self
, &rect
, TRUE
);
1167 /***********************************************************************
1168 * LISTBOX_GetItemHeight
1170 static LRESULT
LISTBOX_GetItemHeight( const LB_DESCR
*descr
, INT index
)
1172 if (descr
->style
& LBS_OWNERDRAWVARIABLE
&& descr
->nb_items
> 0)
1174 if ((index
< 0) || (index
>= descr
->nb_items
))
1176 SetLastError(ERROR_INVALID_INDEX
);
1179 return descr
->items
[index
].height
;
1181 else return descr
->item_height
;
1185 /***********************************************************************
1186 * LISTBOX_SetItemHeight
1188 static LRESULT
LISTBOX_SetItemHeight( LB_DESCR
*descr
, INT index
, INT height
, BOOL repaint
)
1190 if (height
> MAXBYTE
)
1193 if (!height
) height
= 1;
1195 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1197 if ((index
< 0) || (index
>= descr
->nb_items
))
1199 SetLastError(ERROR_INVALID_INDEX
);
1202 TRACE("[%p]: item %d height = %d\n", descr
->self
, index
, height
);
1203 descr
->items
[index
].height
= height
;
1204 LISTBOX_UpdateScroll( descr
);
1206 LISTBOX_InvalidateItems( descr
, index
);
1208 else if (height
!= descr
->item_height
)
1210 TRACE("[%p]: new height = %d\n", descr
->self
, height
);
1211 descr
->item_height
= height
;
1212 LISTBOX_UpdatePage( descr
);
1213 LISTBOX_UpdateScroll( descr
);
1215 InvalidateRect( descr
->self
, 0, TRUE
);
1221 /***********************************************************************
1222 * LISTBOX_SetHorizontalPos
1224 static void LISTBOX_SetHorizontalPos( LB_DESCR
*descr
, INT pos
)
1228 if (pos
> descr
->horz_extent
- descr
->width
)
1229 pos
= descr
->horz_extent
- descr
->width
;
1230 if (pos
< 0) pos
= 0;
1231 if (!(diff
= descr
->horz_pos
- pos
)) return;
1232 TRACE("[%p]: new horz pos = %d\n", descr
->self
, pos
);
1233 descr
->horz_pos
= pos
;
1234 LISTBOX_UpdateScroll( descr
);
1235 if (abs(diff
) < descr
->width
)
1238 /* Invalidate the focused item so it will be repainted correctly */
1239 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1240 InvalidateRect( descr
->self
, &rect
, TRUE
);
1241 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
1242 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1245 InvalidateRect( descr
->self
, NULL
, TRUE
);
1249 /***********************************************************************
1250 * LISTBOX_SetHorizontalExtent
1252 static LRESULT
LISTBOX_SetHorizontalExtent( LB_DESCR
*descr
, INT extent
)
1254 if (descr
->style
& LBS_MULTICOLUMN
)
1256 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1257 TRACE("[%p]: new horz extent = %d\n", descr
->self
, extent
);
1258 descr
->horz_extent
= extent
;
1259 if (descr
->style
& WS_HSCROLL
) {
1261 info
.cbSize
= sizeof(info
);
1263 info
.nMax
= descr
->horz_extent
? descr
->horz_extent
- 1 : 0;
1264 info
.fMask
= SIF_RANGE
;
1265 if (descr
->style
& LBS_DISABLENOSCROLL
)
1266 info
.fMask
|= SIF_DISABLENOSCROLL
;
1267 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
1269 if (descr
->horz_pos
> extent
- descr
->width
)
1270 LISTBOX_SetHorizontalPos( descr
, extent
- descr
->width
);
1275 /***********************************************************************
1276 * LISTBOX_SetColumnWidth
1278 static LRESULT
LISTBOX_SetColumnWidth( LB_DESCR
*descr
, INT width
)
1280 if (width
== descr
->column_width
) return LB_OKAY
;
1281 TRACE("[%p]: new column width = %d\n", descr
->self
, width
);
1282 descr
->column_width
= width
;
1283 LISTBOX_UpdatePage( descr
);
1288 /***********************************************************************
1291 * Returns the item height.
1293 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1297 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1302 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1304 ERR("unable to get DC.\n" );
1307 if (font
) oldFont
= SelectObject( hdc
, font
);
1308 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1309 if (oldFont
) SelectObject( hdc
, oldFont
);
1310 ReleaseDC( descr
->self
, hdc
);
1312 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1313 if (!IS_OWNERDRAW(descr
))
1314 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1319 /***********************************************************************
1320 * LISTBOX_MakeItemVisible
1322 * Make sure that a given item is partially or fully visible.
1324 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1328 TRACE("current top item %d, index %d, fully %d\n", descr
->top_item
, index
, fully
);
1330 if (index
<= descr
->top_item
) top
= index
;
1331 else if (descr
->style
& LBS_MULTICOLUMN
)
1333 INT cols
= descr
->width
;
1334 if (!fully
) cols
+= descr
->column_width
- 1;
1335 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1337 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1338 top
= index
- descr
->page_size
* (cols
- 1);
1340 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1342 INT height
= fully
? descr
->items
[index
].height
: 1;
1343 for (top
= index
; top
> descr
->top_item
; top
--)
1344 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1348 if (index
< descr
->top_item
+ descr
->page_size
) return;
1349 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1350 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1351 top
= index
- descr
->page_size
+ 1;
1353 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1356 /***********************************************************************
1357 * LISTBOX_SetCaretIndex
1360 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1363 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1365 INT oldfocus
= descr
->focus_item
;
1367 TRACE("old focus %d, index %d\n", oldfocus
, index
);
1369 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1370 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1371 if (index
== oldfocus
) return LB_OKAY
;
1373 LISTBOX_DrawFocusRect( descr
, FALSE
);
1374 descr
->focus_item
= index
;
1376 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1377 LISTBOX_DrawFocusRect( descr
, TRUE
);
1383 /***********************************************************************
1384 * LISTBOX_SelectItemRange
1386 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1388 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1393 /* A few sanity checks */
1395 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1396 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1398 if (!descr
->nb_items
) return LB_OKAY
;
1400 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1401 if (first
< 0) first
= 0;
1402 if (last
< first
) return LB_OKAY
;
1404 if (on
) /* Turn selection on */
1406 for (i
= first
; i
<= last
; i
++)
1408 if (descr
->items
[i
].selected
) continue;
1409 descr
->items
[i
].selected
= TRUE
;
1410 LISTBOX_InvalidateItemRect(descr
, i
);
1413 else /* Turn selection off */
1415 for (i
= first
; i
<= last
; i
++)
1417 if (!descr
->items
[i
].selected
) continue;
1418 descr
->items
[i
].selected
= FALSE
;
1419 LISTBOX_InvalidateItemRect(descr
, i
);
1425 /***********************************************************************
1426 * LISTBOX_SetSelection
1428 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1429 BOOL on
, BOOL send_notify
)
1431 TRACE( "cur_sel=%d index=%d notify=%s\n",
1432 descr
->selected_item
, index
, send_notify
? "YES" : "NO" );
1434 if (descr
->style
& LBS_NOSEL
)
1436 descr
->selected_item
= index
;
1439 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1440 if (descr
->style
& LBS_MULTIPLESEL
)
1442 if (index
== -1) /* Select all items */
1443 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1444 else /* Only one item */
1445 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1449 INT oldsel
= descr
->selected_item
;
1450 if (index
== oldsel
) return LB_OKAY
;
1451 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1452 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1453 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1454 descr
->selected_item
= index
;
1455 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1456 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1457 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1459 if( descr
->lphc
) /* set selection change flag for parent combo */
1460 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1466 /***********************************************************************
1469 * Change the caret position and extend the selection to the new caret.
1471 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1473 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1475 if ((index
< 0) || (index
>= descr
->nb_items
))
1478 /* Important, repaint needs to be done in this order if
1479 you want to mimic Windows behavior:
1480 1. Remove the focus and paint the item
1481 2. Remove the selection and paint the item(s)
1482 3. Set the selection and repaint the item(s)
1483 4. Set the focus to 'index' and repaint the item */
1485 /* 1. remove the focus and repaint the item */
1486 LISTBOX_DrawFocusRect( descr
, FALSE
);
1488 /* 2. then turn off the previous selection */
1489 /* 3. repaint the new selected item */
1490 if (descr
->style
& LBS_EXTENDEDSEL
)
1492 if (descr
->anchor_item
!= -1)
1494 INT first
= min( index
, descr
->anchor_item
);
1495 INT last
= max( index
, descr
->anchor_item
);
1497 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1498 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1499 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1502 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1504 /* Set selection to new caret item */
1505 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1508 /* 4. repaint the new item with the focus */
1509 descr
->focus_item
= index
;
1510 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1511 LISTBOX_DrawFocusRect( descr
, TRUE
);
1515 /***********************************************************************
1516 * LISTBOX_InsertItem
1518 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1519 LPWSTR str
, ULONG_PTR data
)
1523 INT oldfocus
= descr
->focus_item
;
1525 if (index
== -1) index
= descr
->nb_items
;
1526 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1527 if (!descr
->items
) max_items
= 0;
1528 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1529 if (descr
->nb_items
== max_items
)
1531 /* We need to grow the array */
1532 max_items
+= LB_ARRAY_GRANULARITY
;
1534 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1535 max_items
* sizeof(LB_ITEMDATA
) );
1537 item
= HeapAlloc( GetProcessHeap(), 0,
1538 max_items
* sizeof(LB_ITEMDATA
) );
1541 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1544 descr
->items
= item
;
1547 /* Insert the item structure */
1549 item
= &descr
->items
[index
];
1550 if (index
< descr
->nb_items
)
1551 RtlMoveMemory( item
+ 1, item
,
1552 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1556 item
->selected
= FALSE
;
1559 /* Get item height */
1561 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1563 MEASUREITEMSTRUCT mis
;
1564 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1566 mis
.CtlType
= ODT_LISTBOX
;
1569 mis
.itemData
= descr
->items
[index
].data
;
1570 mis
.itemHeight
= descr
->item_height
;
1571 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1572 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1573 TRACE("[%p]: measure item %d (%s) = %d\n",
1574 descr
->self
, index
, str
? debugstr_w(str
) : "", item
->height
);
1577 /* Repaint the items */
1579 LISTBOX_UpdateScroll( descr
);
1580 LISTBOX_InvalidateItems( descr
, index
);
1582 /* Move selection and focused item */
1583 /* If listbox was empty, set focus to the first item */
1584 if (descr
->nb_items
== 1)
1585 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1586 /* single select don't change selection index in win31 */
1587 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1589 descr
->selected_item
++;
1590 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1594 if (index
<= descr
->selected_item
)
1596 descr
->selected_item
++;
1597 descr
->focus_item
= oldfocus
; /* focus not changed */
1604 /***********************************************************************
1605 * LISTBOX_InsertString
1607 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
, LPCWSTR str
)
1609 LPWSTR new_str
= NULL
;
1613 if (HAS_STRINGS(descr
))
1615 static const WCHAR empty_stringW
[] = { 0 };
1616 if (!str
) str
= empty_stringW
;
1617 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1619 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1622 strcpyW(new_str
, str
);
1624 else data
= (ULONG_PTR
)str
;
1626 if (index
== -1) index
= descr
->nb_items
;
1627 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, data
)) != 0)
1629 HeapFree( GetProcessHeap(), 0, new_str
);
1633 TRACE("[%p]: added item %d %s\n",
1634 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1639 /***********************************************************************
1640 * LISTBOX_DeleteItem
1642 * Delete the content of an item. 'index' must be a valid index.
1644 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1646 /* save the item data before it gets freed by LB_RESETCONTENT */
1647 ULONG_PTR item_data
= descr
->items
[index
].data
;
1648 LPWSTR item_str
= descr
->items
[index
].str
;
1650 if (!descr
->nb_items
)
1651 SendMessageW( descr
->self
, LB_RESETCONTENT
, 0, 0 );
1653 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1654 * while Win95 sends it for all items with user data.
1655 * It's probably better to send it too often than not
1656 * often enough, so this is what we do here.
1658 if (IS_OWNERDRAW(descr
) || item_data
)
1660 DELETEITEMSTRUCT dis
;
1661 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1663 dis
.CtlType
= ODT_LISTBOX
;
1666 dis
.hwndItem
= descr
->self
;
1667 dis
.itemData
= item_data
;
1668 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1670 if (HAS_STRINGS(descr
))
1671 HeapFree( GetProcessHeap(), 0, item_str
);
1675 /***********************************************************************
1676 * LISTBOX_RemoveItem
1678 * Remove an item from the listbox and delete its content.
1680 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1685 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1687 /* We need to invalidate the original rect instead of the updated one. */
1688 LISTBOX_InvalidateItems( descr
, index
);
1691 LISTBOX_DeleteItem( descr
, index
);
1693 if (!descr
->nb_items
) return LB_OKAY
;
1695 /* Remove the item */
1697 item
= &descr
->items
[index
];
1698 if (index
< descr
->nb_items
)
1699 RtlMoveMemory( item
, item
+ 1,
1700 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1701 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1703 /* Shrink the item array if possible */
1705 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1706 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1708 max_items
-= LB_ARRAY_GRANULARITY
;
1709 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1710 max_items
* sizeof(LB_ITEMDATA
) );
1711 if (item
) descr
->items
= item
;
1713 /* Repaint the items */
1715 LISTBOX_UpdateScroll( descr
);
1716 /* if we removed the scrollbar, reset the top of the list
1717 (correct for owner-drawn ???) */
1718 if (descr
->nb_items
== descr
->page_size
)
1719 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1721 /* Move selection and focused item */
1722 if (!IS_MULTISELECT(descr
))
1724 if (index
== descr
->selected_item
)
1725 descr
->selected_item
= -1;
1726 else if (index
< descr
->selected_item
)
1728 descr
->selected_item
--;
1729 if (ISWIN31
) /* win 31 do not change the selected item number */
1730 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1734 if (descr
->focus_item
>= descr
->nb_items
)
1736 descr
->focus_item
= descr
->nb_items
- 1;
1737 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1743 /***********************************************************************
1744 * LISTBOX_ResetContent
1746 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1750 for(i
= descr
->nb_items
- 1; i
>=0; i
--) LISTBOX_DeleteItem( descr
, i
);
1751 HeapFree( GetProcessHeap(), 0, descr
->items
);
1752 descr
->nb_items
= 0;
1753 descr
->top_item
= 0;
1754 descr
->selected_item
= -1;
1755 descr
->focus_item
= 0;
1756 descr
->anchor_item
= -1;
1757 descr
->items
= NULL
;
1761 /***********************************************************************
1764 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, INT count
)
1768 if (HAS_STRINGS(descr
))
1770 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1774 /* FIXME: this is far from optimal... */
1775 if (count
> descr
->nb_items
)
1777 while (count
> descr
->nb_items
)
1778 if ((ret
= LISTBOX_InsertString( descr
, -1, 0 )) < 0)
1781 else if (count
< descr
->nb_items
)
1783 while (count
< descr
->nb_items
)
1784 if ((ret
= LISTBOX_RemoveItem( descr
, (descr
->nb_items
- 1) )) < 0)
1788 InvalidateRect( descr
->self
, NULL
, TRUE
);
1793 /***********************************************************************
1796 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1797 LPCWSTR filespec
, BOOL long_names
)
1800 LRESULT ret
= LB_OKAY
;
1801 WIN32_FIND_DATAW entry
;
1803 LRESULT maxinsert
= LB_ERR
;
1805 /* don't scan directory if we just want drives exclusively */
1806 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1807 /* scan directory */
1808 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1810 int le
= GetLastError();
1811 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1818 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1820 static const WCHAR bracketW
[] = { ']',0 };
1821 static const WCHAR dotW
[] = { '.',0 };
1822 if (!(attrib
& DDL_DIRECTORY
) ||
1823 !strcmpW( entry
.cFileName
, dotW
)) continue;
1825 if (!long_names
&& entry
.cAlternateFileName
[0])
1826 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1828 strcpyW( buffer
+ 1, entry
.cFileName
);
1829 strcatW(buffer
, bracketW
);
1831 else /* not a directory */
1833 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1834 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1836 if ((attrib
& DDL_EXCLUSIVE
) &&
1837 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1840 if (!long_names
&& entry
.cAlternateFileName
[0])
1841 strcpyW( buffer
, entry
.cAlternateFileName
);
1843 strcpyW( buffer
, entry
.cFileName
);
1845 if (!long_names
) CharLowerW( buffer
);
1846 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1847 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1849 if (ret
<= maxinsert
) maxinsert
++; else maxinsert
= ret
;
1850 } while (FindNextFileW( handle
, &entry
));
1851 FindClose( handle
);
1859 if (attrib
& DDL_DRIVES
)
1861 WCHAR buffer
[] = {'[','-','a','-',']',0};
1862 WCHAR root
[] = {'A',':','\\',0};
1864 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1866 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1867 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1876 /***********************************************************************
1877 * LISTBOX_HandleVScroll
1879 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1883 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1887 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1890 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1893 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1894 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1897 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1898 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1900 case SB_THUMBPOSITION
:
1901 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1904 info
.cbSize
= sizeof(info
);
1905 info
.fMask
= SIF_TRACKPOS
;
1906 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1907 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1910 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1913 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1920 /***********************************************************************
1921 * LISTBOX_HandleHScroll
1923 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1928 if (descr
->style
& LBS_MULTICOLUMN
)
1933 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
1937 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
1941 page
= descr
->width
/ descr
->column_width
;
1942 if (page
< 1) page
= 1;
1943 LISTBOX_SetTopItem( descr
,
1944 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1947 page
= descr
->width
/ descr
->column_width
;
1948 if (page
< 1) page
= 1;
1949 LISTBOX_SetTopItem( descr
,
1950 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1952 case SB_THUMBPOSITION
:
1953 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
1956 info
.cbSize
= sizeof(info
);
1957 info
.fMask
= SIF_TRACKPOS
;
1958 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1959 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
1963 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1966 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1970 else if (descr
->horz_extent
)
1975 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
1978 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
1981 LISTBOX_SetHorizontalPos( descr
,
1982 descr
->horz_pos
- descr
->width
);
1985 LISTBOX_SetHorizontalPos( descr
,
1986 descr
->horz_pos
+ descr
->width
);
1988 case SB_THUMBPOSITION
:
1989 LISTBOX_SetHorizontalPos( descr
, pos
);
1992 info
.cbSize
= sizeof(info
);
1993 info
.fMask
= SIF_TRACKPOS
;
1994 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
1995 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
1998 LISTBOX_SetHorizontalPos( descr
, 0 );
2001 LISTBOX_SetHorizontalPos( descr
,
2002 descr
->horz_extent
- descr
->width
);
2009 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
2011 UINT pulScrollLines
= 3;
2013 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
2015 /* if scrolling changes direction, ignore left overs */
2016 if ((delta
< 0 && descr
->wheel_remain
< 0) ||
2017 (delta
> 0 && descr
->wheel_remain
> 0))
2018 descr
->wheel_remain
+= delta
;
2020 descr
->wheel_remain
= delta
;
2022 if (descr
->wheel_remain
&& pulScrollLines
)
2025 pulScrollLines
= min((UINT
) descr
->page_size
, pulScrollLines
);
2026 cLineScroll
= pulScrollLines
* (float)descr
->wheel_remain
/ WHEEL_DELTA
;
2027 descr
->wheel_remain
-= WHEEL_DELTA
* cLineScroll
/ (int)pulScrollLines
;
2028 LISTBOX_SetTopItem( descr
, descr
->top_item
- cLineScroll
, TRUE
);
2033 /***********************************************************************
2034 * LISTBOX_HandleLButtonDown
2036 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
2038 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2040 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2041 descr
->self
, x
, y
, index
, descr
->focus_item
);
2043 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2045 if (!descr
->in_focus
)
2047 if( !descr
->lphc
) SetFocus( descr
->self
);
2048 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2051 if (index
== -1) return 0;
2055 if (descr
->style
& LBS_NOTIFY
)
2056 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2057 MAKELPARAM( x
, y
) );
2060 descr
->captured
= TRUE
;
2061 SetCapture( descr
->self
);
2063 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2065 /* we should perhaps make sure that all items are deselected
2066 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2067 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2068 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2071 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2072 if (keys
& MK_CONTROL
)
2074 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2075 LISTBOX_SetSelection( descr
, index
,
2076 !descr
->items
[index
].selected
,
2077 (descr
->style
& LBS_NOTIFY
) != 0);
2081 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2083 if (descr
->style
& LBS_EXTENDEDSEL
)
2085 LISTBOX_SetSelection( descr
, index
,
2086 descr
->items
[index
].selected
,
2087 (descr
->style
& LBS_NOTIFY
) != 0 );
2091 LISTBOX_SetSelection( descr
, index
,
2092 !descr
->items
[index
].selected
,
2093 (descr
->style
& LBS_NOTIFY
) != 0 );
2099 descr
->anchor_item
= index
;
2100 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2101 LISTBOX_SetSelection( descr
, index
,
2102 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2107 if (GetWindowLongW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2114 if (DragDetect( descr
->self
, pt
))
2115 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2122 /*************************************************************************
2123 * LISTBOX_HandleLButtonDownCombo [Internal]
2125 * Process LButtonDown message for the ComboListBox
2128 * pWnd [I] The windows internal structure
2129 * pDescr [I] The ListBox internal structure
2130 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2131 * x [I] X Mouse Coordinate
2132 * y [I] Y Mouse Coordinate
2135 * 0 since we are processing the WM_LBUTTONDOWN Message
2138 * This function is only to be used when a ListBox is a ComboListBox
2141 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2143 RECT clientRect
, screenRect
;
2149 GetClientRect(descr
->self
, &clientRect
);
2151 if(PtInRect(&clientRect
, mousePos
))
2153 /* MousePos is in client, resume normal processing */
2154 if (msg
== WM_LBUTTONDOWN
)
2156 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2157 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2159 else if (descr
->style
& LBS_NOTIFY
)
2160 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2164 POINT screenMousePos
;
2165 HWND hWndOldCapture
;
2167 /* Check the Non-Client Area */
2168 screenMousePos
= mousePos
;
2169 hWndOldCapture
= GetCapture();
2171 GetWindowRect(descr
->self
, &screenRect
);
2172 ClientToScreen(descr
->self
, &screenMousePos
);
2174 if(!PtInRect(&screenRect
, screenMousePos
))
2176 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2177 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2178 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2182 /* Check to see the NC is a scrollbar */
2184 LONG style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2185 /* Check Vertical scroll bar */
2186 if (style
& WS_VSCROLL
)
2188 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2189 if (PtInRect( &clientRect
, mousePos
))
2190 nHitTestType
= HTVSCROLL
;
2192 /* Check horizontal scroll bar */
2193 if (style
& WS_HSCROLL
)
2195 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2196 if (PtInRect( &clientRect
, mousePos
))
2197 nHitTestType
= HTHSCROLL
;
2199 /* Windows sends this message when a scrollbar is clicked
2202 if(nHitTestType
!= 0)
2204 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2205 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2207 /* Resume the Capture after scrolling is complete
2209 if(hWndOldCapture
!= 0)
2210 SetCapture(hWndOldCapture
);
2216 /***********************************************************************
2217 * LISTBOX_HandleLButtonUp
2219 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2221 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2222 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2223 LISTBOX_Timer
= LB_TIMER_NONE
;
2224 if (descr
->captured
)
2226 descr
->captured
= FALSE
;
2227 if (GetCapture() == descr
->self
) ReleaseCapture();
2228 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2229 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2235 /***********************************************************************
2236 * LISTBOX_HandleTimer
2238 * Handle scrolling upon a timer event.
2239 * Return TRUE if scrolling should continue.
2241 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2246 if (descr
->top_item
) index
= descr
->top_item
- 1;
2250 if (descr
->top_item
) index
-= descr
->page_size
;
2253 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2254 if (index
== descr
->focus_item
) index
++;
2255 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2257 case LB_TIMER_RIGHT
:
2258 if (index
+ descr
->page_size
< descr
->nb_items
)
2259 index
+= descr
->page_size
;
2264 if (index
== descr
->focus_item
) return FALSE
;
2265 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2270 /***********************************************************************
2271 * LISTBOX_HandleSystemTimer
2273 * WM_SYSTIMER handler.
2275 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2277 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2279 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2280 LISTBOX_Timer
= LB_TIMER_NONE
;
2286 /***********************************************************************
2287 * LISTBOX_HandleMouseMove
2289 * WM_MOUSEMOVE handler.
2291 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2295 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2297 if (!descr
->captured
) return;
2299 if (descr
->style
& LBS_MULTICOLUMN
)
2302 else if (y
>= descr
->item_height
* descr
->page_size
)
2303 y
= descr
->item_height
* descr
->page_size
- 1;
2307 dir
= LB_TIMER_LEFT
;
2310 else if (x
>= descr
->width
)
2312 dir
= LB_TIMER_RIGHT
;
2313 x
= descr
->width
- 1;
2318 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2319 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2322 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2323 if (index
== -1) index
= descr
->focus_item
;
2324 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2326 /* Start/stop the system timer */
2328 if (dir
!= LB_TIMER_NONE
)
2329 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2330 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2331 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2332 LISTBOX_Timer
= dir
;
2336 /***********************************************************************
2337 * LISTBOX_HandleKeyDown
2339 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2342 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2343 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2344 bForceSelection
= FALSE
; /* only for single select list */
2346 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2348 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2349 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2350 (LPARAM
)descr
->self
);
2351 if (caret
== -2) return 0;
2353 if (caret
== -1) switch(key
)
2356 if (descr
->style
& LBS_MULTICOLUMN
)
2358 bForceSelection
= FALSE
;
2359 if (descr
->focus_item
>= descr
->page_size
)
2360 caret
= descr
->focus_item
- descr
->page_size
;
2365 caret
= descr
->focus_item
- 1;
2366 if (caret
< 0) caret
= 0;
2369 if (descr
->style
& LBS_MULTICOLUMN
)
2371 bForceSelection
= FALSE
;
2372 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2373 caret
= descr
->focus_item
+ descr
->page_size
;
2378 caret
= descr
->focus_item
+ 1;
2379 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2383 if (descr
->style
& LBS_MULTICOLUMN
)
2385 INT page
= descr
->width
/ descr
->column_width
;
2386 if (page
< 1) page
= 1;
2387 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2389 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2390 if (caret
< 0) caret
= 0;
2393 if (descr
->style
& LBS_MULTICOLUMN
)
2395 INT page
= descr
->width
/ descr
->column_width
;
2396 if (page
< 1) page
= 1;
2397 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2399 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2400 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2406 caret
= descr
->nb_items
- 1;
2409 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2410 else if (descr
->style
& LBS_MULTIPLESEL
)
2412 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2413 !descr
->items
[descr
->focus_item
].selected
,
2414 (descr
->style
& LBS_NOTIFY
) != 0 );
2418 bForceSelection
= FALSE
;
2420 if (bForceSelection
) /* focused item is used instead of key */
2421 caret
= descr
->focus_item
;
2424 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2425 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2426 !IS_MULTISELECT(descr
))
2427 descr
->anchor_item
= caret
;
2428 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2430 if (descr
->style
& LBS_MULTIPLESEL
)
2431 descr
->selected_item
= caret
;
2433 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2434 if (descr
->style
& LBS_NOTIFY
)
2436 if (descr
->lphc
&& IsWindowVisible( descr
->self
))
2438 /* make sure that combo parent doesn't hide us */
2439 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2441 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2448 /***********************************************************************
2449 * LISTBOX_HandleChar
2451 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2459 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2461 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2462 MAKEWPARAM(charW
, descr
->focus_item
),
2463 (LPARAM
)descr
->self
);
2464 if (caret
== -2) return 0;
2467 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2470 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2471 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2472 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2473 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2474 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2480 /***********************************************************************
2483 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2486 MEASUREITEMSTRUCT mis
;
2489 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2492 GetClientRect( hwnd
, &rect
);
2494 descr
->owner
= GetParent( descr
->self
);
2495 descr
->style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2496 descr
->width
= rect
.right
- rect
.left
;
2497 descr
->height
= rect
.bottom
- rect
.top
;
2498 descr
->items
= NULL
;
2499 descr
->nb_items
= 0;
2500 descr
->top_item
= 0;
2501 descr
->selected_item
= -1;
2502 descr
->focus_item
= 0;
2503 descr
->anchor_item
= -1;
2504 descr
->item_height
= 1;
2505 descr
->page_size
= 1;
2506 descr
->column_width
= 150;
2507 descr
->horz_extent
= 0;
2508 descr
->horz_pos
= 0;
2511 descr
->wheel_remain
= 0;
2512 descr
->caret_on
= !lphc
;
2513 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2514 descr
->in_focus
= FALSE
;
2515 descr
->captured
= FALSE
;
2517 descr
->locale
= GetUserDefaultLCID();
2522 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2523 descr
->owner
= lphc
->self
;
2526 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2528 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2530 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2531 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2532 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2533 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2535 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2537 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2539 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2540 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2544 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2545 mis
.CtlType
= ODT_LISTBOX
;
2550 mis
.itemHeight
= descr
->item_height
;
2551 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2552 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2556 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2561 /***********************************************************************
2564 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2566 LISTBOX_ResetContent( descr
);
2567 SetWindowLongPtrW( descr
->self
, 0, 0 );
2568 HeapFree( GetProcessHeap(), 0, descr
);
2573 /***********************************************************************
2574 * ListBoxWndProc_common
2576 LRESULT
ListBoxWndProc_common( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2578 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2579 LPHEADCOMBO lphc
= 0;
2584 if (!IsWindow(hwnd
)) return 0;
2586 if (msg
== WM_CREATE
)
2588 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2589 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= lpcs
->lpCreateParams
;
2590 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2591 TRACE("creating hwnd %p descr %p\n", hwnd
, (void *)GetWindowLongPtrW( hwnd
, 0 ) );
2594 /* Ignore all other messages before we get a WM_CREATE */
2595 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2596 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2598 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2600 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2601 descr
->self
, SPY_GetMsgName(msg
, descr
->self
), wParam
, lParam
);
2605 case LB_RESETCONTENT
:
2606 LISTBOX_ResetContent( descr
);
2607 LISTBOX_UpdateScroll( descr
);
2608 InvalidateRect( descr
->self
, NULL
, TRUE
);
2615 if(unicode
|| !HAS_STRINGS(descr
))
2616 textW
= (LPWSTR
)lParam
;
2619 LPSTR textA
= (LPSTR
)lParam
;
2620 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2621 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2622 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2626 wParam
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2627 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2628 if (!unicode
&& HAS_STRINGS(descr
))
2629 HeapFree(GetProcessHeap(), 0, textW
);
2633 case LB_INSERTSTRING
:
2637 if(unicode
|| !HAS_STRINGS(descr
))
2638 textW
= (LPWSTR
)lParam
;
2641 LPSTR textA
= (LPSTR
)lParam
;
2642 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2643 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2644 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2648 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2649 if(!unicode
&& HAS_STRINGS(descr
))
2650 HeapFree(GetProcessHeap(), 0, textW
);
2658 if(unicode
|| !HAS_STRINGS(descr
))
2659 textW
= (LPWSTR
)lParam
;
2662 LPSTR textA
= (LPSTR
)lParam
;
2663 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2664 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2665 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2669 wParam
= LISTBOX_FindFileStrPos( descr
, textW
);
2670 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2671 if(!unicode
&& HAS_STRINGS(descr
))
2672 HeapFree(GetProcessHeap(), 0, textW
);
2676 case LB_DELETESTRING
:
2677 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2678 return descr
->nb_items
;
2681 SetLastError(ERROR_INVALID_INDEX
);
2685 case LB_GETITEMDATA
:
2686 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2688 SetLastError(ERROR_INVALID_INDEX
);
2691 return descr
->items
[wParam
].data
;
2693 case LB_SETITEMDATA
:
2694 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2696 SetLastError(ERROR_INVALID_INDEX
);
2699 descr
->items
[wParam
].data
= lParam
;
2700 /* undocumented: returns TRUE, not LB_OKAY (0) */
2704 return descr
->nb_items
;
2707 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, unicode
);
2710 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2712 SetLastError(ERROR_INVALID_INDEX
);
2715 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2716 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2717 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2718 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2721 if (descr
->nb_items
== 0)
2723 if (!IS_MULTISELECT(descr
))
2724 return descr
->selected_item
;
2725 if (descr
->selected_item
!= -1)
2726 return descr
->selected_item
;
2727 return descr
->focus_item
;
2728 /* otherwise, if the user tries to move the selection with the */
2729 /* arrow keys, we will give the application something to choke on */
2730 case LB_GETTOPINDEX
:
2731 return descr
->top_item
;
2733 case LB_GETITEMHEIGHT
:
2734 return LISTBOX_GetItemHeight( descr
, wParam
);
2736 case LB_SETITEMHEIGHT
:
2737 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2739 case LB_ITEMFROMPOINT
:
2746 /* The hiword of the return value is not a client area
2747 hittest as suggested by MSDN, but rather a hittest on
2748 the returned listbox item. */
2750 if(descr
->nb_items
== 0)
2751 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2753 pt
.x
= (short)LOWORD(lParam
);
2754 pt
.y
= (short)HIWORD(lParam
);
2756 SetRect(&rect
, 0, 0, descr
->width
, descr
->height
);
2758 if(!PtInRect(&rect
, pt
))
2760 pt
.x
= min(pt
.x
, rect
.right
- 1);
2761 pt
.x
= max(pt
.x
, 0);
2762 pt
.y
= min(pt
.y
, rect
.bottom
- 1);
2763 pt
.y
= max(pt
.y
, 0);
2767 index
= LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
);
2771 index
= descr
->nb_items
- 1;
2774 return MAKELONG(index
, hit
? 0 : 1);
2777 case LB_SETCARETINDEX
:
2778 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2779 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2786 case LB_GETCARETINDEX
:
2787 return descr
->focus_item
;
2789 case LB_SETTOPINDEX
:
2790 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2792 case LB_SETCOLUMNWIDTH
:
2793 return LISTBOX_SetColumnWidth( descr
, wParam
);
2795 case LB_GETITEMRECT
:
2796 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2802 if(unicode
|| !HAS_STRINGS(descr
))
2803 textW
= (LPWSTR
)lParam
;
2806 LPSTR textA
= (LPSTR
)lParam
;
2807 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2808 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2809 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2811 ret
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2812 if(!unicode
&& HAS_STRINGS(descr
))
2813 HeapFree(GetProcessHeap(), 0, textW
);
2817 case LB_FINDSTRINGEXACT
:
2821 if(unicode
|| !HAS_STRINGS(descr
))
2822 textW
= (LPWSTR
)lParam
;
2825 LPSTR textA
= (LPSTR
)lParam
;
2826 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2827 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2828 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2830 ret
= LISTBOX_FindString( descr
, wParam
, textW
, TRUE
);
2831 if(!unicode
&& HAS_STRINGS(descr
))
2832 HeapFree(GetProcessHeap(), 0, textW
);
2836 case LB_SELECTSTRING
:
2841 if(HAS_STRINGS(descr
))
2842 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2843 debugstr_a((LPSTR
)lParam
));
2844 if(unicode
|| !HAS_STRINGS(descr
))
2845 textW
= (LPWSTR
)lParam
;
2848 LPSTR textA
= (LPSTR
)lParam
;
2849 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2850 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2851 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2853 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2854 if(!unicode
&& HAS_STRINGS(descr
))
2855 HeapFree(GetProcessHeap(), 0, textW
);
2856 if (index
!= LB_ERR
)
2858 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2859 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2865 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2867 return descr
->items
[wParam
].selected
;
2870 return LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2873 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2874 LISTBOX_SetCaretIndex( descr
, wParam
, TRUE
);
2875 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2876 if (ret
!= LB_ERR
) ret
= descr
->selected_item
;
2879 case LB_GETSELCOUNT
:
2880 return LISTBOX_GetSelCount( descr
);
2882 case LB_GETSELITEMS
:
2883 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2885 case LB_SELITEMRANGE
:
2886 if (LOWORD(lParam
) <= HIWORD(lParam
))
2887 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2888 HIWORD(lParam
), wParam
);
2890 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2891 LOWORD(lParam
), wParam
);
2893 case LB_SELITEMRANGEEX
:
2894 if ((INT
)lParam
>= (INT
)wParam
)
2895 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2897 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
2899 case LB_GETHORIZONTALEXTENT
:
2900 return descr
->horz_extent
;
2902 case LB_SETHORIZONTALEXTENT
:
2903 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
2905 case LB_GETANCHORINDEX
:
2906 return descr
->anchor_item
;
2908 case LB_SETANCHORINDEX
:
2909 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2911 SetLastError(ERROR_INVALID_INDEX
);
2914 descr
->anchor_item
= (INT
)wParam
;
2922 textW
= (LPWSTR
)lParam
;
2925 LPSTR textA
= (LPSTR
)lParam
;
2926 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2927 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2928 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2930 ret
= LISTBOX_Directory( descr
, wParam
, textW
, msg
== LB_DIR
);
2932 HeapFree(GetProcessHeap(), 0, textW
);
2937 return descr
->locale
;
2942 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
2944 ret
= descr
->locale
;
2945 descr
->locale
= (LCID
)wParam
;
2949 case LB_INITSTORAGE
:
2950 return LISTBOX_InitStorage( descr
, wParam
);
2953 return LISTBOX_SetCount( descr
, (INT
)wParam
);
2955 case LB_SETTABSTOPS
:
2956 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
);
2959 if (descr
->caret_on
)
2961 descr
->caret_on
= TRUE
;
2962 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2963 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2967 if (!descr
->caret_on
)
2969 descr
->caret_on
= FALSE
;
2970 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2971 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2974 case LB_GETLISTBOXINFO
:
2975 return descr
->page_size
;
2978 return LISTBOX_Destroy( descr
);
2981 InvalidateRect( descr
->self
, NULL
, TRUE
);
2985 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
2989 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2991 case WM_PRINTCLIENT
:
2995 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
2996 ret
= LISTBOX_Paint( descr
, hdc
);
2997 if( !wParam
) EndPaint( descr
->self
, &ps
);
3001 LISTBOX_UpdateSize( descr
);
3004 return (LRESULT
)descr
->font
;
3006 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
3007 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
3010 descr
->in_focus
= TRUE
;
3011 descr
->caret_on
= TRUE
;
3012 if (descr
->focus_item
!= -1)
3013 LISTBOX_DrawFocusRect( descr
, TRUE
);
3014 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
3017 LISTBOX_HandleLButtonUp( descr
); /* Release capture if we have it */
3018 descr
->in_focus
= FALSE
;
3019 descr
->wheel_remain
= 0;
3020 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
3021 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3022 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
3025 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3027 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3029 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
3030 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
3031 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
3032 case WM_LBUTTONDOWN
:
3034 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3035 (INT16
)LOWORD(lParam
),
3036 (INT16
)HIWORD(lParam
) );
3037 return LISTBOX_HandleLButtonDown( descr
, wParam
,
3038 (INT16
)LOWORD(lParam
),
3039 (INT16
)HIWORD(lParam
) );
3040 case WM_LBUTTONDBLCLK
:
3042 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3043 (INT16
)LOWORD(lParam
),
3044 (INT16
)HIWORD(lParam
) );
3045 if (descr
->style
& LBS_NOTIFY
)
3046 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
3049 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
3051 BOOL captured
= descr
->captured
;
3055 mousePos
.x
= (INT16
)LOWORD(lParam
);
3056 mousePos
.y
= (INT16
)HIWORD(lParam
);
3059 * If we are in a dropdown combobox, we simulate that
3060 * the mouse is captured to show the tracking of the item.
3062 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
3063 descr
->captured
= TRUE
;
3065 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
3067 descr
->captured
= captured
;
3069 else if (GetCapture() == descr
->self
)
3071 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
3072 (INT16
)HIWORD(lParam
) );
3082 * If the mouse button "up" is not in the listbox,
3083 * we make sure there is no selection by re-selecting the
3084 * item that was selected when the listbox was made visible.
3086 mousePos
.x
= (INT16
)LOWORD(lParam
);
3087 mousePos
.y
= (INT16
)HIWORD(lParam
);
3089 GetClientRect(descr
->self
, &clientRect
);
3092 * When the user clicks outside the combobox and the focus
3093 * is lost, the owning combobox will send a fake buttonup with
3094 * 0xFFFFFFF as the mouse location, we must also revert the
3095 * selection to the original selection.
3097 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
3098 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
3100 return LISTBOX_HandleLButtonUp( descr
);
3102 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3104 /* for some reason Windows makes it possible to
3105 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3107 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3108 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3109 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3111 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3115 return LISTBOX_HandleKeyDown( descr
, wParam
);
3120 charW
= (WCHAR
)wParam
;
3123 CHAR charA
= (CHAR
)wParam
;
3124 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
3126 return LISTBOX_HandleChar( descr
, charW
);
3129 return LISTBOX_HandleSystemTimer( descr
);
3131 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3134 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3135 wParam
, (LPARAM
)descr
->self
);
3136 TRACE("hbrush = %p\n", hbrush
);
3138 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3141 GetClientRect(descr
->self
, &rect
);
3142 FillRect((HDC
)wParam
, &rect
, hbrush
);
3147 if( lphc
) return 0;
3148 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3149 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3152 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3161 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3162 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3163 hwnd
, msg
, wParam
, lParam
);
3166 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3167 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3170 DWORD WINAPI
GetListBoxInfo(HWND hwnd
)
3172 TRACE("%p\n", hwnd
);
3173 return SendMessageW(hwnd
, LB_GETLISTBOXINFO
, 0, 0);