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 diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
306 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
310 if (index
> descr
->top_item
)
312 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
313 diff
-= descr
->items
[i
].height
;
317 for (i
= index
; i
< descr
->top_item
; i
++)
318 diff
+= descr
->items
[i
].height
;
322 diff
= (descr
->top_item
- index
) * descr
->item_height
;
324 ScrollWindowEx( descr
->self
, 0, diff
, NULL
, NULL
, 0, NULL
,
325 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
328 InvalidateRect( descr
->self
, NULL
, TRUE
);
329 descr
->top_item
= index
;
330 LISTBOX_UpdateScroll( descr
);
335 /***********************************************************************
338 * Update the page size. Should be called when the size of
339 * the client area or the item height changes.
341 static void LISTBOX_UpdatePage( LB_DESCR
*descr
)
345 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
347 if (page_size
== descr
->page_size
) return;
348 descr
->page_size
= page_size
;
349 if (descr
->style
& LBS_MULTICOLUMN
)
350 InvalidateRect( descr
->self
, NULL
, TRUE
);
351 LISTBOX_SetTopItem( descr
, descr
->top_item
, FALSE
);
355 /***********************************************************************
358 * Update the size of the listbox. Should be called when the size of
359 * the client area changes.
361 static void LISTBOX_UpdateSize( LB_DESCR
*descr
)
365 GetClientRect( descr
->self
, &rect
);
366 descr
->width
= rect
.right
- rect
.left
;
367 descr
->height
= rect
.bottom
- rect
.top
;
368 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
373 GetWindowRect( descr
->self
, &rect
);
374 if(descr
->item_height
!= 0)
375 remaining
= descr
->height
% descr
->item_height
;
378 if ((descr
->height
> descr
->item_height
) && remaining
)
380 TRACE("[%p]: changing height %d -> %d\n",
381 descr
->self
, descr
->height
, descr
->height
- remaining
);
382 SetWindowPos( descr
->self
, 0, 0, 0, rect
.right
- rect
.left
,
383 rect
.bottom
- rect
.top
- remaining
,
384 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
388 TRACE("[%p]: new size = %d,%d\n", descr
->self
, descr
->width
, descr
->height
);
389 LISTBOX_UpdatePage( descr
);
390 LISTBOX_UpdateScroll( descr
);
392 /* Invalidate the focused item so it will be repainted correctly */
393 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
395 InvalidateRect( descr
->self
, &rect
, FALSE
);
400 /***********************************************************************
401 * LISTBOX_GetItemRect
403 * Get the rectangle enclosing an item, in listbox client coordinates.
404 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
406 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
)
408 /* Index <= 0 is legal even on empty listboxes */
409 if (index
&& (index
>= descr
->nb_items
))
412 SetLastError(ERROR_INVALID_INDEX
);
415 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
416 if (descr
->style
& LBS_MULTICOLUMN
)
418 INT col
= (index
/ descr
->page_size
) -
419 (descr
->top_item
/ descr
->page_size
);
420 rect
->left
+= col
* descr
->column_width
;
421 rect
->right
= rect
->left
+ descr
->column_width
;
422 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
423 rect
->bottom
= rect
->top
+ descr
->item_height
;
425 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
428 rect
->right
+= descr
->horz_pos
;
429 if ((index
>= 0) && (index
< descr
->nb_items
))
431 if (index
< descr
->top_item
)
433 for (i
= descr
->top_item
-1; i
>= index
; i
--)
434 rect
->top
-= descr
->items
[i
].height
;
438 for (i
= descr
->top_item
; i
< index
; i
++)
439 rect
->top
+= descr
->items
[i
].height
;
441 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
447 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
448 rect
->bottom
= rect
->top
+ descr
->item_height
;
449 rect
->right
+= descr
->horz_pos
;
452 TRACE("item %d, rect %s\n", index
, wine_dbgstr_rect(rect
));
454 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
455 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
459 /***********************************************************************
460 * LISTBOX_GetItemFromPoint
462 * Return the item nearest from point (x,y) (in client coordinates).
464 static INT
LISTBOX_GetItemFromPoint( const LB_DESCR
*descr
, INT x
, INT y
)
466 INT index
= descr
->top_item
;
468 if (!descr
->nb_items
) return -1; /* No items */
469 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
474 while (index
< descr
->nb_items
)
476 if ((pos
+= descr
->items
[index
].height
) > y
) break;
485 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
489 else if (descr
->style
& LBS_MULTICOLUMN
)
491 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
492 if (y
>= 0) index
+= y
/ descr
->item_height
;
493 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
494 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
498 index
+= (y
/ descr
->item_height
);
500 if (index
< 0) return 0;
501 if (index
>= descr
->nb_items
) return -1;
506 /***********************************************************************
511 static void LISTBOX_PaintItem( LB_DESCR
*descr
, HDC hdc
, const RECT
*rect
,
512 INT index
, UINT action
, BOOL ignoreFocus
)
514 LB_ITEMDATA
*item
= NULL
;
515 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
517 if (IS_OWNERDRAW(descr
))
525 if (action
== ODA_FOCUS
)
526 DrawFocusRect( hdc
, rect
);
528 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
532 /* some programs mess with the clipping region when
533 drawing the item, *and* restore the previous region
534 after they are done, so a region has better to exist
535 else everything ends clipped */
536 GetClientRect(descr
->self
, &r
);
537 hrgn
= set_control_clipping( hdc
, &r
);
539 dis
.CtlType
= ODT_LISTBOX
;
540 dis
.CtlID
= GetWindowLongPtrW( descr
->self
, GWLP_ID
);
541 dis
.hwndItem
= descr
->self
;
542 dis
.itemAction
= action
;
546 if (item
->selected
) dis
.itemState
|= ODS_SELECTED
;
547 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
549 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
550 if (!IsWindowEnabled(descr
->self
)) dis
.itemState
|= ODS_DISABLED
;
551 dis
.itemData
= item
->data
;
553 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
554 descr
->self
, index
, debugstr_w(item
->str
), action
,
555 dis
.itemState
, wine_dbgstr_rect(rect
) );
556 SendMessageW(descr
->owner
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
557 SelectClipRgn( hdc
, hrgn
);
558 if (hrgn
) DeleteObject( hrgn
);
562 COLORREF oldText
= 0, oldBk
= 0;
564 if (action
== ODA_FOCUS
)
566 DrawFocusRect( hdc
, rect
);
569 if (item
&& item
->selected
)
571 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
572 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
575 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
576 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
577 wine_dbgstr_rect(rect
) );
579 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
580 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
581 else if (!(descr
->style
& LBS_USETABSTOPS
))
582 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
583 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
584 strlenW(item
->str
), NULL
);
587 /* Output empty string to paint background in the full width. */
588 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
589 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
590 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
591 item
->str
, strlenW(item
->str
),
592 descr
->nb_tabs
, descr
->tabs
, 0);
594 if (item
&& item
->selected
)
596 SetBkColor( hdc
, oldBk
);
597 SetTextColor( hdc
, oldText
);
599 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
601 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
606 /***********************************************************************
609 * Change the redraw flag.
611 static void LISTBOX_SetRedraw( LB_DESCR
*descr
, BOOL on
)
615 if (!(descr
->style
& LBS_NOREDRAW
)) return;
616 descr
->style
&= ~LBS_NOREDRAW
;
617 if (descr
->style
& LBS_DISPLAYCHANGED
)
618 { /* page was changed while setredraw false, refresh automatically */
619 InvalidateRect(descr
->self
, NULL
, TRUE
);
620 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
621 { /* reset top of page if less than number of items/page */
622 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
623 if (descr
->top_item
< 0) descr
->top_item
= 0;
625 descr
->style
&= ~LBS_DISPLAYCHANGED
;
627 LISTBOX_UpdateScroll( descr
);
629 else descr
->style
|= LBS_NOREDRAW
;
633 /***********************************************************************
634 * LISTBOX_RepaintItem
636 * Repaint a single item synchronously.
638 static void LISTBOX_RepaintItem( LB_DESCR
*descr
, INT index
, UINT action
)
643 HBRUSH hbrush
, oldBrush
= 0;
645 /* Do not repaint the item if the item is not visible */
646 if (!IsWindowVisible(descr
->self
)) return;
647 if (descr
->style
& LBS_NOREDRAW
)
649 descr
->style
|= LBS_DISPLAYCHANGED
;
652 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
653 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
654 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
655 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
656 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
657 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
658 if (!IsWindowEnabled(descr
->self
))
659 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
660 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
661 LISTBOX_PaintItem( descr
, hdc
, &rect
, index
, action
, TRUE
);
662 if (oldFont
) SelectObject( hdc
, oldFont
);
663 if (oldBrush
) SelectObject( hdc
, oldBrush
);
664 ReleaseDC( descr
->self
, hdc
);
668 /***********************************************************************
669 * LISTBOX_DrawFocusRect
671 static void LISTBOX_DrawFocusRect( LB_DESCR
*descr
, BOOL on
)
677 /* Do not repaint the item if the item is not visible */
678 if (!IsWindowVisible(descr
->self
)) return;
680 if (descr
->focus_item
== -1) return;
681 if (!descr
->caret_on
|| !descr
->in_focus
) return;
683 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) != 1) return;
684 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
685 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
686 if (!IsWindowEnabled(descr
->self
))
687 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
688 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
689 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, !on
);
690 if (oldFont
) SelectObject( hdc
, oldFont
);
691 ReleaseDC( descr
->self
, hdc
);
695 /***********************************************************************
696 * LISTBOX_InitStorage
698 static LRESULT
LISTBOX_InitStorage( LB_DESCR
*descr
, INT nb_items
)
702 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
703 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
705 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
706 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
707 nb_items
* sizeof(LB_ITEMDATA
));
710 item
= HeapAlloc( GetProcessHeap(), 0,
711 nb_items
* sizeof(LB_ITEMDATA
));
716 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
724 /***********************************************************************
725 * LISTBOX_SetTabStops
727 static BOOL
LISTBOX_SetTabStops( LB_DESCR
*descr
, INT count
, LPINT tabs
)
731 if (!(descr
->style
& LBS_USETABSTOPS
))
733 SetLastError(ERROR_LB_WITHOUT_TABSTOPS
);
737 HeapFree( GetProcessHeap(), 0, descr
->tabs
);
738 if (!(descr
->nb_tabs
= count
))
743 if (!(descr
->tabs
= HeapAlloc( GetProcessHeap(), 0,
744 descr
->nb_tabs
* sizeof(INT
) )))
746 memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
748 /* convert into "dialog units"*/
749 for (i
= 0; i
< descr
->nb_tabs
; i
++)
750 descr
->tabs
[i
] = MulDiv(descr
->tabs
[i
], descr
->avg_char_width
, 4);
756 /***********************************************************************
759 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPWSTR buffer
, BOOL unicode
)
763 if ((index
< 0) || (index
>= descr
->nb_items
))
765 SetLastError(ERROR_INVALID_INDEX
);
768 if (HAS_STRINGS(descr
))
772 len
= strlenW(descr
->items
[index
].str
);
775 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[index
].str
, len
,
776 NULL
, 0, NULL
, NULL
);
779 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
781 __TRY
/* hide a Delphi bug that passes a read-only buffer */
785 strcpyW( buffer
, descr
->items
[index
].str
);
786 len
= strlenW(buffer
);
790 len
= WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1,
791 (LPSTR
)buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
796 WARN( "got an invalid buffer (Delphi bug?)\n" );
797 SetLastError( ERROR_INVALID_PARAMETER
);
803 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
809 static inline INT
LISTBOX_lstrcmpiW( LCID lcid
, LPCWSTR str1
, LPCWSTR str2
)
811 INT ret
= CompareStringW( lcid
, NORM_IGNORECASE
, str1
, -1, str2
, -1 );
812 if (ret
== CSTR_LESS_THAN
)
814 if (ret
== CSTR_EQUAL
)
816 if (ret
== CSTR_GREATER_THAN
)
821 /***********************************************************************
822 * LISTBOX_FindStringPos
824 * Find the nearest string located before a given string in sort order.
825 * If 'exact' is TRUE, return an error if we don't get an exact match.
827 static INT
LISTBOX_FindStringPos( LB_DESCR
*descr
, LPCWSTR str
, BOOL exact
)
829 INT index
, min
, max
, res
;
831 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
833 max
= descr
->nb_items
;
836 index
= (min
+ max
) / 2;
837 if (HAS_STRINGS(descr
))
838 res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, descr
->items
[index
].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
= (ULONG_PTR
)str
;
852 cis
.itemData2
= descr
->items
[index
].data
;
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
;
858 else min
= index
+ 1;
860 return exact
? -1 : max
;
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 width
)
1267 if (width
== descr
->column_width
) return LB_OKAY
;
1268 TRACE("[%p]: new column width = %d\n", descr
->self
, width
);
1269 descr
->column_width
= width
;
1270 LISTBOX_UpdatePage( descr
);
1275 /***********************************************************************
1278 * Returns the item height.
1280 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1284 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1289 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1291 ERR("unable to get DC.\n" );
1294 if (font
) oldFont
= SelectObject( hdc
, font
);
1295 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1296 if (oldFont
) SelectObject( hdc
, oldFont
);
1297 ReleaseDC( descr
->self
, hdc
);
1299 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1300 if (!IS_OWNERDRAW(descr
))
1301 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1306 /***********************************************************************
1307 * LISTBOX_MakeItemVisible
1309 * Make sure that a given item is partially or fully visible.
1311 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1315 TRACE("current top item %d, index %d, fully %d\n", descr
->top_item
, index
, fully
);
1317 if (index
<= descr
->top_item
) top
= index
;
1318 else if (descr
->style
& LBS_MULTICOLUMN
)
1320 INT cols
= descr
->width
;
1321 if (!fully
) cols
+= descr
->column_width
- 1;
1322 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1324 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1325 top
= index
- descr
->page_size
* (cols
- 1);
1327 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1329 INT height
= fully
? descr
->items
[index
].height
: 1;
1330 for (top
= index
; top
> descr
->top_item
; top
--)
1331 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1335 if (index
< descr
->top_item
+ descr
->page_size
) return;
1336 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1337 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1338 top
= index
- descr
->page_size
+ 1;
1340 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1343 /***********************************************************************
1344 * LISTBOX_SetCaretIndex
1347 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1350 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1352 INT oldfocus
= descr
->focus_item
;
1354 TRACE("old focus %d, index %d\n", oldfocus
, index
);
1356 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1357 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1358 if (index
== oldfocus
) return LB_OKAY
;
1360 LISTBOX_DrawFocusRect( descr
, FALSE
);
1361 descr
->focus_item
= index
;
1363 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1364 LISTBOX_DrawFocusRect( descr
, TRUE
);
1370 /***********************************************************************
1371 * LISTBOX_SelectItemRange
1373 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1375 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1380 /* A few sanity checks */
1382 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1383 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1385 if (!descr
->nb_items
) return LB_OKAY
;
1387 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1388 if (first
< 0) first
= 0;
1389 if (last
< first
) return LB_OKAY
;
1391 if (on
) /* Turn selection on */
1393 for (i
= first
; i
<= last
; i
++)
1395 if (descr
->items
[i
].selected
) continue;
1396 descr
->items
[i
].selected
= TRUE
;
1397 LISTBOX_InvalidateItemRect(descr
, i
);
1400 else /* Turn selection off */
1402 for (i
= first
; i
<= last
; i
++)
1404 if (!descr
->items
[i
].selected
) continue;
1405 descr
->items
[i
].selected
= FALSE
;
1406 LISTBOX_InvalidateItemRect(descr
, i
);
1412 /***********************************************************************
1413 * LISTBOX_SetSelection
1415 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1416 BOOL on
, BOOL send_notify
)
1418 TRACE( "cur_sel=%d index=%d notify=%s\n",
1419 descr
->selected_item
, index
, send_notify
? "YES" : "NO" );
1421 if (descr
->style
& LBS_NOSEL
)
1423 descr
->selected_item
= index
;
1426 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1427 if (descr
->style
& LBS_MULTIPLESEL
)
1429 if (index
== -1) /* Select all items */
1430 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1431 else /* Only one item */
1432 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1436 INT oldsel
= descr
->selected_item
;
1437 if (index
== oldsel
) return LB_OKAY
;
1438 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1439 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1440 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1441 descr
->selected_item
= index
;
1442 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1443 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1444 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1446 if( descr
->lphc
) /* set selection change flag for parent combo */
1447 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1453 /***********************************************************************
1456 * Change the caret position and extend the selection to the new caret.
1458 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1460 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1462 if ((index
< 0) || (index
>= descr
->nb_items
))
1465 /* Important, repaint needs to be done in this order if
1466 you want to mimic Windows behavior:
1467 1. Remove the focus and paint the item
1468 2. Remove the selection and paint the item(s)
1469 3. Set the selection and repaint the item(s)
1470 4. Set the focus to 'index' and repaint the item */
1472 /* 1. remove the focus and repaint the item */
1473 LISTBOX_DrawFocusRect( descr
, FALSE
);
1475 /* 2. then turn off the previous selection */
1476 /* 3. repaint the new selected item */
1477 if (descr
->style
& LBS_EXTENDEDSEL
)
1479 if (descr
->anchor_item
!= -1)
1481 INT first
= min( index
, descr
->anchor_item
);
1482 INT last
= max( index
, descr
->anchor_item
);
1484 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1485 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1486 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1489 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1491 /* Set selection to new caret item */
1492 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1495 /* 4. repaint the new item with the focus */
1496 descr
->focus_item
= index
;
1497 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1498 LISTBOX_DrawFocusRect( descr
, TRUE
);
1502 /***********************************************************************
1503 * LISTBOX_InsertItem
1505 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1506 LPWSTR str
, ULONG_PTR data
)
1510 INT oldfocus
= descr
->focus_item
;
1512 if (index
== -1) index
= descr
->nb_items
;
1513 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1514 if (!descr
->items
) max_items
= 0;
1515 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1516 if (descr
->nb_items
== max_items
)
1518 /* We need to grow the array */
1519 max_items
+= LB_ARRAY_GRANULARITY
;
1521 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1522 max_items
* sizeof(LB_ITEMDATA
) );
1524 item
= HeapAlloc( GetProcessHeap(), 0,
1525 max_items
* sizeof(LB_ITEMDATA
) );
1528 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1531 descr
->items
= item
;
1534 /* Insert the item structure */
1536 item
= &descr
->items
[index
];
1537 if (index
< descr
->nb_items
)
1538 RtlMoveMemory( item
+ 1, item
,
1539 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1543 item
->selected
= FALSE
;
1546 /* Get item height */
1548 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1550 MEASUREITEMSTRUCT mis
;
1551 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1553 mis
.CtlType
= ODT_LISTBOX
;
1556 mis
.itemData
= descr
->items
[index
].data
;
1557 mis
.itemHeight
= descr
->item_height
;
1558 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1559 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1560 TRACE("[%p]: measure item %d (%s) = %d\n",
1561 descr
->self
, index
, str
? debugstr_w(str
) : "", item
->height
);
1564 /* Repaint the items */
1566 LISTBOX_UpdateScroll( descr
);
1567 LISTBOX_InvalidateItems( descr
, index
);
1569 /* Move selection and focused item */
1570 /* If listbox was empty, set focus to the first item */
1571 if (descr
->nb_items
== 1)
1572 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1573 /* single select don't change selection index in win31 */
1574 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1576 descr
->selected_item
++;
1577 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1581 if (index
<= descr
->selected_item
)
1583 descr
->selected_item
++;
1584 descr
->focus_item
= oldfocus
; /* focus not changed */
1591 /***********************************************************************
1592 * LISTBOX_InsertString
1594 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
, LPCWSTR str
)
1596 LPWSTR new_str
= NULL
;
1600 if (HAS_STRINGS(descr
))
1602 static const WCHAR empty_stringW
[] = { 0 };
1603 if (!str
) str
= empty_stringW
;
1604 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1606 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1609 strcpyW(new_str
, str
);
1611 else data
= (ULONG_PTR
)str
;
1613 if (index
== -1) index
= descr
->nb_items
;
1614 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, data
)) != 0)
1616 HeapFree( GetProcessHeap(), 0, new_str
);
1620 TRACE("[%p]: added item %d %s\n",
1621 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1626 /***********************************************************************
1627 * LISTBOX_DeleteItem
1629 * Delete the content of an item. 'index' must be a valid index.
1631 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1633 /* save the item data before it gets freed by LB_RESETCONTENT */
1634 ULONG_PTR item_data
= descr
->items
[index
].data
;
1635 LPWSTR item_str
= descr
->items
[index
].str
;
1637 if (!descr
->nb_items
)
1638 SendMessageW( descr
->self
, LB_RESETCONTENT
, 0, 0 );
1640 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1641 * while Win95 sends it for all items with user data.
1642 * It's probably better to send it too often than not
1643 * often enough, so this is what we do here.
1645 if (IS_OWNERDRAW(descr
) || item_data
)
1647 DELETEITEMSTRUCT dis
;
1648 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1650 dis
.CtlType
= ODT_LISTBOX
;
1653 dis
.hwndItem
= descr
->self
;
1654 dis
.itemData
= item_data
;
1655 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1657 if (HAS_STRINGS(descr
))
1658 HeapFree( GetProcessHeap(), 0, item_str
);
1662 /***********************************************************************
1663 * LISTBOX_RemoveItem
1665 * Remove an item from the listbox and delete its content.
1667 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1672 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1674 /* We need to invalidate the original rect instead of the updated one. */
1675 LISTBOX_InvalidateItems( descr
, index
);
1678 LISTBOX_DeleteItem( descr
, index
);
1680 if (!descr
->nb_items
) return LB_OKAY
;
1682 /* Remove the item */
1684 item
= &descr
->items
[index
];
1685 if (index
< descr
->nb_items
)
1686 RtlMoveMemory( item
, item
+ 1,
1687 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1688 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1690 /* Shrink the item array if possible */
1692 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1693 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1695 max_items
-= LB_ARRAY_GRANULARITY
;
1696 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1697 max_items
* sizeof(LB_ITEMDATA
) );
1698 if (item
) descr
->items
= item
;
1700 /* Repaint the items */
1702 LISTBOX_UpdateScroll( descr
);
1703 /* if we removed the scrollbar, reset the top of the list
1704 (correct for owner-drawn ???) */
1705 if (descr
->nb_items
== descr
->page_size
)
1706 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1708 /* Move selection and focused item */
1709 if (!IS_MULTISELECT(descr
))
1711 if (index
== descr
->selected_item
)
1712 descr
->selected_item
= -1;
1713 else if (index
< descr
->selected_item
)
1715 descr
->selected_item
--;
1716 if (ISWIN31
) /* win 31 do not change the selected item number */
1717 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1721 if (descr
->focus_item
>= descr
->nb_items
)
1723 descr
->focus_item
= descr
->nb_items
- 1;
1724 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1730 /***********************************************************************
1731 * LISTBOX_ResetContent
1733 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1737 for(i
= descr
->nb_items
- 1; i
>=0; i
--) LISTBOX_DeleteItem( descr
, i
);
1738 HeapFree( GetProcessHeap(), 0, descr
->items
);
1739 descr
->nb_items
= 0;
1740 descr
->top_item
= 0;
1741 descr
->selected_item
= -1;
1742 descr
->focus_item
= 0;
1743 descr
->anchor_item
= -1;
1744 descr
->items
= NULL
;
1748 /***********************************************************************
1751 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, INT count
)
1755 if (HAS_STRINGS(descr
))
1757 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1761 /* FIXME: this is far from optimal... */
1762 if (count
> descr
->nb_items
)
1764 while (count
> descr
->nb_items
)
1765 if ((ret
= LISTBOX_InsertString( descr
, -1, 0 )) < 0)
1768 else if (count
< descr
->nb_items
)
1770 while (count
< descr
->nb_items
)
1771 if ((ret
= LISTBOX_RemoveItem( descr
, (descr
->nb_items
- 1) )) < 0)
1775 InvalidateRect( descr
->self
, NULL
, TRUE
);
1780 /***********************************************************************
1783 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1784 LPCWSTR filespec
, BOOL long_names
)
1787 LRESULT ret
= LB_OKAY
;
1788 WIN32_FIND_DATAW entry
;
1790 LRESULT maxinsert
= LB_ERR
;
1792 /* don't scan directory if we just want drives exclusively */
1793 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1794 /* scan directory */
1795 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1797 int le
= GetLastError();
1798 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1805 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1807 static const WCHAR bracketW
[] = { ']',0 };
1808 static const WCHAR dotW
[] = { '.',0 };
1809 if (!(attrib
& DDL_DIRECTORY
) ||
1810 !strcmpW( entry
.cFileName
, dotW
)) continue;
1812 if (!long_names
&& entry
.cAlternateFileName
[0])
1813 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1815 strcpyW( buffer
+ 1, entry
.cFileName
);
1816 strcatW(buffer
, bracketW
);
1818 else /* not a directory */
1820 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1821 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1823 if ((attrib
& DDL_EXCLUSIVE
) &&
1824 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1827 if (!long_names
&& entry
.cAlternateFileName
[0])
1828 strcpyW( buffer
, entry
.cAlternateFileName
);
1830 strcpyW( buffer
, entry
.cFileName
);
1832 if (!long_names
) CharLowerW( buffer
);
1833 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1834 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1836 if (ret
<= maxinsert
) maxinsert
++; else maxinsert
= ret
;
1837 } while (FindNextFileW( handle
, &entry
));
1838 FindClose( handle
);
1846 if (attrib
& DDL_DRIVES
)
1848 WCHAR buffer
[] = {'[','-','a','-',']',0};
1849 WCHAR root
[] = {'A',':','\\',0};
1851 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1853 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1854 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1863 /***********************************************************************
1864 * LISTBOX_HandleVScroll
1866 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1870 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1874 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1877 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1880 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1881 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1884 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1885 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1887 case SB_THUMBPOSITION
:
1888 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1891 info
.cbSize
= sizeof(info
);
1892 info
.fMask
= SIF_TRACKPOS
;
1893 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1894 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1897 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1900 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1907 /***********************************************************************
1908 * LISTBOX_HandleHScroll
1910 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1915 if (descr
->style
& LBS_MULTICOLUMN
)
1920 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
1924 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
1928 page
= descr
->width
/ descr
->column_width
;
1929 if (page
< 1) page
= 1;
1930 LISTBOX_SetTopItem( descr
,
1931 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1934 page
= descr
->width
/ descr
->column_width
;
1935 if (page
< 1) page
= 1;
1936 LISTBOX_SetTopItem( descr
,
1937 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1939 case SB_THUMBPOSITION
:
1940 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
1943 info
.cbSize
= sizeof(info
);
1944 info
.fMask
= SIF_TRACKPOS
;
1945 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1946 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
1950 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1953 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1957 else if (descr
->horz_extent
)
1962 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
1965 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
1968 LISTBOX_SetHorizontalPos( descr
,
1969 descr
->horz_pos
- descr
->width
);
1972 LISTBOX_SetHorizontalPos( descr
,
1973 descr
->horz_pos
+ descr
->width
);
1975 case SB_THUMBPOSITION
:
1976 LISTBOX_SetHorizontalPos( descr
, pos
);
1979 info
.cbSize
= sizeof(info
);
1980 info
.fMask
= SIF_TRACKPOS
;
1981 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
1982 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
1985 LISTBOX_SetHorizontalPos( descr
, 0 );
1988 LISTBOX_SetHorizontalPos( descr
,
1989 descr
->horz_extent
- descr
->width
);
1996 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
1998 UINT pulScrollLines
= 3;
2000 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
2002 /* if scrolling changes direction, ignore left overs */
2003 if ((delta
< 0 && descr
->wheel_remain
< 0) ||
2004 (delta
> 0 && descr
->wheel_remain
> 0))
2005 descr
->wheel_remain
+= delta
;
2007 descr
->wheel_remain
= delta
;
2009 if (descr
->wheel_remain
&& pulScrollLines
)
2012 pulScrollLines
= min((UINT
) descr
->page_size
, pulScrollLines
);
2013 cLineScroll
= pulScrollLines
* (float)descr
->wheel_remain
/ WHEEL_DELTA
;
2014 descr
->wheel_remain
-= WHEEL_DELTA
* cLineScroll
/ (int)pulScrollLines
;
2015 LISTBOX_SetTopItem( descr
, descr
->top_item
- cLineScroll
, TRUE
);
2020 /***********************************************************************
2021 * LISTBOX_HandleLButtonDown
2023 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
2025 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2027 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2028 descr
->self
, x
, y
, index
, descr
->focus_item
);
2030 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2032 if (!descr
->in_focus
)
2034 if( !descr
->lphc
) SetFocus( descr
->self
);
2035 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2038 if (index
== -1) return 0;
2042 if (descr
->style
& LBS_NOTIFY
)
2043 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2044 MAKELPARAM( x
, y
) );
2047 descr
->captured
= TRUE
;
2048 SetCapture( descr
->self
);
2050 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2052 /* we should perhaps make sure that all items are deselected
2053 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2054 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2055 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2058 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2059 if (keys
& MK_CONTROL
)
2061 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2062 LISTBOX_SetSelection( descr
, index
,
2063 !descr
->items
[index
].selected
,
2064 (descr
->style
& LBS_NOTIFY
) != 0);
2068 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2070 if (descr
->style
& LBS_EXTENDEDSEL
)
2072 LISTBOX_SetSelection( descr
, index
,
2073 descr
->items
[index
].selected
,
2074 (descr
->style
& LBS_NOTIFY
) != 0 );
2078 LISTBOX_SetSelection( descr
, index
,
2079 !descr
->items
[index
].selected
,
2080 (descr
->style
& LBS_NOTIFY
) != 0 );
2086 descr
->anchor_item
= index
;
2087 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2088 LISTBOX_SetSelection( descr
, index
,
2089 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2094 if (GetWindowLongW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2101 if (DragDetect( descr
->self
, pt
))
2102 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2109 /*************************************************************************
2110 * LISTBOX_HandleLButtonDownCombo [Internal]
2112 * Process LButtonDown message for the ComboListBox
2115 * pWnd [I] The windows internal structure
2116 * pDescr [I] The ListBox internal structure
2117 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2118 * x [I] X Mouse Coordinate
2119 * y [I] Y Mouse Coordinate
2122 * 0 since we are processing the WM_LBUTTONDOWN Message
2125 * This function is only to be used when a ListBox is a ComboListBox
2128 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2130 RECT clientRect
, screenRect
;
2136 GetClientRect(descr
->self
, &clientRect
);
2138 if(PtInRect(&clientRect
, mousePos
))
2140 /* MousePos is in client, resume normal processing */
2141 if (msg
== WM_LBUTTONDOWN
)
2143 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2144 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2146 else if (descr
->style
& LBS_NOTIFY
)
2147 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2151 POINT screenMousePos
;
2152 HWND hWndOldCapture
;
2154 /* Check the Non-Client Area */
2155 screenMousePos
= mousePos
;
2156 hWndOldCapture
= GetCapture();
2158 GetWindowRect(descr
->self
, &screenRect
);
2159 ClientToScreen(descr
->self
, &screenMousePos
);
2161 if(!PtInRect(&screenRect
, screenMousePos
))
2163 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2164 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2165 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2169 /* Check to see the NC is a scrollbar */
2171 LONG style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2172 /* Check Vertical scroll bar */
2173 if (style
& WS_VSCROLL
)
2175 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2176 if (PtInRect( &clientRect
, mousePos
))
2177 nHitTestType
= HTVSCROLL
;
2179 /* Check horizontal scroll bar */
2180 if (style
& WS_HSCROLL
)
2182 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2183 if (PtInRect( &clientRect
, mousePos
))
2184 nHitTestType
= HTHSCROLL
;
2186 /* Windows sends this message when a scrollbar is clicked
2189 if(nHitTestType
!= 0)
2191 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2192 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2194 /* Resume the Capture after scrolling is complete
2196 if(hWndOldCapture
!= 0)
2197 SetCapture(hWndOldCapture
);
2203 /***********************************************************************
2204 * LISTBOX_HandleLButtonUp
2206 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2208 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2209 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2210 LISTBOX_Timer
= LB_TIMER_NONE
;
2211 if (descr
->captured
)
2213 descr
->captured
= FALSE
;
2214 if (GetCapture() == descr
->self
) ReleaseCapture();
2215 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2216 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2222 /***********************************************************************
2223 * LISTBOX_HandleTimer
2225 * Handle scrolling upon a timer event.
2226 * Return TRUE if scrolling should continue.
2228 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2233 if (descr
->top_item
) index
= descr
->top_item
- 1;
2237 if (descr
->top_item
) index
-= descr
->page_size
;
2240 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2241 if (index
== descr
->focus_item
) index
++;
2242 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2244 case LB_TIMER_RIGHT
:
2245 if (index
+ descr
->page_size
< descr
->nb_items
)
2246 index
+= descr
->page_size
;
2251 if (index
== descr
->focus_item
) return FALSE
;
2252 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2257 /***********************************************************************
2258 * LISTBOX_HandleSystemTimer
2260 * WM_SYSTIMER handler.
2262 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2264 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2266 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2267 LISTBOX_Timer
= LB_TIMER_NONE
;
2273 /***********************************************************************
2274 * LISTBOX_HandleMouseMove
2276 * WM_MOUSEMOVE handler.
2278 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2282 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2284 if (!descr
->captured
) return;
2286 if (descr
->style
& LBS_MULTICOLUMN
)
2289 else if (y
>= descr
->item_height
* descr
->page_size
)
2290 y
= descr
->item_height
* descr
->page_size
- 1;
2294 dir
= LB_TIMER_LEFT
;
2297 else if (x
>= descr
->width
)
2299 dir
= LB_TIMER_RIGHT
;
2300 x
= descr
->width
- 1;
2305 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2306 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2309 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2310 if (index
== -1) index
= descr
->focus_item
;
2311 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2313 /* Start/stop the system timer */
2315 if (dir
!= LB_TIMER_NONE
)
2316 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2317 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2318 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2319 LISTBOX_Timer
= dir
;
2323 /***********************************************************************
2324 * LISTBOX_HandleKeyDown
2326 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2329 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2330 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2331 bForceSelection
= FALSE
; /* only for single select list */
2333 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2335 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2336 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2337 (LPARAM
)descr
->self
);
2338 if (caret
== -2) return 0;
2340 if (caret
== -1) switch(key
)
2343 if (descr
->style
& LBS_MULTICOLUMN
)
2345 bForceSelection
= FALSE
;
2346 if (descr
->focus_item
>= descr
->page_size
)
2347 caret
= descr
->focus_item
- descr
->page_size
;
2352 caret
= descr
->focus_item
- 1;
2353 if (caret
< 0) caret
= 0;
2356 if (descr
->style
& LBS_MULTICOLUMN
)
2358 bForceSelection
= FALSE
;
2359 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2360 caret
= descr
->focus_item
+ descr
->page_size
;
2365 caret
= descr
->focus_item
+ 1;
2366 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2370 if (descr
->style
& LBS_MULTICOLUMN
)
2372 INT page
= descr
->width
/ descr
->column_width
;
2373 if (page
< 1) page
= 1;
2374 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2376 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2377 if (caret
< 0) caret
= 0;
2380 if (descr
->style
& LBS_MULTICOLUMN
)
2382 INT page
= descr
->width
/ descr
->column_width
;
2383 if (page
< 1) page
= 1;
2384 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2386 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2387 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2393 caret
= descr
->nb_items
- 1;
2396 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2397 else if (descr
->style
& LBS_MULTIPLESEL
)
2399 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2400 !descr
->items
[descr
->focus_item
].selected
,
2401 (descr
->style
& LBS_NOTIFY
) != 0 );
2405 bForceSelection
= FALSE
;
2407 if (bForceSelection
) /* focused item is used instead of key */
2408 caret
= descr
->focus_item
;
2411 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2412 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2413 !IS_MULTISELECT(descr
))
2414 descr
->anchor_item
= caret
;
2415 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2417 if (descr
->style
& LBS_MULTIPLESEL
)
2418 descr
->selected_item
= caret
;
2420 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2421 if (descr
->style
& LBS_NOTIFY
)
2423 if (descr
->lphc
&& IsWindowVisible( descr
->self
))
2425 /* make sure that combo parent doesn't hide us */
2426 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2428 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2435 /***********************************************************************
2436 * LISTBOX_HandleChar
2438 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2446 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2448 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2449 MAKEWPARAM(charW
, descr
->focus_item
),
2450 (LPARAM
)descr
->self
);
2451 if (caret
== -2) return 0;
2454 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2457 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2458 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2459 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2460 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2461 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2467 /***********************************************************************
2470 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2473 MEASUREITEMSTRUCT mis
;
2476 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2479 GetClientRect( hwnd
, &rect
);
2481 descr
->owner
= GetParent( descr
->self
);
2482 descr
->style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2483 descr
->width
= rect
.right
- rect
.left
;
2484 descr
->height
= rect
.bottom
- rect
.top
;
2485 descr
->items
= NULL
;
2486 descr
->nb_items
= 0;
2487 descr
->top_item
= 0;
2488 descr
->selected_item
= -1;
2489 descr
->focus_item
= 0;
2490 descr
->anchor_item
= -1;
2491 descr
->item_height
= 1;
2492 descr
->page_size
= 1;
2493 descr
->column_width
= 150;
2494 descr
->horz_extent
= 0;
2495 descr
->horz_pos
= 0;
2498 descr
->wheel_remain
= 0;
2499 descr
->caret_on
= !lphc
;
2500 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2501 descr
->in_focus
= FALSE
;
2502 descr
->captured
= FALSE
;
2504 descr
->locale
= GetUserDefaultLCID();
2509 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2510 descr
->owner
= lphc
->self
;
2513 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2515 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2517 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2518 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2519 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2520 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2522 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2524 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2526 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2527 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2531 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2532 mis
.CtlType
= ODT_LISTBOX
;
2537 mis
.itemHeight
= descr
->item_height
;
2538 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2539 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2543 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2548 /***********************************************************************
2551 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2553 LISTBOX_ResetContent( descr
);
2554 SetWindowLongPtrW( descr
->self
, 0, 0 );
2555 HeapFree( GetProcessHeap(), 0, descr
);
2560 /***********************************************************************
2561 * ListBoxWndProc_common
2563 LRESULT
ListBoxWndProc_common( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2565 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2566 LPHEADCOMBO lphc
= 0;
2571 if (!IsWindow(hwnd
)) return 0;
2573 if (msg
== WM_CREATE
)
2575 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2576 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= lpcs
->lpCreateParams
;
2577 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2578 TRACE("creating hwnd %p descr %p\n", hwnd
, (void *)GetWindowLongPtrW( hwnd
, 0 ) );
2581 /* Ignore all other messages before we get a WM_CREATE */
2582 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2583 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2585 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2587 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2588 descr
->self
, SPY_GetMsgName(msg
, descr
->self
), wParam
, lParam
);
2592 case LB_RESETCONTENT
:
2593 LISTBOX_ResetContent( descr
);
2594 LISTBOX_UpdateScroll( descr
);
2595 InvalidateRect( descr
->self
, NULL
, TRUE
);
2602 if(unicode
|| !HAS_STRINGS(descr
))
2603 textW
= (LPWSTR
)lParam
;
2606 LPSTR textA
= (LPSTR
)lParam
;
2607 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2608 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2609 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2613 wParam
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2614 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2615 if (!unicode
&& HAS_STRINGS(descr
))
2616 HeapFree(GetProcessHeap(), 0, textW
);
2620 case LB_INSERTSTRING
:
2624 if(unicode
|| !HAS_STRINGS(descr
))
2625 textW
= (LPWSTR
)lParam
;
2628 LPSTR textA
= (LPSTR
)lParam
;
2629 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2630 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2631 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2635 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2636 if(!unicode
&& HAS_STRINGS(descr
))
2637 HeapFree(GetProcessHeap(), 0, textW
);
2645 if(unicode
|| !HAS_STRINGS(descr
))
2646 textW
= (LPWSTR
)lParam
;
2649 LPSTR textA
= (LPSTR
)lParam
;
2650 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2651 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2652 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2656 wParam
= LISTBOX_FindFileStrPos( descr
, textW
);
2657 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2658 if(!unicode
&& HAS_STRINGS(descr
))
2659 HeapFree(GetProcessHeap(), 0, textW
);
2663 case LB_DELETESTRING
:
2664 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2665 return descr
->nb_items
;
2668 SetLastError(ERROR_INVALID_INDEX
);
2672 case LB_GETITEMDATA
:
2673 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2675 SetLastError(ERROR_INVALID_INDEX
);
2678 return descr
->items
[wParam
].data
;
2680 case LB_SETITEMDATA
:
2681 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2683 SetLastError(ERROR_INVALID_INDEX
);
2686 descr
->items
[wParam
].data
= lParam
;
2687 /* undocumented: returns TRUE, not LB_OKAY (0) */
2691 return descr
->nb_items
;
2694 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, unicode
);
2697 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2699 SetLastError(ERROR_INVALID_INDEX
);
2702 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2703 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2704 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2705 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2708 if (descr
->nb_items
== 0)
2710 if (!IS_MULTISELECT(descr
))
2711 return descr
->selected_item
;
2712 if (descr
->selected_item
!= -1)
2713 return descr
->selected_item
;
2714 return descr
->focus_item
;
2715 /* otherwise, if the user tries to move the selection with the */
2716 /* arrow keys, we will give the application something to choke on */
2717 case LB_GETTOPINDEX
:
2718 return descr
->top_item
;
2720 case LB_GETITEMHEIGHT
:
2721 return LISTBOX_GetItemHeight( descr
, wParam
);
2723 case LB_SETITEMHEIGHT
:
2724 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2726 case LB_ITEMFROMPOINT
:
2733 /* The hiword of the return value is not a client area
2734 hittest as suggested by MSDN, but rather a hittest on
2735 the returned listbox item. */
2737 if(descr
->nb_items
== 0)
2738 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2740 pt
.x
= (short)LOWORD(lParam
);
2741 pt
.y
= (short)HIWORD(lParam
);
2743 SetRect(&rect
, 0, 0, descr
->width
, descr
->height
);
2745 if(!PtInRect(&rect
, pt
))
2747 pt
.x
= min(pt
.x
, rect
.right
- 1);
2748 pt
.x
= max(pt
.x
, 0);
2749 pt
.y
= min(pt
.y
, rect
.bottom
- 1);
2750 pt
.y
= max(pt
.y
, 0);
2754 index
= LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
);
2758 index
= descr
->nb_items
- 1;
2761 return MAKELONG(index
, hit
? 0 : 1);
2764 case LB_SETCARETINDEX
:
2765 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2766 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2773 case LB_GETCARETINDEX
:
2774 return descr
->focus_item
;
2776 case LB_SETTOPINDEX
:
2777 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2779 case LB_SETCOLUMNWIDTH
:
2780 return LISTBOX_SetColumnWidth( descr
, wParam
);
2782 case LB_GETITEMRECT
:
2783 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2789 if(unicode
|| !HAS_STRINGS(descr
))
2790 textW
= (LPWSTR
)lParam
;
2793 LPSTR textA
= (LPSTR
)lParam
;
2794 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2795 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2796 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2798 ret
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2799 if(!unicode
&& HAS_STRINGS(descr
))
2800 HeapFree(GetProcessHeap(), 0, textW
);
2804 case LB_FINDSTRINGEXACT
:
2808 if(unicode
|| !HAS_STRINGS(descr
))
2809 textW
= (LPWSTR
)lParam
;
2812 LPSTR textA
= (LPSTR
)lParam
;
2813 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2814 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2815 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2817 ret
= LISTBOX_FindString( descr
, wParam
, textW
, TRUE
);
2818 if(!unicode
&& HAS_STRINGS(descr
))
2819 HeapFree(GetProcessHeap(), 0, textW
);
2823 case LB_SELECTSTRING
:
2828 if(HAS_STRINGS(descr
))
2829 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2830 debugstr_a((LPSTR
)lParam
));
2831 if(unicode
|| !HAS_STRINGS(descr
))
2832 textW
= (LPWSTR
)lParam
;
2835 LPSTR textA
= (LPSTR
)lParam
;
2836 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2837 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2838 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2840 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2841 if(!unicode
&& HAS_STRINGS(descr
))
2842 HeapFree(GetProcessHeap(), 0, textW
);
2843 if (index
!= LB_ERR
)
2845 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2846 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2852 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2854 return descr
->items
[wParam
].selected
;
2857 return LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2860 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2861 LISTBOX_SetCaretIndex( descr
, wParam
, TRUE
);
2862 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2863 if (ret
!= LB_ERR
) ret
= descr
->selected_item
;
2866 case LB_GETSELCOUNT
:
2867 return LISTBOX_GetSelCount( descr
);
2869 case LB_GETSELITEMS
:
2870 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2872 case LB_SELITEMRANGE
:
2873 if (LOWORD(lParam
) <= HIWORD(lParam
))
2874 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2875 HIWORD(lParam
), wParam
);
2877 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2878 LOWORD(lParam
), wParam
);
2880 case LB_SELITEMRANGEEX
:
2881 if ((INT
)lParam
>= (INT
)wParam
)
2882 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2884 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
2886 case LB_GETHORIZONTALEXTENT
:
2887 return descr
->horz_extent
;
2889 case LB_SETHORIZONTALEXTENT
:
2890 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
2892 case LB_GETANCHORINDEX
:
2893 return descr
->anchor_item
;
2895 case LB_SETANCHORINDEX
:
2896 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2898 SetLastError(ERROR_INVALID_INDEX
);
2901 descr
->anchor_item
= (INT
)wParam
;
2909 textW
= (LPWSTR
)lParam
;
2912 LPSTR textA
= (LPSTR
)lParam
;
2913 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2914 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2915 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2917 ret
= LISTBOX_Directory( descr
, wParam
, textW
, msg
== LB_DIR
);
2919 HeapFree(GetProcessHeap(), 0, textW
);
2924 return descr
->locale
;
2929 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
2931 ret
= descr
->locale
;
2932 descr
->locale
= (LCID
)wParam
;
2936 case LB_INITSTORAGE
:
2937 return LISTBOX_InitStorage( descr
, wParam
);
2940 return LISTBOX_SetCount( descr
, (INT
)wParam
);
2942 case LB_SETTABSTOPS
:
2943 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
);
2946 if (descr
->caret_on
)
2948 descr
->caret_on
= TRUE
;
2949 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2950 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2954 if (!descr
->caret_on
)
2956 descr
->caret_on
= FALSE
;
2957 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2958 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2961 case LB_GETLISTBOXINFO
:
2962 return descr
->page_size
;
2965 return LISTBOX_Destroy( descr
);
2968 InvalidateRect( descr
->self
, NULL
, TRUE
);
2972 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
2976 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2978 case WM_PRINTCLIENT
:
2982 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
2983 ret
= LISTBOX_Paint( descr
, hdc
);
2984 if( !wParam
) EndPaint( descr
->self
, &ps
);
2988 LISTBOX_UpdateSize( descr
);
2991 return (LRESULT
)descr
->font
;
2993 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
2994 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
2997 descr
->in_focus
= TRUE
;
2998 descr
->caret_on
= TRUE
;
2999 if (descr
->focus_item
!= -1)
3000 LISTBOX_DrawFocusRect( descr
, TRUE
);
3001 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
3004 LISTBOX_HandleLButtonUp( descr
); /* Release capture if we have it */
3005 descr
->in_focus
= FALSE
;
3006 descr
->wheel_remain
= 0;
3007 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
3008 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3009 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
3012 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3014 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3016 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
3017 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
3018 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
3019 case WM_LBUTTONDOWN
:
3021 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3022 (INT16
)LOWORD(lParam
),
3023 (INT16
)HIWORD(lParam
) );
3024 return LISTBOX_HandleLButtonDown( descr
, wParam
,
3025 (INT16
)LOWORD(lParam
),
3026 (INT16
)HIWORD(lParam
) );
3027 case WM_LBUTTONDBLCLK
:
3029 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3030 (INT16
)LOWORD(lParam
),
3031 (INT16
)HIWORD(lParam
) );
3032 if (descr
->style
& LBS_NOTIFY
)
3033 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
3036 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
3038 BOOL captured
= descr
->captured
;
3042 mousePos
.x
= (INT16
)LOWORD(lParam
);
3043 mousePos
.y
= (INT16
)HIWORD(lParam
);
3046 * If we are in a dropdown combobox, we simulate that
3047 * the mouse is captured to show the tracking of the item.
3049 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
3050 descr
->captured
= TRUE
;
3052 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
3054 descr
->captured
= captured
;
3056 else if (GetCapture() == descr
->self
)
3058 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
3059 (INT16
)HIWORD(lParam
) );
3069 * If the mouse button "up" is not in the listbox,
3070 * we make sure there is no selection by re-selecting the
3071 * item that was selected when the listbox was made visible.
3073 mousePos
.x
= (INT16
)LOWORD(lParam
);
3074 mousePos
.y
= (INT16
)HIWORD(lParam
);
3076 GetClientRect(descr
->self
, &clientRect
);
3079 * When the user clicks outside the combobox and the focus
3080 * is lost, the owning combobox will send a fake buttonup with
3081 * 0xFFFFFFF as the mouse location, we must also revert the
3082 * selection to the original selection.
3084 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
3085 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
3087 return LISTBOX_HandleLButtonUp( descr
);
3089 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3091 /* for some reason Windows makes it possible to
3092 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3094 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3095 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3096 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3098 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3102 return LISTBOX_HandleKeyDown( descr
, wParam
);
3107 charW
= (WCHAR
)wParam
;
3110 CHAR charA
= (CHAR
)wParam
;
3111 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
3113 return LISTBOX_HandleChar( descr
, charW
);
3116 return LISTBOX_HandleSystemTimer( descr
);
3118 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3121 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3122 wParam
, (LPARAM
)descr
->self
);
3123 TRACE("hbrush = %p\n", hbrush
);
3125 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3128 GetClientRect(descr
->self
, &rect
);
3129 FillRect((HDC
)wParam
, &rect
, hbrush
);
3134 if( lphc
) return 0;
3135 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3136 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3139 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3148 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3149 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3150 hwnd
, msg
, wParam
, lParam
);
3153 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3154 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3157 DWORD WINAPI
GetListBoxInfo(HWND hwnd
)
3159 TRACE("%p\n", hwnd
);
3160 return SendMessageW(hwnd
, LB_GETLISTBOXINFO
, 0, 0);