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
31 #include "wine/unicode.h"
32 #include "user_private.h"
34 #include "wine/exception.h"
35 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(listbox
);
39 /* Items array granularity */
40 #define LB_ARRAY_GRANULARITY 16
42 /* Scrolling timeout in ms */
43 #define LB_SCROLL_TIMEOUT 50
45 /* Listbox system timer id */
48 /* flag listbox changed while setredraw false - internal style */
49 #define LBS_DISPLAYCHANGED 0x80000000
54 LPWSTR str
; /* Item text */
55 BOOL selected
; /* Is item selected? */
56 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
57 ULONG_PTR data
; /* User data */
60 /* Listbox structure */
63 HWND self
; /* Our own window handle */
64 HWND owner
; /* Owner window to send notifications to */
65 UINT style
; /* Window style */
66 INT width
; /* Window width */
67 INT height
; /* Window height */
68 LB_ITEMDATA
*items
; /* Array of items */
69 INT nb_items
; /* Number of items */
70 INT top_item
; /* Top visible item */
71 INT selected_item
; /* Selected item */
72 INT focus_item
; /* Item that has the focus */
73 INT anchor_item
; /* Anchor item for extended selection */
74 INT item_height
; /* Default item height */
75 INT page_size
; /* Items per listbox page */
76 INT column_width
; /* Column width for multi-column listboxes */
77 INT horz_extent
; /* Horizontal extent */
78 INT horz_pos
; /* Horizontal position */
79 INT nb_tabs
; /* Number of tabs in array */
80 INT
*tabs
; /* Array of tabs */
81 INT avg_char_width
; /* Average width of characters */
82 INT wheel_remain
; /* Left over scroll amount */
83 BOOL caret_on
; /* Is caret on? */
84 BOOL captured
; /* Is mouse captured? */
86 HFONT font
; /* Current font */
87 LCID locale
; /* Current locale for string comparisons */
88 LPHEADCOMBO lphc
; /* ComboLBox */
92 #define IS_OWNERDRAW(descr) \
93 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
95 #define HAS_STRINGS(descr) \
96 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
99 #define IS_MULTISELECT(descr) \
100 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
101 !((descr)->style & LBS_NOSEL))
103 #define SEND_NOTIFICATION(descr,code) \
104 (SendMessageW( (descr)->owner, WM_COMMAND, \
105 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
107 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
109 /* Current timer status */
119 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
121 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
);
123 /*********************************************************************
124 * listbox class descriptor
126 static const WCHAR listboxW
[] = {'L','i','s','t','B','o','x',0};
127 const struct builtin_class_descr LISTBOX_builtin_class
=
130 CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
131 WINPROC_LISTBOX
, /* proc */
132 sizeof(LB_DESCR
*), /* extra */
133 IDC_ARROW
, /* cursor */
138 /*********************************************************************
139 * combolbox class descriptor
141 static const WCHAR combolboxW
[] = {'C','o','m','b','o','L','B','o','x',0};
142 const struct builtin_class_descr COMBOLBOX_builtin_class
=
144 combolboxW
, /* name */
145 CS_DBLCLKS
| CS_SAVEBITS
, /* style */
146 WINPROC_LISTBOX
, /* proc */
147 sizeof(LB_DESCR
*), /* extra */
148 IDC_ARROW
, /* cursor */
153 /***********************************************************************
154 * LISTBOX_GetCurrentPageSize
156 * Return the current page size
158 static INT
LISTBOX_GetCurrentPageSize( const LB_DESCR
*descr
)
161 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
162 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
164 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
166 if (i
== descr
->top_item
) return 1;
167 else return i
- descr
->top_item
;
171 /***********************************************************************
172 * LISTBOX_GetMaxTopIndex
174 * Return the maximum possible index for the top of the listbox.
176 static INT
LISTBOX_GetMaxTopIndex( const LB_DESCR
*descr
)
180 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
182 page
= descr
->height
;
183 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
184 if ((page
-= descr
->items
[max
].height
) < 0) break;
185 if (max
< descr
->nb_items
- 1) max
++;
187 else if (descr
->style
& LBS_MULTICOLUMN
)
189 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
190 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
191 max
= (max
- page
) * descr
->page_size
;
195 max
= descr
->nb_items
- descr
->page_size
;
197 if (max
< 0) max
= 0;
202 /***********************************************************************
203 * LISTBOX_UpdateScroll
205 * Update the scrollbars. Should be called whenever the content
206 * of the listbox changes.
208 static void LISTBOX_UpdateScroll( LB_DESCR
*descr
)
212 /* Check the listbox scroll bar flags individually before we call
213 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
214 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
215 scroll bar when we do not need one.
216 if (!(descr->style & WS_VSCROLL)) return;
219 /* It is important that we check descr->style, and not wnd->dwStyle,
220 for WS_VSCROLL, as the former is exactly the one passed in
221 argument to CreateWindow.
222 In Windows (and from now on in Wine :) a listbox created
223 with such a style (no WS_SCROLL) does not update
224 the scrollbar with listbox-related data, thus letting
225 the programmer use it for his/her own purposes. */
227 if (descr
->style
& LBS_NOREDRAW
) return;
228 info
.cbSize
= sizeof(info
);
230 if (descr
->style
& LBS_MULTICOLUMN
)
233 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
234 info
.nPos
= descr
->top_item
/ descr
->page_size
;
235 info
.nPage
= descr
->width
/ descr
->column_width
;
236 if (info
.nPage
< 1) info
.nPage
= 1;
237 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
238 if (descr
->style
& LBS_DISABLENOSCROLL
)
239 info
.fMask
|= SIF_DISABLENOSCROLL
;
240 if (descr
->style
& WS_HSCROLL
)
241 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
243 info
.fMask
= SIF_RANGE
;
244 if (descr
->style
& WS_VSCROLL
)
245 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
250 info
.nMax
= descr
->nb_items
- 1;
251 info
.nPos
= descr
->top_item
;
252 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
253 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
254 if (descr
->style
& LBS_DISABLENOSCROLL
)
255 info
.fMask
|= SIF_DISABLENOSCROLL
;
256 if (descr
->style
& WS_VSCROLL
)
257 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
259 if ((descr
->style
& WS_HSCROLL
) && descr
->horz_extent
)
261 info
.nPos
= descr
->horz_pos
;
262 info
.nPage
= descr
->width
;
263 info
.fMask
= SIF_POS
| SIF_PAGE
;
264 if (descr
->style
& LBS_DISABLENOSCROLL
)
265 info
.fMask
|= SIF_DISABLENOSCROLL
;
266 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
270 if (descr
->style
& LBS_DISABLENOSCROLL
)
274 info
.fMask
= SIF_RANGE
| SIF_DISABLENOSCROLL
;
275 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
279 ShowScrollBar( descr
->self
, SB_HORZ
, FALSE
);
286 /***********************************************************************
289 * Set the top item of the listbox, scrolling up or down if necessary.
291 static LRESULT
LISTBOX_SetTopItem( LB_DESCR
*descr
, INT index
, BOOL scroll
)
293 INT max
= LISTBOX_GetMaxTopIndex( descr
);
295 TRACE("setting top item %d, scroll %d\n", index
, scroll
);
297 if (index
> max
) index
= max
;
298 if (index
< 0) index
= 0;
299 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
300 if (descr
->top_item
== index
) return LB_OKAY
;
304 if (descr
->style
& LBS_MULTICOLUMN
)
305 dx
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
306 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
309 if (index
> descr
->top_item
)
311 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
312 dy
-= descr
->items
[i
].height
;
316 for (i
= index
; i
< descr
->top_item
; i
++)
317 dy
+= descr
->items
[i
].height
;
321 dy
= (descr
->top_item
- index
) * descr
->item_height
;
323 ScrollWindowEx( descr
->self
, dx
, dy
, NULL
, NULL
, 0, NULL
,
324 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
327 InvalidateRect( descr
->self
, NULL
, TRUE
);
328 descr
->top_item
= index
;
329 LISTBOX_UpdateScroll( descr
);
334 /***********************************************************************
337 * Update the page size. Should be called when the size of
338 * the client area or the item height changes.
340 static void LISTBOX_UpdatePage( LB_DESCR
*descr
)
344 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
346 if (page_size
== descr
->page_size
) return;
347 descr
->page_size
= page_size
;
348 if (descr
->style
& LBS_MULTICOLUMN
)
349 InvalidateRect( descr
->self
, NULL
, TRUE
);
350 LISTBOX_SetTopItem( descr
, descr
->top_item
, FALSE
);
354 /***********************************************************************
357 * Update the size of the listbox. Should be called when the size of
358 * the client area changes.
360 static void LISTBOX_UpdateSize( LB_DESCR
*descr
)
364 GetClientRect( descr
->self
, &rect
);
365 descr
->width
= rect
.right
- rect
.left
;
366 descr
->height
= rect
.bottom
- rect
.top
;
367 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
372 GetWindowRect( descr
->self
, &rect
);
373 if(descr
->item_height
!= 0)
374 remaining
= descr
->height
% descr
->item_height
;
377 if ((descr
->height
> descr
->item_height
) && remaining
)
379 TRACE("[%p]: changing height %d -> %d\n",
380 descr
->self
, descr
->height
, descr
->height
- remaining
);
381 SetWindowPos( descr
->self
, 0, 0, 0, rect
.right
- rect
.left
,
382 rect
.bottom
- rect
.top
- remaining
,
383 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
387 TRACE("[%p]: new size = %d,%d\n", descr
->self
, descr
->width
, descr
->height
);
388 LISTBOX_UpdatePage( descr
);
389 LISTBOX_UpdateScroll( descr
);
391 /* Invalidate the focused item so it will be repainted correctly */
392 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
394 InvalidateRect( descr
->self
, &rect
, FALSE
);
399 /***********************************************************************
400 * LISTBOX_GetItemRect
402 * Get the rectangle enclosing an item, in listbox client coordinates.
403 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
405 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
)
407 /* Index <= 0 is legal even on empty listboxes */
408 if (index
&& (index
>= descr
->nb_items
))
411 SetLastError(ERROR_INVALID_INDEX
);
414 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
415 if (descr
->style
& LBS_MULTICOLUMN
)
417 INT col
= (index
/ descr
->page_size
) -
418 (descr
->top_item
/ descr
->page_size
);
419 rect
->left
+= col
* descr
->column_width
;
420 rect
->right
= rect
->left
+ descr
->column_width
;
421 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
422 rect
->bottom
= rect
->top
+ descr
->item_height
;
424 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
427 rect
->right
+= descr
->horz_pos
;
428 if ((index
>= 0) && (index
< descr
->nb_items
))
430 if (index
< descr
->top_item
)
432 for (i
= descr
->top_item
-1; i
>= index
; i
--)
433 rect
->top
-= descr
->items
[i
].height
;
437 for (i
= descr
->top_item
; i
< index
; i
++)
438 rect
->top
+= descr
->items
[i
].height
;
440 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
446 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
447 rect
->bottom
= rect
->top
+ descr
->item_height
;
448 rect
->right
+= descr
->horz_pos
;
451 TRACE("item %d, rect %s\n", index
, wine_dbgstr_rect(rect
));
453 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
454 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
458 /***********************************************************************
459 * LISTBOX_GetItemFromPoint
461 * Return the item nearest from point (x,y) (in client coordinates).
463 static INT
LISTBOX_GetItemFromPoint( const LB_DESCR
*descr
, INT x
, INT y
)
465 INT index
= descr
->top_item
;
467 if (!descr
->nb_items
) return -1; /* No items */
468 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
473 while (index
< descr
->nb_items
)
475 if ((pos
+= descr
->items
[index
].height
) > y
) break;
484 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
488 else if (descr
->style
& LBS_MULTICOLUMN
)
490 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
491 if (y
>= 0) index
+= y
/ descr
->item_height
;
492 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
493 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
497 index
+= (y
/ descr
->item_height
);
499 if (index
< 0) return 0;
500 if (index
>= descr
->nb_items
) return -1;
505 /***********************************************************************
510 static void LISTBOX_PaintItem( LB_DESCR
*descr
, HDC hdc
, const RECT
*rect
,
511 INT index
, UINT action
, BOOL ignoreFocus
)
513 LB_ITEMDATA
*item
= NULL
;
514 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
516 if (IS_OWNERDRAW(descr
))
524 if (action
== ODA_FOCUS
)
525 DrawFocusRect( hdc
, rect
);
527 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
531 /* some programs mess with the clipping region when
532 drawing the item, *and* restore the previous region
533 after they are done, so a region has better to exist
534 else everything ends clipped */
535 GetClientRect(descr
->self
, &r
);
536 hrgn
= set_control_clipping( hdc
, &r
);
538 dis
.CtlType
= ODT_LISTBOX
;
539 dis
.CtlID
= GetWindowLongPtrW( descr
->self
, GWLP_ID
);
540 dis
.hwndItem
= descr
->self
;
541 dis
.itemAction
= action
;
545 if (item
->selected
) dis
.itemState
|= ODS_SELECTED
;
546 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
548 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
549 if (!IsWindowEnabled(descr
->self
)) dis
.itemState
|= ODS_DISABLED
;
550 dis
.itemData
= item
->data
;
552 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
553 descr
->self
, index
, debugstr_w(item
->str
), action
,
554 dis
.itemState
, wine_dbgstr_rect(rect
) );
555 SendMessageW(descr
->owner
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
556 SelectClipRgn( hdc
, hrgn
);
557 if (hrgn
) DeleteObject( hrgn
);
561 COLORREF oldText
= 0, oldBk
= 0;
563 if (action
== ODA_FOCUS
)
565 DrawFocusRect( hdc
, rect
);
568 if (item
&& item
->selected
)
570 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
571 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
574 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
575 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
576 wine_dbgstr_rect(rect
) );
578 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
579 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
580 else if (!(descr
->style
& LBS_USETABSTOPS
))
581 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
582 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
583 strlenW(item
->str
), NULL
);
586 /* Output empty string to paint background in the full width. */
587 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
588 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
589 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
590 item
->str
, strlenW(item
->str
),
591 descr
->nb_tabs
, descr
->tabs
, 0);
593 if (item
&& item
->selected
)
595 SetBkColor( hdc
, oldBk
);
596 SetTextColor( hdc
, oldText
);
598 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
600 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
605 /***********************************************************************
608 * Change the redraw flag.
610 static void LISTBOX_SetRedraw( LB_DESCR
*descr
, BOOL on
)
614 if (!(descr
->style
& LBS_NOREDRAW
)) return;
615 descr
->style
&= ~LBS_NOREDRAW
;
616 if (descr
->style
& LBS_DISPLAYCHANGED
)
617 { /* page was changed while setredraw false, refresh automatically */
618 InvalidateRect(descr
->self
, NULL
, TRUE
);
619 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
620 { /* reset top of page if less than number of items/page */
621 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
622 if (descr
->top_item
< 0) descr
->top_item
= 0;
624 descr
->style
&= ~LBS_DISPLAYCHANGED
;
626 LISTBOX_UpdateScroll( descr
);
628 else descr
->style
|= LBS_NOREDRAW
;
632 /***********************************************************************
633 * LISTBOX_RepaintItem
635 * Repaint a single item synchronously.
637 static void LISTBOX_RepaintItem( LB_DESCR
*descr
, INT index
, UINT action
)
642 HBRUSH hbrush
, oldBrush
= 0;
644 /* Do not repaint the item if the item is not visible */
645 if (!IsWindowVisible(descr
->self
)) return;
646 if (descr
->style
& LBS_NOREDRAW
)
648 descr
->style
|= LBS_DISPLAYCHANGED
;
651 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
652 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
653 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
654 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
655 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
656 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
657 if (!IsWindowEnabled(descr
->self
))
658 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
659 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
660 LISTBOX_PaintItem( descr
, hdc
, &rect
, index
, action
, TRUE
);
661 if (oldFont
) SelectObject( hdc
, oldFont
);
662 if (oldBrush
) SelectObject( hdc
, oldBrush
);
663 ReleaseDC( descr
->self
, hdc
);
667 /***********************************************************************
668 * LISTBOX_DrawFocusRect
670 static void LISTBOX_DrawFocusRect( LB_DESCR
*descr
, BOOL on
)
676 /* Do not repaint the item if the item is not visible */
677 if (!IsWindowVisible(descr
->self
)) return;
679 if (descr
->focus_item
== -1) return;
680 if (!descr
->caret_on
|| !descr
->in_focus
) return;
682 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) != 1) return;
683 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
684 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
685 if (!IsWindowEnabled(descr
->self
))
686 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
687 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
688 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, !on
);
689 if (oldFont
) SelectObject( hdc
, oldFont
);
690 ReleaseDC( descr
->self
, hdc
);
694 /***********************************************************************
695 * LISTBOX_InitStorage
697 static LRESULT
LISTBOX_InitStorage( LB_DESCR
*descr
, INT nb_items
)
701 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
702 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
704 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
705 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
706 nb_items
* sizeof(LB_ITEMDATA
));
709 item
= HeapAlloc( GetProcessHeap(), 0,
710 nb_items
* sizeof(LB_ITEMDATA
));
715 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
723 /***********************************************************************
724 * LISTBOX_SetTabStops
726 static BOOL
LISTBOX_SetTabStops( LB_DESCR
*descr
, INT count
, LPINT tabs
)
730 if (!(descr
->style
& LBS_USETABSTOPS
))
732 SetLastError(ERROR_LB_WITHOUT_TABSTOPS
);
736 HeapFree( GetProcessHeap(), 0, descr
->tabs
);
737 if (!(descr
->nb_tabs
= count
))
742 if (!(descr
->tabs
= HeapAlloc( GetProcessHeap(), 0,
743 descr
->nb_tabs
* sizeof(INT
) )))
745 memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
747 /* convert into "dialog units"*/
748 for (i
= 0; i
< descr
->nb_tabs
; i
++)
749 descr
->tabs
[i
] = MulDiv(descr
->tabs
[i
], descr
->avg_char_width
, 4);
755 /***********************************************************************
758 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPWSTR buffer
, BOOL unicode
)
762 if ((index
< 0) || (index
>= descr
->nb_items
))
764 SetLastError(ERROR_INVALID_INDEX
);
767 if (HAS_STRINGS(descr
))
771 len
= strlenW(descr
->items
[index
].str
);
774 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[index
].str
, len
,
775 NULL
, 0, NULL
, NULL
);
778 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
780 __TRY
/* hide a Delphi bug that passes a read-only buffer */
784 strcpyW( buffer
, descr
->items
[index
].str
);
785 len
= strlenW(buffer
);
789 len
= WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1,
790 (LPSTR
)buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
795 WARN( "got an invalid buffer (Delphi bug?)\n" );
796 SetLastError( ERROR_INVALID_PARAMETER
);
802 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
808 static inline INT
LISTBOX_lstrcmpiW( LCID lcid
, LPCWSTR str1
, LPCWSTR str2
)
810 INT ret
= CompareStringW( lcid
, NORM_IGNORECASE
, str1
, -1, str2
, -1 );
811 if (ret
== CSTR_LESS_THAN
)
813 if (ret
== CSTR_EQUAL
)
815 if (ret
== CSTR_GREATER_THAN
)
820 /***********************************************************************
821 * LISTBOX_FindStringPos
823 * Find the nearest string located before a given string in sort order.
824 * If 'exact' is TRUE, return an error if we don't get an exact match.
826 static INT
LISTBOX_FindStringPos( LB_DESCR
*descr
, LPCWSTR str
, BOOL exact
)
828 INT index
, min
, max
, res
;
830 if (!descr
->nb_items
|| !(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
833 max
= descr
->nb_items
- 1;
836 index
= (min
+ max
) / 2;
837 if (HAS_STRINGS(descr
))
838 res
= LISTBOX_lstrcmpiW( descr
->locale
, descr
->items
[index
].str
, str
);
841 COMPAREITEMSTRUCT cis
;
842 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
844 cis
.CtlType
= ODT_LISTBOX
;
846 cis
.hwndItem
= descr
->self
;
847 /* note that some application (MetaStock) expects the second item
848 * to be in the listbox */
850 cis
.itemData1
= descr
->items
[index
].data
;
852 cis
.itemData2
= (ULONG_PTR
)str
;
853 cis
.dwLocaleId
= descr
->locale
;
854 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
856 if (!res
) return index
;
857 if (res
> 0) max
= index
- 1;
858 else min
= index
+ 1;
860 return exact
? -1 : min
;
864 /***********************************************************************
865 * LISTBOX_FindFileStrPos
867 * Find the nearest string located before a given string in directory
868 * sort order (i.e. first files, then directories, then drives).
870 static INT
LISTBOX_FindFileStrPos( LB_DESCR
*descr
, LPCWSTR str
)
874 if (!HAS_STRINGS(descr
))
875 return LISTBOX_FindStringPos( descr
, str
, FALSE
);
877 max
= descr
->nb_items
;
880 INT index
= (min
+ max
) / 2;
881 LPCWSTR p
= descr
->items
[index
].str
;
882 if (*p
== '[') /* drive or directory */
884 if (*str
!= '[') res
= -1;
885 else if (p
[1] == '-') /* drive */
887 if (str
[1] == '-') res
= str
[2] - p
[2];
892 if (str
[1] == '-') res
= 1;
893 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
898 if (*str
== '[') res
= 1;
899 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
901 if (!res
) return index
;
902 if (res
< 0) max
= index
;
903 else min
= index
+ 1;
909 /***********************************************************************
912 * Find the item beginning with a given string.
914 static INT
LISTBOX_FindString( LB_DESCR
*descr
, INT start
, LPCWSTR str
, BOOL exact
)
919 if (start
>= descr
->nb_items
) start
= -1;
920 item
= descr
->items
+ start
+ 1;
921 if (HAS_STRINGS(descr
))
923 if (!str
|| ! str
[0] ) return LB_ERR
;
926 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
927 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
928 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
929 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
933 /* Special case for drives and directories: ignore prefix */
934 #define CHECK_DRIVE(item) \
935 if ((item)->str[0] == '[') \
937 if (!strncmpiW( str, (item)->str+1, len )) return i; \
938 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
942 INT len
= strlenW(str
);
943 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
945 if (!strncmpiW( str
, item
->str
, len
)) return i
;
948 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
950 if (!strncmpiW( str
, item
->str
, len
)) return i
;
958 if (exact
&& (descr
->style
& LBS_SORT
))
959 /* If sorted, use a WM_COMPAREITEM binary search */
960 return LISTBOX_FindStringPos( descr
, str
, TRUE
);
962 /* Otherwise use a linear search */
963 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
964 if (item
->data
== (ULONG_PTR
)str
) return i
;
965 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
966 if (item
->data
== (ULONG_PTR
)str
) return i
;
972 /***********************************************************************
973 * LISTBOX_GetSelCount
975 static LRESULT
LISTBOX_GetSelCount( const LB_DESCR
*descr
)
978 const LB_ITEMDATA
*item
= descr
->items
;
980 if (!(descr
->style
& LBS_MULTIPLESEL
) ||
981 (descr
->style
& LBS_NOSEL
))
983 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
984 if (item
->selected
) count
++;
989 /***********************************************************************
990 * LISTBOX_GetSelItems
992 static LRESULT
LISTBOX_GetSelItems( const LB_DESCR
*descr
, INT max
, LPINT array
)
995 const LB_ITEMDATA
*item
= descr
->items
;
997 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
998 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
999 if (item
->selected
) array
[count
++] = i
;
1004 /***********************************************************************
1007 static LRESULT
LISTBOX_Paint( LB_DESCR
*descr
, HDC hdc
)
1009 INT i
, col_pos
= descr
->page_size
- 1;
1011 RECT focusRect
= {-1, -1, -1, -1};
1013 HBRUSH hbrush
, oldBrush
= 0;
1015 if (descr
->style
& LBS_NOREDRAW
) return 0;
1017 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1018 if (descr
->style
& LBS_MULTICOLUMN
)
1019 rect
.right
= rect
.left
+ descr
->column_width
;
1020 else if (descr
->horz_pos
)
1022 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1023 rect
.right
+= descr
->horz_pos
;
1026 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1027 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
1028 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
1029 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1030 if (!IsWindowEnabled(descr
->self
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1032 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1035 /* Special case for empty listbox: paint focus rect */
1036 rect
.bottom
= rect
.top
+ descr
->item_height
;
1037 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1038 &rect
, NULL
, 0, NULL
);
1039 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1040 rect
.top
= rect
.bottom
;
1043 /* Paint all the item, regarding the selection
1044 Focus state will be painted after */
1046 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1048 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1049 rect
.bottom
= rect
.top
+ descr
->item_height
;
1051 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1053 /* keep the focus rect, to paint the focus item after */
1054 if (i
== descr
->focus_item
)
1057 LISTBOX_PaintItem( descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1058 rect
.top
= rect
.bottom
;
1060 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1062 if (!IS_OWNERDRAW(descr
))
1064 /* Clear the bottom of the column */
1065 if (rect
.top
< descr
->height
)
1067 rect
.bottom
= descr
->height
;
1068 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1069 &rect
, NULL
, 0, NULL
);
1073 /* Go to the next column */
1074 rect
.left
+= descr
->column_width
;
1075 rect
.right
+= descr
->column_width
;
1077 col_pos
= descr
->page_size
- 1;
1082 if (rect
.top
>= descr
->height
) break;
1086 /* Paint the focus item now */
1087 if (focusRect
.top
!= focusRect
.bottom
&&
1088 descr
->caret_on
&& descr
->in_focus
)
1089 LISTBOX_PaintItem( descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1091 if (!IS_OWNERDRAW(descr
))
1093 /* Clear the remainder of the client area */
1094 if (rect
.top
< descr
->height
)
1096 rect
.bottom
= descr
->height
;
1097 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1098 &rect
, NULL
, 0, NULL
);
1100 if (rect
.right
< descr
->width
)
1102 rect
.left
= rect
.right
;
1103 rect
.right
= descr
->width
;
1105 rect
.bottom
= descr
->height
;
1106 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1107 &rect
, NULL
, 0, NULL
);
1110 if (oldFont
) SelectObject( hdc
, oldFont
);
1111 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1116 /***********************************************************************
1117 * LISTBOX_InvalidateItems
1119 * Invalidate all items from a given item. If the specified item is not
1120 * visible, nothing happens.
1122 static void LISTBOX_InvalidateItems( LB_DESCR
*descr
, INT index
)
1126 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1128 if (descr
->style
& LBS_NOREDRAW
)
1130 descr
->style
|= LBS_DISPLAYCHANGED
;
1133 rect
.bottom
= descr
->height
;
1134 InvalidateRect( descr
->self
, &rect
, TRUE
);
1135 if (descr
->style
& LBS_MULTICOLUMN
)
1137 /* Repaint the other columns */
1138 rect
.left
= rect
.right
;
1139 rect
.right
= descr
->width
;
1141 InvalidateRect( descr
->self
, &rect
, TRUE
);
1146 static void LISTBOX_InvalidateItemRect( LB_DESCR
*descr
, INT index
)
1150 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1151 InvalidateRect( descr
->self
, &rect
, TRUE
);
1154 /***********************************************************************
1155 * LISTBOX_GetItemHeight
1157 static LRESULT
LISTBOX_GetItemHeight( const LB_DESCR
*descr
, INT index
)
1159 if (descr
->style
& LBS_OWNERDRAWVARIABLE
&& descr
->nb_items
> 0)
1161 if ((index
< 0) || (index
>= descr
->nb_items
))
1163 SetLastError(ERROR_INVALID_INDEX
);
1166 return descr
->items
[index
].height
;
1168 else return descr
->item_height
;
1172 /***********************************************************************
1173 * LISTBOX_SetItemHeight
1175 static LRESULT
LISTBOX_SetItemHeight( LB_DESCR
*descr
, INT index
, INT height
, BOOL repaint
)
1177 if (height
> MAXBYTE
)
1180 if (!height
) height
= 1;
1182 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1184 if ((index
< 0) || (index
>= descr
->nb_items
))
1186 SetLastError(ERROR_INVALID_INDEX
);
1189 TRACE("[%p]: item %d height = %d\n", descr
->self
, index
, height
);
1190 descr
->items
[index
].height
= height
;
1191 LISTBOX_UpdateScroll( descr
);
1193 LISTBOX_InvalidateItems( descr
, index
);
1195 else if (height
!= descr
->item_height
)
1197 TRACE("[%p]: new height = %d\n", descr
->self
, height
);
1198 descr
->item_height
= height
;
1199 LISTBOX_UpdatePage( descr
);
1200 LISTBOX_UpdateScroll( descr
);
1202 InvalidateRect( descr
->self
, 0, TRUE
);
1208 /***********************************************************************
1209 * LISTBOX_SetHorizontalPos
1211 static void LISTBOX_SetHorizontalPos( LB_DESCR
*descr
, INT pos
)
1215 if (pos
> descr
->horz_extent
- descr
->width
)
1216 pos
= descr
->horz_extent
- descr
->width
;
1217 if (pos
< 0) pos
= 0;
1218 if (!(diff
= descr
->horz_pos
- pos
)) return;
1219 TRACE("[%p]: new horz pos = %d\n", descr
->self
, pos
);
1220 descr
->horz_pos
= pos
;
1221 LISTBOX_UpdateScroll( descr
);
1222 if (abs(diff
) < descr
->width
)
1225 /* Invalidate the focused item so it will be repainted correctly */
1226 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1227 InvalidateRect( descr
->self
, &rect
, TRUE
);
1228 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
1229 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1232 InvalidateRect( descr
->self
, NULL
, TRUE
);
1236 /***********************************************************************
1237 * LISTBOX_SetHorizontalExtent
1239 static LRESULT
LISTBOX_SetHorizontalExtent( LB_DESCR
*descr
, INT extent
)
1241 if (descr
->style
& LBS_MULTICOLUMN
)
1243 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1244 TRACE("[%p]: new horz extent = %d\n", descr
->self
, extent
);
1245 descr
->horz_extent
= extent
;
1246 if (descr
->style
& WS_HSCROLL
) {
1248 info
.cbSize
= sizeof(info
);
1250 info
.nMax
= descr
->horz_extent
? descr
->horz_extent
- 1 : 0;
1251 info
.fMask
= SIF_RANGE
;
1252 if (descr
->style
& LBS_DISABLENOSCROLL
)
1253 info
.fMask
|= SIF_DISABLENOSCROLL
;
1254 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
1256 if (descr
->horz_pos
> extent
- descr
->width
)
1257 LISTBOX_SetHorizontalPos( descr
, extent
- descr
->width
);
1262 /***********************************************************************
1263 * LISTBOX_SetColumnWidth
1265 static LRESULT
LISTBOX_SetColumnWidth( LB_DESCR
*descr
, INT column_width
)
1269 TRACE("[%p]: new column width = %d\n", descr
->self
, column_width
);
1271 GetClientRect(descr
->self
, &rect
);
1272 descr
->width
= rect
.right
- rect
.left
;
1273 descr
->height
= rect
.bottom
- rect
.top
;
1274 descr
->column_width
= column_width
;
1276 LISTBOX_UpdatePage(descr
);
1277 LISTBOX_UpdateScroll(descr
);
1282 /***********************************************************************
1285 * Returns the item height.
1287 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1291 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1296 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1298 ERR("unable to get DC.\n" );
1301 if (font
) oldFont
= SelectObject( hdc
, font
);
1302 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1303 if (oldFont
) SelectObject( hdc
, oldFont
);
1304 ReleaseDC( descr
->self
, hdc
);
1306 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1307 if (!IS_OWNERDRAW(descr
))
1308 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1313 /***********************************************************************
1314 * LISTBOX_MakeItemVisible
1316 * Make sure that a given item is partially or fully visible.
1318 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1322 TRACE("current top item %d, index %d, fully %d\n", descr
->top_item
, index
, fully
);
1324 if (index
<= descr
->top_item
) top
= index
;
1325 else if (descr
->style
& LBS_MULTICOLUMN
)
1327 INT cols
= descr
->width
;
1328 if (!fully
) cols
+= descr
->column_width
- 1;
1329 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1331 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1332 top
= index
- descr
->page_size
* (cols
- 1);
1334 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1336 INT height
= fully
? descr
->items
[index
].height
: 1;
1337 for (top
= index
; top
> descr
->top_item
; top
--)
1338 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1342 if (index
< descr
->top_item
+ descr
->page_size
) return;
1343 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1344 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1345 top
= index
- descr
->page_size
+ 1;
1347 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1350 /***********************************************************************
1351 * LISTBOX_SetCaretIndex
1354 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1357 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1359 INT oldfocus
= descr
->focus_item
;
1361 TRACE("old focus %d, index %d\n", oldfocus
, index
);
1363 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1364 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1365 if (index
== oldfocus
) return LB_OKAY
;
1367 LISTBOX_DrawFocusRect( descr
, FALSE
);
1368 descr
->focus_item
= index
;
1370 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1371 LISTBOX_DrawFocusRect( descr
, TRUE
);
1377 /***********************************************************************
1378 * LISTBOX_SelectItemRange
1380 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1382 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1387 /* A few sanity checks */
1389 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1390 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1392 if (!descr
->nb_items
) return LB_OKAY
;
1394 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1395 if (first
< 0) first
= 0;
1396 if (last
< first
) return LB_OKAY
;
1398 if (on
) /* Turn selection on */
1400 for (i
= first
; i
<= last
; i
++)
1402 if (descr
->items
[i
].selected
) continue;
1403 descr
->items
[i
].selected
= TRUE
;
1404 LISTBOX_InvalidateItemRect(descr
, i
);
1407 else /* Turn selection off */
1409 for (i
= first
; i
<= last
; i
++)
1411 if (!descr
->items
[i
].selected
) continue;
1412 descr
->items
[i
].selected
= FALSE
;
1413 LISTBOX_InvalidateItemRect(descr
, i
);
1419 /***********************************************************************
1420 * LISTBOX_SetSelection
1422 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1423 BOOL on
, BOOL send_notify
)
1425 TRACE( "cur_sel=%d index=%d notify=%s\n",
1426 descr
->selected_item
, index
, send_notify
? "YES" : "NO" );
1428 if (descr
->style
& LBS_NOSEL
)
1430 descr
->selected_item
= index
;
1433 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1434 if (descr
->style
& LBS_MULTIPLESEL
)
1436 if (index
== -1) /* Select all items */
1437 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1438 else /* Only one item */
1439 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1443 INT oldsel
= descr
->selected_item
;
1444 if (index
== oldsel
) return LB_OKAY
;
1445 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1446 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1447 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1448 descr
->selected_item
= index
;
1449 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1450 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1451 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1453 if( descr
->lphc
) /* set selection change flag for parent combo */
1454 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1460 /***********************************************************************
1463 * Change the caret position and extend the selection to the new caret.
1465 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1467 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1469 if ((index
< 0) || (index
>= descr
->nb_items
))
1472 /* Important, repaint needs to be done in this order if
1473 you want to mimic Windows behavior:
1474 1. Remove the focus and paint the item
1475 2. Remove the selection and paint the item(s)
1476 3. Set the selection and repaint the item(s)
1477 4. Set the focus to 'index' and repaint the item */
1479 /* 1. remove the focus and repaint the item */
1480 LISTBOX_DrawFocusRect( descr
, FALSE
);
1482 /* 2. then turn off the previous selection */
1483 /* 3. repaint the new selected item */
1484 if (descr
->style
& LBS_EXTENDEDSEL
)
1486 if (descr
->anchor_item
!= -1)
1488 INT first
= min( index
, descr
->anchor_item
);
1489 INT last
= max( index
, descr
->anchor_item
);
1491 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1492 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1493 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1496 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1498 /* Set selection to new caret item */
1499 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1502 /* 4. repaint the new item with the focus */
1503 descr
->focus_item
= index
;
1504 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1505 LISTBOX_DrawFocusRect( descr
, TRUE
);
1509 /***********************************************************************
1510 * LISTBOX_InsertItem
1512 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1513 LPWSTR str
, ULONG_PTR data
)
1517 INT oldfocus
= descr
->focus_item
;
1519 if (index
== -1) index
= descr
->nb_items
;
1520 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1521 if (!descr
->items
) max_items
= 0;
1522 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1523 if (descr
->nb_items
== max_items
)
1525 /* We need to grow the array */
1526 max_items
+= LB_ARRAY_GRANULARITY
;
1528 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1529 max_items
* sizeof(LB_ITEMDATA
) );
1531 item
= HeapAlloc( GetProcessHeap(), 0,
1532 max_items
* sizeof(LB_ITEMDATA
) );
1535 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1538 descr
->items
= item
;
1541 /* Insert the item structure */
1543 item
= &descr
->items
[index
];
1544 if (index
< descr
->nb_items
)
1545 RtlMoveMemory( item
+ 1, item
,
1546 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1548 item
->data
= HAS_STRINGS(descr
) ? 0 : data
;
1550 item
->selected
= FALSE
;
1553 /* Get item height */
1555 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1557 MEASUREITEMSTRUCT mis
;
1558 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1560 mis
.CtlType
= ODT_LISTBOX
;
1563 mis
.itemData
= str
? (ULONG_PTR
)str
: data
;
1564 mis
.itemHeight
= descr
->item_height
;
1565 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1566 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1567 TRACE("[%p]: measure item %d (%s) = %d\n",
1568 descr
->self
, index
, str
? debugstr_w(str
) : "", item
->height
);
1571 /* Repaint the items */
1573 LISTBOX_UpdateScroll( descr
);
1574 LISTBOX_InvalidateItems( descr
, index
);
1576 /* Move selection and focused item */
1577 /* If listbox was empty, set focus to the first item */
1578 if (descr
->nb_items
== 1)
1579 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1580 /* single select don't change selection index in win31 */
1581 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1583 descr
->selected_item
++;
1584 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1588 if (index
<= descr
->selected_item
)
1590 descr
->selected_item
++;
1591 descr
->focus_item
= oldfocus
; /* focus not changed */
1598 /***********************************************************************
1599 * LISTBOX_InsertString
1601 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
, LPCWSTR str
)
1603 LPWSTR new_str
= NULL
;
1606 if (HAS_STRINGS(descr
))
1608 static const WCHAR empty_stringW
[] = { 0 };
1609 if (!str
) str
= empty_stringW
;
1610 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1612 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1615 strcpyW(new_str
, str
);
1618 if (index
== -1) index
= descr
->nb_items
;
1619 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, (ULONG_PTR
)str
)) != 0)
1621 HeapFree( GetProcessHeap(), 0, new_str
);
1625 TRACE("[%p]: added item %d %s\n",
1626 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1631 /***********************************************************************
1632 * LISTBOX_DeleteItem
1634 * Delete the content of an item. 'index' must be a valid index.
1636 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1638 /* save the item data before it gets freed by LB_RESETCONTENT */
1639 ULONG_PTR item_data
= descr
->items
[index
].data
;
1640 LPWSTR item_str
= descr
->items
[index
].str
;
1642 if (!descr
->nb_items
)
1643 SendMessageW( descr
->self
, LB_RESETCONTENT
, 0, 0 );
1645 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1646 * while Win95 sends it for all items with user data.
1647 * It's probably better to send it too often than not
1648 * often enough, so this is what we do here.
1650 if (IS_OWNERDRAW(descr
) || item_data
)
1652 DELETEITEMSTRUCT dis
;
1653 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1655 dis
.CtlType
= ODT_LISTBOX
;
1658 dis
.hwndItem
= descr
->self
;
1659 dis
.itemData
= item_data
;
1660 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1662 if (HAS_STRINGS(descr
))
1663 HeapFree( GetProcessHeap(), 0, item_str
);
1667 /***********************************************************************
1668 * LISTBOX_RemoveItem
1670 * Remove an item from the listbox and delete its content.
1672 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1677 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1679 /* We need to invalidate the original rect instead of the updated one. */
1680 LISTBOX_InvalidateItems( descr
, index
);
1683 LISTBOX_DeleteItem( descr
, index
);
1685 if (!descr
->nb_items
) return LB_OKAY
;
1687 /* Remove the item */
1689 item
= &descr
->items
[index
];
1690 if (index
< descr
->nb_items
)
1691 RtlMoveMemory( item
, item
+ 1,
1692 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1693 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1695 /* Shrink the item array if possible */
1697 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1698 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1700 max_items
-= LB_ARRAY_GRANULARITY
;
1701 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1702 max_items
* sizeof(LB_ITEMDATA
) );
1703 if (item
) descr
->items
= item
;
1705 /* Repaint the items */
1707 LISTBOX_UpdateScroll( descr
);
1708 /* if we removed the scrollbar, reset the top of the list
1709 (correct for owner-drawn ???) */
1710 if (descr
->nb_items
== descr
->page_size
)
1711 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1713 /* Move selection and focused item */
1714 if (!IS_MULTISELECT(descr
))
1716 if (index
== descr
->selected_item
)
1717 descr
->selected_item
= -1;
1718 else if (index
< descr
->selected_item
)
1720 descr
->selected_item
--;
1721 if (ISWIN31
) /* win 31 do not change the selected item number */
1722 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1726 if (descr
->focus_item
>= descr
->nb_items
)
1728 descr
->focus_item
= descr
->nb_items
- 1;
1729 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1735 /***********************************************************************
1736 * LISTBOX_ResetContent
1738 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1742 for(i
= descr
->nb_items
- 1; i
>=0; i
--) LISTBOX_DeleteItem( descr
, i
);
1743 HeapFree( GetProcessHeap(), 0, descr
->items
);
1744 descr
->nb_items
= 0;
1745 descr
->top_item
= 0;
1746 descr
->selected_item
= -1;
1747 descr
->focus_item
= 0;
1748 descr
->anchor_item
= -1;
1749 descr
->items
= NULL
;
1753 /***********************************************************************
1756 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, INT count
)
1760 if (HAS_STRINGS(descr
))
1762 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1766 /* FIXME: this is far from optimal... */
1767 if (count
> descr
->nb_items
)
1769 while (count
> descr
->nb_items
)
1770 if ((ret
= LISTBOX_InsertString( descr
, -1, 0 )) < 0)
1773 else if (count
< descr
->nb_items
)
1775 while (count
< descr
->nb_items
)
1776 if ((ret
= LISTBOX_RemoveItem( descr
, (descr
->nb_items
- 1) )) < 0)
1780 InvalidateRect( descr
->self
, NULL
, TRUE
);
1785 /***********************************************************************
1788 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1789 LPCWSTR filespec
, BOOL long_names
)
1792 LRESULT ret
= LB_OKAY
;
1793 WIN32_FIND_DATAW entry
;
1795 LRESULT maxinsert
= LB_ERR
;
1797 /* don't scan directory if we just want drives exclusively */
1798 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1799 /* scan directory */
1800 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1802 int le
= GetLastError();
1803 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1810 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1812 static const WCHAR bracketW
[] = { ']',0 };
1813 static const WCHAR dotW
[] = { '.',0 };
1814 if (!(attrib
& DDL_DIRECTORY
) ||
1815 !strcmpW( entry
.cFileName
, dotW
)) continue;
1817 if (!long_names
&& entry
.cAlternateFileName
[0])
1818 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1820 strcpyW( buffer
+ 1, entry
.cFileName
);
1821 strcatW(buffer
, bracketW
);
1823 else /* not a directory */
1825 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1826 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1828 if ((attrib
& DDL_EXCLUSIVE
) &&
1829 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1832 if (!long_names
&& entry
.cAlternateFileName
[0])
1833 strcpyW( buffer
, entry
.cAlternateFileName
);
1835 strcpyW( buffer
, entry
.cFileName
);
1837 if (!long_names
) CharLowerW( buffer
);
1838 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1839 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1841 if (ret
<= maxinsert
) maxinsert
++; else maxinsert
= ret
;
1842 } while (FindNextFileW( handle
, &entry
));
1843 FindClose( handle
);
1851 if (attrib
& DDL_DRIVES
)
1853 WCHAR buffer
[] = {'[','-','a','-',']',0};
1854 WCHAR root
[] = {'A',':','\\',0};
1856 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1858 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1859 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1868 /***********************************************************************
1869 * LISTBOX_HandleVScroll
1871 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1875 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1879 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1882 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1885 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1886 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1889 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1890 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1892 case SB_THUMBPOSITION
:
1893 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1896 info
.cbSize
= sizeof(info
);
1897 info
.fMask
= SIF_TRACKPOS
;
1898 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1899 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1902 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1905 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1912 /***********************************************************************
1913 * LISTBOX_HandleHScroll
1915 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1920 if (descr
->style
& LBS_MULTICOLUMN
)
1925 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
1929 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
1933 page
= descr
->width
/ descr
->column_width
;
1934 if (page
< 1) page
= 1;
1935 LISTBOX_SetTopItem( descr
,
1936 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1939 page
= descr
->width
/ descr
->column_width
;
1940 if (page
< 1) page
= 1;
1941 LISTBOX_SetTopItem( descr
,
1942 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1944 case SB_THUMBPOSITION
:
1945 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
1948 info
.cbSize
= sizeof(info
);
1949 info
.fMask
= SIF_TRACKPOS
;
1950 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1951 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
1955 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1958 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1962 else if (descr
->horz_extent
)
1967 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
1970 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
1973 LISTBOX_SetHorizontalPos( descr
,
1974 descr
->horz_pos
- descr
->width
);
1977 LISTBOX_SetHorizontalPos( descr
,
1978 descr
->horz_pos
+ descr
->width
);
1980 case SB_THUMBPOSITION
:
1981 LISTBOX_SetHorizontalPos( descr
, pos
);
1984 info
.cbSize
= sizeof(info
);
1985 info
.fMask
= SIF_TRACKPOS
;
1986 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
1987 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
1990 LISTBOX_SetHorizontalPos( descr
, 0 );
1993 LISTBOX_SetHorizontalPos( descr
,
1994 descr
->horz_extent
- descr
->width
);
2001 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
2003 UINT pulScrollLines
= 3;
2005 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
2007 /* if scrolling changes direction, ignore left overs */
2008 if ((delta
< 0 && descr
->wheel_remain
< 0) ||
2009 (delta
> 0 && descr
->wheel_remain
> 0))
2010 descr
->wheel_remain
+= delta
;
2012 descr
->wheel_remain
= delta
;
2014 if (descr
->wheel_remain
&& pulScrollLines
)
2017 pulScrollLines
= min((UINT
) descr
->page_size
, pulScrollLines
);
2018 cLineScroll
= pulScrollLines
* (float)descr
->wheel_remain
/ WHEEL_DELTA
;
2019 descr
->wheel_remain
-= WHEEL_DELTA
* cLineScroll
/ (int)pulScrollLines
;
2020 LISTBOX_SetTopItem( descr
, descr
->top_item
- cLineScroll
, TRUE
);
2025 /***********************************************************************
2026 * LISTBOX_HandleLButtonDown
2028 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
2030 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2032 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2033 descr
->self
, x
, y
, index
, descr
->focus_item
);
2035 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2037 if (!descr
->in_focus
)
2039 if( !descr
->lphc
) SetFocus( descr
->self
);
2040 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2043 if (index
== -1) return 0;
2047 if (descr
->style
& LBS_NOTIFY
)
2048 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2049 MAKELPARAM( x
, y
) );
2052 descr
->captured
= TRUE
;
2053 SetCapture( descr
->self
);
2055 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2057 /* we should perhaps make sure that all items are deselected
2058 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2059 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2060 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2063 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2064 if (keys
& MK_CONTROL
)
2066 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2067 LISTBOX_SetSelection( descr
, index
,
2068 !descr
->items
[index
].selected
,
2069 (descr
->style
& LBS_NOTIFY
) != 0);
2073 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2075 if (descr
->style
& LBS_EXTENDEDSEL
)
2077 LISTBOX_SetSelection( descr
, index
,
2078 descr
->items
[index
].selected
,
2079 (descr
->style
& LBS_NOTIFY
) != 0 );
2083 LISTBOX_SetSelection( descr
, index
,
2084 !descr
->items
[index
].selected
,
2085 (descr
->style
& LBS_NOTIFY
) != 0 );
2091 descr
->anchor_item
= index
;
2092 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2093 LISTBOX_SetSelection( descr
, index
,
2094 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2099 if (GetWindowLongW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2106 if (DragDetect( descr
->self
, pt
))
2107 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2114 /*************************************************************************
2115 * LISTBOX_HandleLButtonDownCombo [Internal]
2117 * Process LButtonDown message for the ComboListBox
2120 * pWnd [I] The windows internal structure
2121 * pDescr [I] The ListBox internal structure
2122 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2123 * x [I] X Mouse Coordinate
2124 * y [I] Y Mouse Coordinate
2127 * 0 since we are processing the WM_LBUTTONDOWN Message
2130 * This function is only to be used when a ListBox is a ComboListBox
2133 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2135 RECT clientRect
, screenRect
;
2141 GetClientRect(descr
->self
, &clientRect
);
2143 if(PtInRect(&clientRect
, mousePos
))
2145 /* MousePos is in client, resume normal processing */
2146 if (msg
== WM_LBUTTONDOWN
)
2148 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2149 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2151 else if (descr
->style
& LBS_NOTIFY
)
2152 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2156 POINT screenMousePos
;
2157 HWND hWndOldCapture
;
2159 /* Check the Non-Client Area */
2160 screenMousePos
= mousePos
;
2161 hWndOldCapture
= GetCapture();
2163 GetWindowRect(descr
->self
, &screenRect
);
2164 ClientToScreen(descr
->self
, &screenMousePos
);
2166 if(!PtInRect(&screenRect
, screenMousePos
))
2168 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2169 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2170 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2174 /* Check to see the NC is a scrollbar */
2176 LONG style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2177 /* Check Vertical scroll bar */
2178 if (style
& WS_VSCROLL
)
2180 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2181 if (PtInRect( &clientRect
, mousePos
))
2182 nHitTestType
= HTVSCROLL
;
2184 /* Check horizontal scroll bar */
2185 if (style
& WS_HSCROLL
)
2187 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2188 if (PtInRect( &clientRect
, mousePos
))
2189 nHitTestType
= HTHSCROLL
;
2191 /* Windows sends this message when a scrollbar is clicked
2194 if(nHitTestType
!= 0)
2196 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2197 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2199 /* Resume the Capture after scrolling is complete
2201 if(hWndOldCapture
!= 0)
2202 SetCapture(hWndOldCapture
);
2208 /***********************************************************************
2209 * LISTBOX_HandleLButtonUp
2211 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2213 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2214 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2215 LISTBOX_Timer
= LB_TIMER_NONE
;
2216 if (descr
->captured
)
2218 descr
->captured
= FALSE
;
2219 if (GetCapture() == descr
->self
) ReleaseCapture();
2220 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2221 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2227 /***********************************************************************
2228 * LISTBOX_HandleTimer
2230 * Handle scrolling upon a timer event.
2231 * Return TRUE if scrolling should continue.
2233 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2238 if (descr
->top_item
) index
= descr
->top_item
- 1;
2242 if (descr
->top_item
) index
-= descr
->page_size
;
2245 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2246 if (index
== descr
->focus_item
) index
++;
2247 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2249 case LB_TIMER_RIGHT
:
2250 if (index
+ descr
->page_size
< descr
->nb_items
)
2251 index
+= descr
->page_size
;
2256 if (index
== descr
->focus_item
) return FALSE
;
2257 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2262 /***********************************************************************
2263 * LISTBOX_HandleSystemTimer
2265 * WM_SYSTIMER handler.
2267 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2269 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2271 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2272 LISTBOX_Timer
= LB_TIMER_NONE
;
2278 /***********************************************************************
2279 * LISTBOX_HandleMouseMove
2281 * WM_MOUSEMOVE handler.
2283 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2287 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2289 if (!descr
->captured
) return;
2291 if (descr
->style
& LBS_MULTICOLUMN
)
2294 else if (y
>= descr
->item_height
* descr
->page_size
)
2295 y
= descr
->item_height
* descr
->page_size
- 1;
2299 dir
= LB_TIMER_LEFT
;
2302 else if (x
>= descr
->width
)
2304 dir
= LB_TIMER_RIGHT
;
2305 x
= descr
->width
- 1;
2310 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2311 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2314 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2315 if (index
== -1) index
= descr
->focus_item
;
2316 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2318 /* Start/stop the system timer */
2320 if (dir
!= LB_TIMER_NONE
)
2321 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2322 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2323 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2324 LISTBOX_Timer
= dir
;
2328 /***********************************************************************
2329 * LISTBOX_HandleKeyDown
2331 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2334 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2335 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2336 bForceSelection
= FALSE
; /* only for single select list */
2338 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2340 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2341 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2342 (LPARAM
)descr
->self
);
2343 if (caret
== -2) return 0;
2345 if (caret
== -1) switch(key
)
2348 if (descr
->style
& LBS_MULTICOLUMN
)
2350 bForceSelection
= FALSE
;
2351 if (descr
->focus_item
>= descr
->page_size
)
2352 caret
= descr
->focus_item
- descr
->page_size
;
2357 caret
= descr
->focus_item
- 1;
2358 if (caret
< 0) caret
= 0;
2361 if (descr
->style
& LBS_MULTICOLUMN
)
2363 bForceSelection
= FALSE
;
2364 caret
= min(descr
->focus_item
+ descr
->page_size
, descr
->nb_items
- 1);
2369 caret
= descr
->focus_item
+ 1;
2370 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2374 if (descr
->style
& LBS_MULTICOLUMN
)
2376 INT page
= descr
->width
/ descr
->column_width
;
2377 if (page
< 1) page
= 1;
2378 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2380 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2381 if (caret
< 0) caret
= 0;
2384 if (descr
->style
& LBS_MULTICOLUMN
)
2386 INT page
= descr
->width
/ descr
->column_width
;
2387 if (page
< 1) page
= 1;
2388 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2390 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2391 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2397 caret
= descr
->nb_items
- 1;
2400 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2401 else if (descr
->style
& LBS_MULTIPLESEL
)
2403 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2404 !descr
->items
[descr
->focus_item
].selected
,
2405 (descr
->style
& LBS_NOTIFY
) != 0 );
2409 bForceSelection
= FALSE
;
2411 if (bForceSelection
) /* focused item is used instead of key */
2412 caret
= descr
->focus_item
;
2415 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2416 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2417 !IS_MULTISELECT(descr
))
2418 descr
->anchor_item
= caret
;
2419 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2421 if (descr
->style
& LBS_MULTIPLESEL
)
2422 descr
->selected_item
= caret
;
2424 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2425 if (descr
->style
& LBS_NOTIFY
)
2427 if (descr
->lphc
&& IsWindowVisible( descr
->self
))
2429 /* make sure that combo parent doesn't hide us */
2430 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2432 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2439 /***********************************************************************
2440 * LISTBOX_HandleChar
2442 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2450 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2452 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2453 MAKEWPARAM(charW
, descr
->focus_item
),
2454 (LPARAM
)descr
->self
);
2455 if (caret
== -2) return 0;
2458 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2461 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2462 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2463 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2464 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2465 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2471 /***********************************************************************
2474 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2477 MEASUREITEMSTRUCT mis
;
2480 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2483 GetClientRect( hwnd
, &rect
);
2485 descr
->owner
= GetParent( descr
->self
);
2486 descr
->style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2487 descr
->width
= rect
.right
- rect
.left
;
2488 descr
->height
= rect
.bottom
- rect
.top
;
2489 descr
->items
= NULL
;
2490 descr
->nb_items
= 0;
2491 descr
->top_item
= 0;
2492 descr
->selected_item
= -1;
2493 descr
->focus_item
= 0;
2494 descr
->anchor_item
= -1;
2495 descr
->item_height
= 1;
2496 descr
->page_size
= 1;
2497 descr
->column_width
= 150;
2498 descr
->horz_extent
= 0;
2499 descr
->horz_pos
= 0;
2502 descr
->wheel_remain
= 0;
2503 descr
->caret_on
= !lphc
;
2504 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2505 descr
->in_focus
= FALSE
;
2506 descr
->captured
= FALSE
;
2508 descr
->locale
= GetUserDefaultLCID();
2513 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2514 descr
->owner
= lphc
->self
;
2517 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2519 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2521 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2522 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2523 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2524 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2526 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2528 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2530 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2531 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2535 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2536 mis
.CtlType
= ODT_LISTBOX
;
2541 mis
.itemHeight
= descr
->item_height
;
2542 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2543 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2547 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2552 /***********************************************************************
2555 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2557 LISTBOX_ResetContent( descr
);
2558 SetWindowLongPtrW( descr
->self
, 0, 0 );
2559 HeapFree( GetProcessHeap(), 0, descr
);
2564 /***********************************************************************
2565 * ListBoxWndProc_common
2567 LRESULT
ListBoxWndProc_common( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2569 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2570 LPHEADCOMBO lphc
= 0;
2575 if (!IsWindow(hwnd
)) return 0;
2577 if (msg
== WM_CREATE
)
2579 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2580 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= lpcs
->lpCreateParams
;
2581 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2582 TRACE("creating hwnd %p descr %p\n", hwnd
, (void *)GetWindowLongPtrW( hwnd
, 0 ) );
2585 /* Ignore all other messages before we get a WM_CREATE */
2586 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2587 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2589 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2591 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2592 descr
->self
, SPY_GetMsgName(msg
, descr
->self
), wParam
, lParam
);
2596 case LB_RESETCONTENT
:
2597 LISTBOX_ResetContent( descr
);
2598 LISTBOX_UpdateScroll( descr
);
2599 InvalidateRect( descr
->self
, NULL
, TRUE
);
2606 if(unicode
|| !HAS_STRINGS(descr
))
2607 textW
= (LPWSTR
)lParam
;
2610 LPSTR textA
= (LPSTR
)lParam
;
2611 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2612 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2613 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2617 wParam
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2618 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2619 if (!unicode
&& HAS_STRINGS(descr
))
2620 HeapFree(GetProcessHeap(), 0, textW
);
2624 case LB_INSERTSTRING
:
2628 if(unicode
|| !HAS_STRINGS(descr
))
2629 textW
= (LPWSTR
)lParam
;
2632 LPSTR textA
= (LPSTR
)lParam
;
2633 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2634 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2635 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2639 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2640 if(!unicode
&& HAS_STRINGS(descr
))
2641 HeapFree(GetProcessHeap(), 0, textW
);
2649 if(unicode
|| !HAS_STRINGS(descr
))
2650 textW
= (LPWSTR
)lParam
;
2653 LPSTR textA
= (LPSTR
)lParam
;
2654 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2655 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2656 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2660 wParam
= LISTBOX_FindFileStrPos( descr
, textW
);
2661 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2662 if(!unicode
&& HAS_STRINGS(descr
))
2663 HeapFree(GetProcessHeap(), 0, textW
);
2667 case LB_DELETESTRING
:
2668 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2669 return descr
->nb_items
;
2672 SetLastError(ERROR_INVALID_INDEX
);
2676 case LB_GETITEMDATA
:
2677 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2679 SetLastError(ERROR_INVALID_INDEX
);
2682 return descr
->items
[wParam
].data
;
2684 case LB_SETITEMDATA
:
2685 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2687 SetLastError(ERROR_INVALID_INDEX
);
2690 descr
->items
[wParam
].data
= lParam
;
2691 /* undocumented: returns TRUE, not LB_OKAY (0) */
2695 return descr
->nb_items
;
2698 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, unicode
);
2701 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2703 SetLastError(ERROR_INVALID_INDEX
);
2706 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2707 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2708 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2709 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2712 if (descr
->nb_items
== 0)
2714 if (!IS_MULTISELECT(descr
))
2715 return descr
->selected_item
;
2716 if (descr
->selected_item
!= -1)
2717 return descr
->selected_item
;
2718 return descr
->focus_item
;
2719 /* otherwise, if the user tries to move the selection with the */
2720 /* arrow keys, we will give the application something to choke on */
2721 case LB_GETTOPINDEX
:
2722 return descr
->top_item
;
2724 case LB_GETITEMHEIGHT
:
2725 return LISTBOX_GetItemHeight( descr
, wParam
);
2727 case LB_SETITEMHEIGHT
:
2728 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2730 case LB_ITEMFROMPOINT
:
2737 /* The hiword of the return value is not a client area
2738 hittest as suggested by MSDN, but rather a hittest on
2739 the returned listbox item. */
2741 if(descr
->nb_items
== 0)
2742 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2744 pt
.x
= (short)LOWORD(lParam
);
2745 pt
.y
= (short)HIWORD(lParam
);
2747 SetRect(&rect
, 0, 0, descr
->width
, descr
->height
);
2749 if(!PtInRect(&rect
, pt
))
2751 pt
.x
= min(pt
.x
, rect
.right
- 1);
2752 pt
.x
= max(pt
.x
, 0);
2753 pt
.y
= min(pt
.y
, rect
.bottom
- 1);
2754 pt
.y
= max(pt
.y
, 0);
2758 index
= LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
);
2762 index
= descr
->nb_items
- 1;
2765 return MAKELONG(index
, hit
? 0 : 1);
2768 case LB_SETCARETINDEX
:
2769 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2770 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2777 case LB_GETCARETINDEX
:
2778 return descr
->focus_item
;
2780 case LB_SETTOPINDEX
:
2781 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2783 case LB_SETCOLUMNWIDTH
:
2784 return LISTBOX_SetColumnWidth( descr
, wParam
);
2786 case LB_GETITEMRECT
:
2787 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2793 if(unicode
|| !HAS_STRINGS(descr
))
2794 textW
= (LPWSTR
)lParam
;
2797 LPSTR textA
= (LPSTR
)lParam
;
2798 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2799 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2800 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2802 ret
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2803 if(!unicode
&& HAS_STRINGS(descr
))
2804 HeapFree(GetProcessHeap(), 0, textW
);
2808 case LB_FINDSTRINGEXACT
:
2812 if(unicode
|| !HAS_STRINGS(descr
))
2813 textW
= (LPWSTR
)lParam
;
2816 LPSTR textA
= (LPSTR
)lParam
;
2817 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2818 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2819 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2821 ret
= LISTBOX_FindString( descr
, wParam
, textW
, TRUE
);
2822 if(!unicode
&& HAS_STRINGS(descr
))
2823 HeapFree(GetProcessHeap(), 0, textW
);
2827 case LB_SELECTSTRING
:
2832 if(HAS_STRINGS(descr
))
2833 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2834 debugstr_a((LPSTR
)lParam
));
2835 if(unicode
|| !HAS_STRINGS(descr
))
2836 textW
= (LPWSTR
)lParam
;
2839 LPSTR textA
= (LPSTR
)lParam
;
2840 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2841 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2842 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2844 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2845 if(!unicode
&& HAS_STRINGS(descr
))
2846 HeapFree(GetProcessHeap(), 0, textW
);
2847 if (index
!= LB_ERR
)
2849 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2850 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2856 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2858 return descr
->items
[wParam
].selected
;
2861 ret
= LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2862 if (ret
!= LB_ERR
&& wParam
)
2863 descr
->anchor_item
= lParam
;
2867 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2868 LISTBOX_SetCaretIndex( descr
, wParam
, TRUE
);
2869 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2870 if (ret
!= LB_ERR
) ret
= descr
->selected_item
;
2873 case LB_GETSELCOUNT
:
2874 return LISTBOX_GetSelCount( descr
);
2876 case LB_GETSELITEMS
:
2877 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2879 case LB_SELITEMRANGE
:
2880 if (LOWORD(lParam
) <= HIWORD(lParam
))
2881 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2882 HIWORD(lParam
), wParam
);
2884 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2885 LOWORD(lParam
), wParam
);
2887 case LB_SELITEMRANGEEX
:
2888 if ((INT
)lParam
>= (INT
)wParam
)
2889 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2891 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
2893 case LB_GETHORIZONTALEXTENT
:
2894 return descr
->horz_extent
;
2896 case LB_SETHORIZONTALEXTENT
:
2897 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
2899 case LB_GETANCHORINDEX
:
2900 return descr
->anchor_item
;
2902 case LB_SETANCHORINDEX
:
2903 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2905 SetLastError(ERROR_INVALID_INDEX
);
2908 descr
->anchor_item
= (INT
)wParam
;
2916 textW
= (LPWSTR
)lParam
;
2919 LPSTR textA
= (LPSTR
)lParam
;
2920 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2921 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2922 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2924 ret
= LISTBOX_Directory( descr
, wParam
, textW
, msg
== LB_DIR
);
2926 HeapFree(GetProcessHeap(), 0, textW
);
2931 return descr
->locale
;
2936 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
2938 ret
= descr
->locale
;
2939 descr
->locale
= (LCID
)wParam
;
2943 case LB_INITSTORAGE
:
2944 return LISTBOX_InitStorage( descr
, wParam
);
2947 return LISTBOX_SetCount( descr
, (INT
)wParam
);
2949 case LB_SETTABSTOPS
:
2950 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
);
2953 if (descr
->caret_on
)
2955 descr
->caret_on
= TRUE
;
2956 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2957 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2961 if (!descr
->caret_on
)
2963 descr
->caret_on
= FALSE
;
2964 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2965 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2968 case LB_GETLISTBOXINFO
:
2969 return descr
->page_size
;
2972 return LISTBOX_Destroy( descr
);
2975 InvalidateRect( descr
->self
, NULL
, TRUE
);
2979 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
2983 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2985 case WM_PRINTCLIENT
:
2989 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
2990 ret
= LISTBOX_Paint( descr
, hdc
);
2991 if( !wParam
) EndPaint( descr
->self
, &ps
);
2995 LISTBOX_UpdateSize( descr
);
2998 return (LRESULT
)descr
->font
;
3000 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
3001 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
3004 descr
->in_focus
= TRUE
;
3005 descr
->caret_on
= TRUE
;
3006 if (descr
->focus_item
!= -1)
3007 LISTBOX_DrawFocusRect( descr
, TRUE
);
3008 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
3011 LISTBOX_HandleLButtonUp( descr
); /* Release capture if we have it */
3012 descr
->in_focus
= FALSE
;
3013 descr
->wheel_remain
= 0;
3014 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
3015 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3016 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
3019 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3021 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3023 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
3024 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
3025 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
3026 case WM_LBUTTONDOWN
:
3028 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3029 (INT16
)LOWORD(lParam
),
3030 (INT16
)HIWORD(lParam
) );
3031 return LISTBOX_HandleLButtonDown( descr
, wParam
,
3032 (INT16
)LOWORD(lParam
),
3033 (INT16
)HIWORD(lParam
) );
3034 case WM_LBUTTONDBLCLK
:
3036 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3037 (INT16
)LOWORD(lParam
),
3038 (INT16
)HIWORD(lParam
) );
3039 if (descr
->style
& LBS_NOTIFY
)
3040 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
3043 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
3045 BOOL captured
= descr
->captured
;
3049 mousePos
.x
= (INT16
)LOWORD(lParam
);
3050 mousePos
.y
= (INT16
)HIWORD(lParam
);
3053 * If we are in a dropdown combobox, we simulate that
3054 * the mouse is captured to show the tracking of the item.
3056 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
3057 descr
->captured
= TRUE
;
3059 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
3061 descr
->captured
= captured
;
3063 else if (GetCapture() == descr
->self
)
3065 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
3066 (INT16
)HIWORD(lParam
) );
3076 * If the mouse button "up" is not in the listbox,
3077 * we make sure there is no selection by re-selecting the
3078 * item that was selected when the listbox was made visible.
3080 mousePos
.x
= (INT16
)LOWORD(lParam
);
3081 mousePos
.y
= (INT16
)HIWORD(lParam
);
3083 GetClientRect(descr
->self
, &clientRect
);
3086 * When the user clicks outside the combobox and the focus
3087 * is lost, the owning combobox will send a fake buttonup with
3088 * 0xFFFFFFF as the mouse location, we must also revert the
3089 * selection to the original selection.
3091 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
3092 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
3094 return LISTBOX_HandleLButtonUp( descr
);
3096 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3098 /* for some reason Windows makes it possible to
3099 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3101 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3102 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3103 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3105 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3109 return LISTBOX_HandleKeyDown( descr
, wParam
);
3114 charW
= (WCHAR
)wParam
;
3117 CHAR charA
= (CHAR
)wParam
;
3118 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
3120 return LISTBOX_HandleChar( descr
, charW
);
3123 return LISTBOX_HandleSystemTimer( descr
);
3125 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3128 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3129 wParam
, (LPARAM
)descr
->self
);
3130 TRACE("hbrush = %p\n", hbrush
);
3132 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3135 GetClientRect(descr
->self
, &rect
);
3136 FillRect((HDC
)wParam
, &rect
, hbrush
);
3141 if( lphc
) return 0;
3142 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3143 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3146 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3155 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3156 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3157 hwnd
, msg
, wParam
, lParam
);
3160 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3161 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3164 DWORD WINAPI
GetListBoxInfo(HWND hwnd
)
3166 TRACE("%p\n", hwnd
);
3167 return SendMessageW(hwnd
, LB_GETLISTBOXINFO
, 0, 0);