4 * Copyright 1996 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 * This code was audited for completeness against the documented features
23 * of Comctl32.dll version 6.0 on Oct. 9, 2004, by Dimitrie O. Paun.
25 * Unless otherwise noted, we believe this code to be complete, as per
26 * the specification mentioned above.
27 * If you discover missing features, or bugs, please note them below.
40 #include "wine/unicode.h"
41 #include "user_private.h"
43 #include "wine/exception.h"
44 #include "wine/debug.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(listbox
);
48 /* Items array granularity */
49 #define LB_ARRAY_GRANULARITY 16
51 /* Scrolling timeout in ms */
52 #define LB_SCROLL_TIMEOUT 50
54 /* Listbox system timer id */
57 /* flag listbox changed while setredraw false - internal style */
58 #define LBS_DISPLAYCHANGED 0x80000000
63 LPWSTR str
; /* Item text */
64 BOOL selected
; /* Is item selected? */
65 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
66 ULONG_PTR data
; /* User data */
69 /* Listbox structure */
72 HWND self
; /* Our own window handle */
73 HWND owner
; /* Owner window to send notifications to */
74 UINT style
; /* Window style */
75 INT width
; /* Window width */
76 INT height
; /* Window height */
77 LB_ITEMDATA
*items
; /* Array of items */
78 INT nb_items
; /* Number of items */
79 INT top_item
; /* Top visible item */
80 INT selected_item
; /* Selected item */
81 INT focus_item
; /* Item that has the focus */
82 INT anchor_item
; /* Anchor item for extended selection */
83 INT item_height
; /* Default item height */
84 INT page_size
; /* Items per listbox page */
85 INT column_width
; /* Column width for multi-column listboxes */
86 INT horz_extent
; /* Horizontal extent (0 if no hscroll) */
87 INT horz_pos
; /* Horizontal position */
88 INT nb_tabs
; /* Number of tabs in array */
89 INT
*tabs
; /* Array of tabs */
90 INT avg_char_width
; /* Average width of characters */
91 INT wheel_remain
; /* Left over scroll amount */
92 BOOL caret_on
; /* Is caret on? */
93 BOOL captured
; /* Is mouse captured? */
95 HFONT font
; /* Current font */
96 LCID locale
; /* Current locale for string comparisons */
97 LPHEADCOMBO lphc
; /* ComboLBox */
101 #define IS_OWNERDRAW(descr) \
102 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
104 #define HAS_STRINGS(descr) \
105 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
108 #define IS_MULTISELECT(descr) \
109 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
110 !((descr)->style & LBS_NOSEL))
112 #define SEND_NOTIFICATION(descr,code) \
113 (SendMessageW( (descr)->owner, WM_COMMAND, \
114 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
116 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
118 /* Current timer status */
128 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
130 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
);
132 /*********************************************************************
133 * listbox class descriptor
135 static const WCHAR listboxW
[] = {'L','i','s','t','B','o','x',0};
136 const struct builtin_class_descr LISTBOX_builtin_class
=
139 CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
140 WINPROC_LISTBOX
, /* proc */
141 sizeof(LB_DESCR
*), /* extra */
142 IDC_ARROW
, /* cursor */
147 /*********************************************************************
148 * combolbox class descriptor
150 static const WCHAR combolboxW
[] = {'C','o','m','b','o','L','B','o','x',0};
151 const struct builtin_class_descr COMBOLBOX_builtin_class
=
153 combolboxW
, /* name */
154 CS_DBLCLKS
| CS_SAVEBITS
, /* style */
155 WINPROC_LISTBOX
, /* proc */
156 sizeof(LB_DESCR
*), /* extra */
157 IDC_ARROW
, /* cursor */
162 /***********************************************************************
163 * LISTBOX_GetCurrentPageSize
165 * Return the current page size
167 static INT
LISTBOX_GetCurrentPageSize( const LB_DESCR
*descr
)
170 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
171 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
173 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
175 if (i
== descr
->top_item
) return 1;
176 else return i
- descr
->top_item
;
180 /***********************************************************************
181 * LISTBOX_GetMaxTopIndex
183 * Return the maximum possible index for the top of the listbox.
185 static INT
LISTBOX_GetMaxTopIndex( const LB_DESCR
*descr
)
189 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
191 page
= descr
->height
;
192 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
193 if ((page
-= descr
->items
[max
].height
) < 0) break;
194 if (max
< descr
->nb_items
- 1) max
++;
196 else if (descr
->style
& LBS_MULTICOLUMN
)
198 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
199 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
200 max
= (max
- page
) * descr
->page_size
;
204 max
= descr
->nb_items
- descr
->page_size
;
206 if (max
< 0) max
= 0;
211 /***********************************************************************
212 * LISTBOX_UpdateScroll
214 * Update the scrollbars. Should be called whenever the content
215 * of the listbox changes.
217 static void LISTBOX_UpdateScroll( LB_DESCR
*descr
)
221 /* Check the listbox scroll bar flags individually before we call
222 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
223 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
224 scroll bar when we do not need one.
225 if (!(descr->style & WS_VSCROLL)) return;
228 /* It is important that we check descr->style, and not wnd->dwStyle,
229 for WS_VSCROLL, as the former is exactly the one passed in
230 argument to CreateWindow.
231 In Windows (and from now on in Wine :) a listbox created
232 with such a style (no WS_SCROLL) does not update
233 the scrollbar with listbox-related data, thus letting
234 the programmer use it for his/her own purposes. */
236 if (descr
->style
& LBS_NOREDRAW
) return;
237 info
.cbSize
= sizeof(info
);
239 if (descr
->style
& LBS_MULTICOLUMN
)
242 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
243 info
.nPos
= descr
->top_item
/ descr
->page_size
;
244 info
.nPage
= descr
->width
/ descr
->column_width
;
245 if (info
.nPage
< 1) info
.nPage
= 1;
246 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
247 if (descr
->style
& LBS_DISABLENOSCROLL
)
248 info
.fMask
|= SIF_DISABLENOSCROLL
;
249 if (descr
->style
& WS_HSCROLL
)
250 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
252 info
.fMask
= SIF_RANGE
;
253 if (descr
->style
& WS_VSCROLL
)
254 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
259 info
.nMax
= descr
->nb_items
- 1;
260 info
.nPos
= descr
->top_item
;
261 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
262 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
263 if (descr
->style
& LBS_DISABLENOSCROLL
)
264 info
.fMask
|= SIF_DISABLENOSCROLL
;
265 if (descr
->style
& WS_VSCROLL
)
266 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
268 if (descr
->horz_extent
)
271 info
.nMax
= descr
->horz_extent
- 1;
272 info
.nPos
= descr
->horz_pos
;
273 info
.nPage
= descr
->width
;
274 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
275 if (descr
->style
& LBS_DISABLENOSCROLL
)
276 info
.fMask
|= SIF_DISABLENOSCROLL
;
277 if (descr
->style
& WS_HSCROLL
)
278 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
284 /***********************************************************************
287 * Set the top item of the listbox, scrolling up or down if necessary.
289 static LRESULT
LISTBOX_SetTopItem( LB_DESCR
*descr
, INT index
, BOOL scroll
)
291 INT max
= LISTBOX_GetMaxTopIndex( descr
);
293 TRACE("setting top item %d, scroll %d\n", index
, scroll
);
295 if (index
> max
) index
= max
;
296 if (index
< 0) index
= 0;
297 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
298 if (descr
->top_item
== index
) return LB_OKAY
;
302 if (descr
->style
& LBS_MULTICOLUMN
)
303 diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
304 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
308 if (index
> descr
->top_item
)
310 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
311 diff
-= descr
->items
[i
].height
;
315 for (i
= index
; i
< descr
->top_item
; i
++)
316 diff
+= descr
->items
[i
].height
;
320 diff
= (descr
->top_item
- index
) * descr
->item_height
;
322 ScrollWindowEx( descr
->self
, 0, diff
, NULL
, NULL
, 0, NULL
,
323 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
325 if (!scroll
) InvalidateRect( descr
->self
, NULL
, TRUE
);
326 descr
->top_item
= index
;
327 LISTBOX_UpdateScroll( descr
);
332 /***********************************************************************
335 * Update the page size. Should be called when the size of
336 * the client area or the item height changes.
338 static void LISTBOX_UpdatePage( LB_DESCR
*descr
)
342 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
344 if (page_size
== descr
->page_size
) return;
345 descr
->page_size
= page_size
;
346 if (descr
->style
& LBS_MULTICOLUMN
)
347 InvalidateRect( descr
->self
, NULL
, TRUE
);
348 LISTBOX_SetTopItem( descr
, descr
->top_item
, FALSE
);
352 /***********************************************************************
355 * Update the size of the listbox. Should be called when the size of
356 * the client area changes.
358 static void LISTBOX_UpdateSize( LB_DESCR
*descr
)
362 GetClientRect( descr
->self
, &rect
);
363 descr
->width
= rect
.right
- rect
.left
;
364 descr
->height
= rect
.bottom
- rect
.top
;
365 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
370 GetWindowRect( descr
->self
, &rect
);
371 if(descr
->item_height
!= 0)
372 remaining
= descr
->height
% descr
->item_height
;
375 if ((descr
->height
> descr
->item_height
) && remaining
)
377 TRACE("[%p]: changing height %d -> %d\n",
378 descr
->self
, descr
->height
, descr
->height
- remaining
);
379 SetWindowPos( descr
->self
, 0, 0, 0, rect
.right
- rect
.left
,
380 rect
.bottom
- rect
.top
- remaining
,
381 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
385 TRACE("[%p]: new size = %d,%d\n", descr
->self
, descr
->width
, descr
->height
);
386 LISTBOX_UpdatePage( descr
);
387 LISTBOX_UpdateScroll( descr
);
389 /* Invalidate the focused item so it will be repainted correctly */
390 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
392 InvalidateRect( descr
->self
, &rect
, FALSE
);
397 /***********************************************************************
398 * LISTBOX_GetItemRect
400 * Get the rectangle enclosing an item, in listbox client coordinates.
401 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
403 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
)
405 /* Index <= 0 is legal even on empty listboxes */
406 if (index
&& (index
>= descr
->nb_items
))
408 memset(rect
, 0, sizeof(*rect
));
409 SetLastError(ERROR_INVALID_INDEX
);
412 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
413 if (descr
->style
& LBS_MULTICOLUMN
)
415 INT col
= (index
/ descr
->page_size
) -
416 (descr
->top_item
/ descr
->page_size
);
417 rect
->left
+= col
* descr
->column_width
;
418 rect
->right
= rect
->left
+ descr
->column_width
;
419 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
420 rect
->bottom
= rect
->top
+ descr
->item_height
;
422 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
425 rect
->right
+= descr
->horz_pos
;
426 if ((index
>= 0) && (index
< descr
->nb_items
))
428 if (index
< descr
->top_item
)
430 for (i
= descr
->top_item
-1; i
>= index
; i
--)
431 rect
->top
-= descr
->items
[i
].height
;
435 for (i
= descr
->top_item
; i
< index
; i
++)
436 rect
->top
+= descr
->items
[i
].height
;
438 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
444 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
445 rect
->bottom
= rect
->top
+ descr
->item_height
;
446 rect
->right
+= descr
->horz_pos
;
449 TRACE("item %d, rect %s\n", index
, wine_dbgstr_rect(rect
));
451 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
452 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
456 /***********************************************************************
457 * LISTBOX_GetItemFromPoint
459 * Return the item nearest from point (x,y) (in client coordinates).
461 static INT
LISTBOX_GetItemFromPoint( const LB_DESCR
*descr
, INT x
, INT y
)
463 INT index
= descr
->top_item
;
465 if (!descr
->nb_items
) return -1; /* No items */
466 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
471 while (index
< descr
->nb_items
)
473 if ((pos
+= descr
->items
[index
].height
) > y
) break;
482 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
486 else if (descr
->style
& LBS_MULTICOLUMN
)
488 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
489 if (y
>= 0) index
+= y
/ descr
->item_height
;
490 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
491 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
495 index
+= (y
/ descr
->item_height
);
497 if (index
< 0) return 0;
498 if (index
>= descr
->nb_items
) return -1;
503 /***********************************************************************
508 static void LISTBOX_PaintItem( LB_DESCR
*descr
, HDC hdc
, const RECT
*rect
,
509 INT index
, UINT action
, BOOL ignoreFocus
)
511 LB_ITEMDATA
*item
= NULL
;
512 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
514 if (IS_OWNERDRAW(descr
))
522 if (action
== ODA_FOCUS
)
523 DrawFocusRect( hdc
, rect
);
525 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
529 /* some programs mess with the clipping region when
530 drawing the item, *and* restore the previous region
531 after they are done, so a region has better to exist
532 else everything ends clipped */
533 GetClientRect(descr
->self
, &r
);
534 hrgn
= set_control_clipping( hdc
, &r
);
536 dis
.CtlType
= ODT_LISTBOX
;
537 dis
.CtlID
= GetWindowLongPtrW( descr
->self
, GWLP_ID
);
538 dis
.hwndItem
= descr
->self
;
539 dis
.itemAction
= action
;
543 if (item
->selected
) dis
.itemState
|= ODS_SELECTED
;
544 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
546 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
547 if (!IsWindowEnabled(descr
->self
)) dis
.itemState
|= ODS_DISABLED
;
548 dis
.itemData
= item
->data
;
550 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
551 descr
->self
, index
, debugstr_w(item
->str
), action
,
552 dis
.itemState
, wine_dbgstr_rect(rect
) );
553 SendMessageW(descr
->owner
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
554 SelectClipRgn( hdc
, hrgn
);
555 if (hrgn
) DeleteObject( hrgn
);
559 COLORREF oldText
= 0, oldBk
= 0;
561 if (action
== ODA_FOCUS
)
563 DrawFocusRect( hdc
, rect
);
566 if (item
&& item
->selected
)
568 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
569 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
572 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
573 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
574 wine_dbgstr_rect(rect
) );
576 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
577 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
578 else if (!(descr
->style
& LBS_USETABSTOPS
))
579 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
580 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
581 strlenW(item
->str
), NULL
);
584 /* Output empty string to paint background in the full width. */
585 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
586 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
587 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
588 item
->str
, strlenW(item
->str
),
589 descr
->nb_tabs
, descr
->tabs
, 0);
591 if (item
&& item
->selected
)
593 SetBkColor( hdc
, oldBk
);
594 SetTextColor( hdc
, oldText
);
596 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
598 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
603 /***********************************************************************
606 * Change the redraw flag.
608 static void LISTBOX_SetRedraw( LB_DESCR
*descr
, BOOL on
)
612 if (!(descr
->style
& LBS_NOREDRAW
)) return;
613 descr
->style
&= ~LBS_NOREDRAW
;
614 if (descr
->style
& LBS_DISPLAYCHANGED
)
615 { /* page was changed while setredraw false, refresh automatically */
616 InvalidateRect(descr
->self
, NULL
, TRUE
);
617 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
618 { /* reset top of page if less than number of items/page */
619 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
620 if (descr
->top_item
< 0) descr
->top_item
= 0;
622 descr
->style
&= ~LBS_DISPLAYCHANGED
;
624 LISTBOX_UpdateScroll( descr
);
626 else descr
->style
|= LBS_NOREDRAW
;
630 /***********************************************************************
631 * LISTBOX_RepaintItem
633 * Repaint a single item synchronously.
635 static void LISTBOX_RepaintItem( LB_DESCR
*descr
, INT index
, UINT action
)
640 HBRUSH hbrush
, oldBrush
= 0;
642 /* Do not repaint the item if the item is not visible */
643 if (!IsWindowVisible(descr
->self
)) return;
644 if (descr
->style
& LBS_NOREDRAW
)
646 descr
->style
|= LBS_DISPLAYCHANGED
;
649 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
650 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
651 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
652 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
653 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
654 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
655 if (!IsWindowEnabled(descr
->self
))
656 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
657 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
658 LISTBOX_PaintItem( descr
, hdc
, &rect
, index
, action
, TRUE
);
659 if (oldFont
) SelectObject( hdc
, oldFont
);
660 if (oldBrush
) SelectObject( hdc
, oldBrush
);
661 ReleaseDC( descr
->self
, hdc
);
665 /***********************************************************************
666 * LISTBOX_DrawFocusRect
668 static void LISTBOX_DrawFocusRect( LB_DESCR
*descr
, BOOL on
)
674 /* Do not repaint the item if the item is not visible */
675 if (!IsWindowVisible(descr
->self
)) return;
677 if (descr
->focus_item
== -1) return;
678 if (!descr
->caret_on
|| !descr
->in_focus
) return;
680 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) != 1) return;
681 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
682 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
683 if (!IsWindowEnabled(descr
->self
))
684 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
685 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
686 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, !on
);
687 if (oldFont
) SelectObject( hdc
, oldFont
);
688 ReleaseDC( descr
->self
, hdc
);
692 /***********************************************************************
693 * LISTBOX_InitStorage
695 static LRESULT
LISTBOX_InitStorage( LB_DESCR
*descr
, INT nb_items
)
699 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
700 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
702 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
703 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
704 nb_items
* sizeof(LB_ITEMDATA
));
707 item
= HeapAlloc( GetProcessHeap(), 0,
708 nb_items
* sizeof(LB_ITEMDATA
));
713 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
721 /***********************************************************************
722 * LISTBOX_SetTabStops
724 static BOOL
LISTBOX_SetTabStops( LB_DESCR
*descr
, INT count
, LPINT tabs
)
728 if (!(descr
->style
& LBS_USETABSTOPS
))
730 SetLastError(ERROR_LB_WITHOUT_TABSTOPS
);
734 HeapFree( GetProcessHeap(), 0, descr
->tabs
);
735 if (!(descr
->nb_tabs
= count
))
740 if (!(descr
->tabs
= HeapAlloc( GetProcessHeap(), 0,
741 descr
->nb_tabs
* sizeof(INT
) )))
743 memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
745 /* convert into "dialog units"*/
746 for (i
= 0; i
< descr
->nb_tabs
; i
++)
747 descr
->tabs
[i
] = MulDiv(descr
->tabs
[i
], descr
->avg_char_width
, 4);
753 /***********************************************************************
756 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPWSTR buffer
, BOOL unicode
)
760 if ((index
< 0) || (index
>= descr
->nb_items
))
762 SetLastError(ERROR_INVALID_INDEX
);
765 if (HAS_STRINGS(descr
))
769 len
= strlenW(descr
->items
[index
].str
);
772 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[index
].str
, len
,
773 NULL
, 0, NULL
, NULL
);
776 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
778 __TRY
/* hide a Delphi bug that passes a read-only buffer */
782 strcpyW( buffer
, descr
->items
[index
].str
);
783 len
= strlenW(buffer
);
787 len
= WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1,
788 (LPSTR
)buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
793 WARN( "got an invalid buffer (Delphi bug?)\n" );
794 SetLastError( ERROR_INVALID_PARAMETER
);
800 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
806 static inline INT
LISTBOX_lstrcmpiW( LCID lcid
, LPCWSTR str1
, LPCWSTR str2
)
808 INT ret
= CompareStringW( lcid
, NORM_IGNORECASE
, str1
, -1, str2
, -1 );
809 if (ret
== CSTR_LESS_THAN
)
811 if (ret
== CSTR_EQUAL
)
813 if (ret
== CSTR_GREATER_THAN
)
818 /***********************************************************************
819 * LISTBOX_FindStringPos
821 * Find the nearest string located before a given string in sort order.
822 * If 'exact' is TRUE, return an error if we don't get an exact match.
824 static INT
LISTBOX_FindStringPos( LB_DESCR
*descr
, LPCWSTR str
, BOOL exact
)
826 INT index
, min
, max
, res
= -1;
828 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
830 max
= descr
->nb_items
;
833 index
= (min
+ max
) / 2;
834 if (HAS_STRINGS(descr
))
835 res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, descr
->items
[index
].str
);
838 COMPAREITEMSTRUCT cis
;
839 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
841 cis
.CtlType
= ODT_LISTBOX
;
843 cis
.hwndItem
= descr
->self
;
844 /* note that some application (MetaStock) expects the second item
845 * to be in the listbox */
847 cis
.itemData1
= (ULONG_PTR
)str
;
849 cis
.itemData2
= descr
->items
[index
].data
;
850 cis
.dwLocaleId
= descr
->locale
;
851 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
853 if (!res
) return index
;
854 if (res
< 0) max
= index
;
855 else min
= index
+ 1;
857 return exact
? -1 : max
;
861 /***********************************************************************
862 * LISTBOX_FindFileStrPos
864 * Find the nearest string located before a given string in directory
865 * sort order (i.e. first files, then directories, then drives).
867 static INT
LISTBOX_FindFileStrPos( LB_DESCR
*descr
, LPCWSTR str
)
869 INT min
, max
, res
= -1;
871 if (!HAS_STRINGS(descr
))
872 return LISTBOX_FindStringPos( descr
, str
, FALSE
);
874 max
= descr
->nb_items
;
877 INT index
= (min
+ max
) / 2;
878 LPCWSTR p
= descr
->items
[index
].str
;
879 if (*p
== '[') /* drive or directory */
881 if (*str
!= '[') res
= -1;
882 else if (p
[1] == '-') /* drive */
884 if (str
[1] == '-') res
= str
[2] - p
[2];
889 if (str
[1] == '-') res
= 1;
890 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
895 if (*str
== '[') res
= 1;
896 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
898 if (!res
) return index
;
899 if (res
< 0) max
= index
;
900 else min
= index
+ 1;
906 /***********************************************************************
909 * Find the item beginning with a given string.
911 static INT
LISTBOX_FindString( LB_DESCR
*descr
, INT start
, LPCWSTR str
, BOOL exact
)
916 if (start
>= descr
->nb_items
) start
= -1;
917 item
= descr
->items
+ start
+ 1;
918 if (HAS_STRINGS(descr
))
920 if (!str
|| ! str
[0] ) return LB_ERR
;
923 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
924 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
925 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
926 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
930 /* Special case for drives and directories: ignore prefix */
931 #define CHECK_DRIVE(item) \
932 if ((item)->str[0] == '[') \
934 if (!strncmpiW( str, (item)->str+1, len )) return i; \
935 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
939 INT len
= strlenW(str
);
940 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
942 if (!strncmpiW( str
, item
->str
, len
)) return i
;
945 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
947 if (!strncmpiW( str
, item
->str
, len
)) return i
;
955 if (exact
&& (descr
->style
& LBS_SORT
))
956 /* If sorted, use a WM_COMPAREITEM binary search */
957 return LISTBOX_FindStringPos( descr
, str
, TRUE
);
959 /* Otherwise use a linear search */
960 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
961 if (item
->data
== (ULONG_PTR
)str
) return i
;
962 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
963 if (item
->data
== (ULONG_PTR
)str
) return i
;
969 /***********************************************************************
970 * LISTBOX_GetSelCount
972 static LRESULT
LISTBOX_GetSelCount( const LB_DESCR
*descr
)
975 const LB_ITEMDATA
*item
= descr
->items
;
977 if (!(descr
->style
& LBS_MULTIPLESEL
) ||
978 (descr
->style
& LBS_NOSEL
))
980 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
981 if (item
->selected
) count
++;
986 /***********************************************************************
987 * LISTBOX_GetSelItems
989 static LRESULT
LISTBOX_GetSelItems( const LB_DESCR
*descr
, INT max
, LPINT array
)
992 const LB_ITEMDATA
*item
= descr
->items
;
994 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
995 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
996 if (item
->selected
) array
[count
++] = i
;
1001 /***********************************************************************
1004 static LRESULT
LISTBOX_Paint( LB_DESCR
*descr
, HDC hdc
)
1006 INT i
, col_pos
= descr
->page_size
- 1;
1008 RECT focusRect
= {-1, -1, -1, -1};
1010 HBRUSH hbrush
, oldBrush
= 0;
1012 if (descr
->style
& LBS_NOREDRAW
) return 0;
1014 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1015 if (descr
->style
& LBS_MULTICOLUMN
)
1016 rect
.right
= rect
.left
+ descr
->column_width
;
1017 else if (descr
->horz_pos
)
1019 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1020 rect
.right
+= descr
->horz_pos
;
1023 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1024 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
1025 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
1026 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1027 if (!IsWindowEnabled(descr
->self
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1029 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1032 /* Special case for empty listbox: paint focus rect */
1033 rect
.bottom
= rect
.top
+ descr
->item_height
;
1034 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1035 &rect
, NULL
, 0, NULL
);
1036 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1037 rect
.top
= rect
.bottom
;
1040 /* Paint all the item, regarding the selection
1041 Focus state will be painted after */
1043 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1045 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1046 rect
.bottom
= rect
.top
+ descr
->item_height
;
1048 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1050 if (i
== descr
->focus_item
)
1052 /* keep the focus rect, to paint the focus item after */
1053 focusRect
.left
= rect
.left
;
1054 focusRect
.right
= rect
.right
;
1055 focusRect
.top
= rect
.top
;
1056 focusRect
.bottom
= rect
.bottom
;
1058 LISTBOX_PaintItem( descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1059 rect
.top
= rect
.bottom
;
1061 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1063 if (!IS_OWNERDRAW(descr
))
1065 /* Clear the bottom of the column */
1066 if (rect
.top
< descr
->height
)
1068 rect
.bottom
= descr
->height
;
1069 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1070 &rect
, NULL
, 0, NULL
);
1074 /* Go to the next column */
1075 rect
.left
+= descr
->column_width
;
1076 rect
.right
+= descr
->column_width
;
1078 col_pos
= descr
->page_size
- 1;
1083 if (rect
.top
>= descr
->height
) break;
1087 /* Paint the focus item now */
1088 if (focusRect
.top
!= focusRect
.bottom
&&
1089 descr
->caret_on
&& descr
->in_focus
)
1090 LISTBOX_PaintItem( descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1092 if (!IS_OWNERDRAW(descr
))
1094 /* Clear the remainder of the client area */
1095 if (rect
.top
< descr
->height
)
1097 rect
.bottom
= descr
->height
;
1098 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1099 &rect
, NULL
, 0, NULL
);
1101 if (rect
.right
< descr
->width
)
1103 rect
.left
= rect
.right
;
1104 rect
.right
= descr
->width
;
1106 rect
.bottom
= descr
->height
;
1107 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1108 &rect
, NULL
, 0, NULL
);
1111 if (oldFont
) SelectObject( hdc
, oldFont
);
1112 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1117 /***********************************************************************
1118 * LISTBOX_InvalidateItems
1120 * Invalidate all items from a given item. If the specified item is not
1121 * visible, nothing happens.
1123 static void LISTBOX_InvalidateItems( LB_DESCR
*descr
, INT index
)
1127 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1129 if (descr
->style
& LBS_NOREDRAW
)
1131 descr
->style
|= LBS_DISPLAYCHANGED
;
1134 rect
.bottom
= descr
->height
;
1135 InvalidateRect( descr
->self
, &rect
, TRUE
);
1136 if (descr
->style
& LBS_MULTICOLUMN
)
1138 /* Repaint the other columns */
1139 rect
.left
= rect
.right
;
1140 rect
.right
= descr
->width
;
1142 InvalidateRect( descr
->self
, &rect
, TRUE
);
1147 static void LISTBOX_InvalidateItemRect( LB_DESCR
*descr
, INT index
)
1151 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1152 InvalidateRect( descr
->self
, &rect
, TRUE
);
1155 /***********************************************************************
1156 * LISTBOX_GetItemHeight
1158 static LRESULT
LISTBOX_GetItemHeight( const LB_DESCR
*descr
, INT index
)
1160 if (descr
->style
& LBS_OWNERDRAWVARIABLE
&& descr
->nb_items
> 0)
1162 if ((index
< 0) || (index
>= descr
->nb_items
))
1164 SetLastError(ERROR_INVALID_INDEX
);
1167 return descr
->items
[index
].height
;
1169 else return descr
->item_height
;
1173 /***********************************************************************
1174 * LISTBOX_SetItemHeight
1176 static LRESULT
LISTBOX_SetItemHeight( LB_DESCR
*descr
, INT index
, INT height
, BOOL repaint
)
1178 if (height
> MAXBYTE
)
1181 if (!height
) height
= 1;
1183 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1185 if ((index
< 0) || (index
>= descr
->nb_items
))
1187 SetLastError(ERROR_INVALID_INDEX
);
1190 TRACE("[%p]: item %d height = %d\n", descr
->self
, index
, height
);
1191 descr
->items
[index
].height
= height
;
1192 LISTBOX_UpdateScroll( descr
);
1194 LISTBOX_InvalidateItems( descr
, index
);
1196 else if (height
!= descr
->item_height
)
1198 TRACE("[%p]: new height = %d\n", descr
->self
, height
);
1199 descr
->item_height
= height
;
1200 LISTBOX_UpdatePage( descr
);
1201 LISTBOX_UpdateScroll( descr
);
1203 InvalidateRect( descr
->self
, 0, TRUE
);
1209 /***********************************************************************
1210 * LISTBOX_SetHorizontalPos
1212 static void LISTBOX_SetHorizontalPos( LB_DESCR
*descr
, INT pos
)
1216 if (pos
> descr
->horz_extent
- descr
->width
)
1217 pos
= descr
->horz_extent
- descr
->width
;
1218 if (pos
< 0) pos
= 0;
1219 if (!(diff
= descr
->horz_pos
- pos
)) return;
1220 TRACE("[%p]: new horz pos = %d\n", descr
->self
, pos
);
1221 descr
->horz_pos
= pos
;
1222 LISTBOX_UpdateScroll( descr
);
1223 if (abs(diff
) < descr
->width
)
1226 /* Invalidate the focused item so it will be repainted correctly */
1227 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1228 InvalidateRect( descr
->self
, &rect
, TRUE
);
1229 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
1230 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1233 InvalidateRect( descr
->self
, NULL
, TRUE
);
1237 /***********************************************************************
1238 * LISTBOX_SetHorizontalExtent
1240 static LRESULT
LISTBOX_SetHorizontalExtent( LB_DESCR
*descr
, INT extent
)
1242 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1244 if (extent
<= 0) extent
= 1;
1245 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1246 TRACE("[%p]: new horz extent = %d\n", descr
->self
, extent
);
1247 descr
->horz_extent
= extent
;
1248 if (descr
->horz_pos
> extent
- descr
->width
)
1249 LISTBOX_SetHorizontalPos( descr
, extent
- descr
->width
);
1251 LISTBOX_UpdateScroll( descr
);
1256 /***********************************************************************
1257 * LISTBOX_SetColumnWidth
1259 static LRESULT
LISTBOX_SetColumnWidth( LB_DESCR
*descr
, INT width
)
1261 if (width
== descr
->column_width
) return LB_OKAY
;
1262 TRACE("[%p]: new column width = %d\n", descr
->self
, width
);
1263 descr
->column_width
= width
;
1264 LISTBOX_UpdatePage( descr
);
1269 /***********************************************************************
1272 * Returns the item height.
1274 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1278 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1283 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1285 ERR("unable to get DC.\n" );
1288 if (font
) oldFont
= SelectObject( hdc
, font
);
1289 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1290 if (oldFont
) SelectObject( hdc
, oldFont
);
1291 ReleaseDC( descr
->self
, hdc
);
1293 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1294 if (!IS_OWNERDRAW(descr
))
1295 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1300 /***********************************************************************
1301 * LISTBOX_MakeItemVisible
1303 * Make sure that a given item is partially or fully visible.
1305 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1309 TRACE("current top item %d, index %d, fully %d\n", descr
->top_item
, index
, fully
);
1311 if (index
<= descr
->top_item
) top
= index
;
1312 else if (descr
->style
& LBS_MULTICOLUMN
)
1314 INT cols
= descr
->width
;
1315 if (!fully
) cols
+= descr
->column_width
- 1;
1316 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1318 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1319 top
= index
- descr
->page_size
* (cols
- 1);
1321 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1323 INT height
= fully
? descr
->items
[index
].height
: 1;
1324 for (top
= index
; top
> descr
->top_item
; top
--)
1325 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1329 if (index
< descr
->top_item
+ descr
->page_size
) return;
1330 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1331 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1332 top
= index
- descr
->page_size
+ 1;
1334 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1337 /***********************************************************************
1338 * LISTBOX_SetCaretIndex
1341 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1344 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1346 INT oldfocus
= descr
->focus_item
;
1348 TRACE("old focus %d, index %d\n", oldfocus
, index
);
1350 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1351 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1352 if (index
== oldfocus
) return LB_OKAY
;
1354 LISTBOX_DrawFocusRect( descr
, FALSE
);
1355 descr
->focus_item
= index
;
1357 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1358 LISTBOX_DrawFocusRect( descr
, TRUE
);
1364 /***********************************************************************
1365 * LISTBOX_SelectItemRange
1367 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1369 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1374 /* A few sanity checks */
1376 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1377 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1379 if (!descr
->nb_items
) return LB_OKAY
;
1381 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1382 if (first
< 0) first
= 0;
1383 if (last
< first
) return LB_OKAY
;
1385 if (on
) /* Turn selection on */
1387 for (i
= first
; i
<= last
; i
++)
1389 if (descr
->items
[i
].selected
) continue;
1390 descr
->items
[i
].selected
= TRUE
;
1391 LISTBOX_InvalidateItemRect(descr
, i
);
1394 else /* Turn selection off */
1396 for (i
= first
; i
<= last
; i
++)
1398 if (!descr
->items
[i
].selected
) continue;
1399 descr
->items
[i
].selected
= FALSE
;
1400 LISTBOX_InvalidateItemRect(descr
, i
);
1406 /***********************************************************************
1407 * LISTBOX_SetSelection
1409 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1410 BOOL on
, BOOL send_notify
)
1412 TRACE( "cur_sel=%d index=%d notify=%s\n",
1413 descr
->selected_item
, index
, send_notify
? "YES" : "NO" );
1415 if (descr
->style
& LBS_NOSEL
)
1417 descr
->selected_item
= index
;
1420 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1421 if (descr
->style
& LBS_MULTIPLESEL
)
1423 if (index
== -1) /* Select all items */
1424 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1425 else /* Only one item */
1426 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1430 INT oldsel
= descr
->selected_item
;
1431 if (index
== oldsel
) return LB_OKAY
;
1432 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1433 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1434 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1435 descr
->selected_item
= index
;
1436 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1437 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1438 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1440 if( descr
->lphc
) /* set selection change flag for parent combo */
1441 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1447 /***********************************************************************
1450 * Change the caret position and extend the selection to the new caret.
1452 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1454 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1456 if ((index
< 0) || (index
>= descr
->nb_items
))
1459 /* Important, repaint needs to be done in this order if
1460 you want to mimic Windows behavior:
1461 1. Remove the focus and paint the item
1462 2. Remove the selection and paint the item(s)
1463 3. Set the selection and repaint the item(s)
1464 4. Set the focus to 'index' and repaint the item */
1466 /* 1. remove the focus and repaint the item */
1467 LISTBOX_DrawFocusRect( descr
, FALSE
);
1469 /* 2. then turn off the previous selection */
1470 /* 3. repaint the new selected item */
1471 if (descr
->style
& LBS_EXTENDEDSEL
)
1473 if (descr
->anchor_item
!= -1)
1475 INT first
= min( index
, descr
->anchor_item
);
1476 INT last
= max( index
, descr
->anchor_item
);
1478 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1479 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1480 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1483 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1485 /* Set selection to new caret item */
1486 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1489 /* 4. repaint the new item with the focus */
1490 descr
->focus_item
= index
;
1491 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1492 LISTBOX_DrawFocusRect( descr
, TRUE
);
1496 /***********************************************************************
1497 * LISTBOX_InsertItem
1499 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1500 LPWSTR str
, ULONG_PTR data
)
1504 INT oldfocus
= descr
->focus_item
;
1506 if (index
== -1) index
= descr
->nb_items
;
1507 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1508 if (!descr
->items
) max_items
= 0;
1509 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1510 if (descr
->nb_items
== max_items
)
1512 /* We need to grow the array */
1513 max_items
+= LB_ARRAY_GRANULARITY
;
1515 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1516 max_items
* sizeof(LB_ITEMDATA
) );
1518 item
= HeapAlloc( GetProcessHeap(), 0,
1519 max_items
* sizeof(LB_ITEMDATA
) );
1522 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1525 descr
->items
= item
;
1528 /* Insert the item structure */
1530 item
= &descr
->items
[index
];
1531 if (index
< descr
->nb_items
)
1532 RtlMoveMemory( item
+ 1, item
,
1533 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1537 item
->selected
= FALSE
;
1540 /* Get item height */
1542 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1544 MEASUREITEMSTRUCT mis
;
1545 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1547 mis
.CtlType
= ODT_LISTBOX
;
1550 mis
.itemData
= descr
->items
[index
].data
;
1551 mis
.itemHeight
= descr
->item_height
;
1552 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1553 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1554 TRACE("[%p]: measure item %d (%s) = %d\n",
1555 descr
->self
, index
, str
? debugstr_w(str
) : "", item
->height
);
1558 /* Repaint the items */
1560 LISTBOX_UpdateScroll( descr
);
1561 LISTBOX_InvalidateItems( descr
, index
);
1563 /* Move selection and focused item */
1564 /* If listbox was empty, set focus to the first item */
1565 if (descr
->nb_items
== 1)
1566 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1567 /* single select don't change selection index in win31 */
1568 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1570 descr
->selected_item
++;
1571 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1575 if (index
<= descr
->selected_item
)
1577 descr
->selected_item
++;
1578 descr
->focus_item
= oldfocus
; /* focus not changed */
1585 /***********************************************************************
1586 * LISTBOX_InsertString
1588 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
, LPCWSTR str
)
1590 LPWSTR new_str
= NULL
;
1594 if (HAS_STRINGS(descr
))
1596 static const WCHAR empty_stringW
[] = { 0 };
1597 if (!str
) str
= empty_stringW
;
1598 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1600 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1603 strcpyW(new_str
, str
);
1605 else data
= (ULONG_PTR
)str
;
1607 if (index
== -1) index
= descr
->nb_items
;
1608 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, data
)) != 0)
1610 HeapFree( GetProcessHeap(), 0, new_str
);
1614 TRACE("[%p]: added item %d %s\n",
1615 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1620 /***********************************************************************
1621 * LISTBOX_DeleteItem
1623 * Delete the content of an item. 'index' must be a valid index.
1625 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1627 /* save the item data before it gets freed by LB_RESETCONTENT */
1628 ULONG_PTR item_data
= descr
->items
[index
].data
;
1629 LPWSTR item_str
= descr
->items
[index
].str
;
1631 if (!descr
->nb_items
)
1632 SendMessageW( descr
->self
, LB_RESETCONTENT
, 0, 0 );
1634 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1635 * while Win95 sends it for all items with user data.
1636 * It's probably better to send it too often than not
1637 * often enough, so this is what we do here.
1639 if (IS_OWNERDRAW(descr
) || item_data
)
1641 DELETEITEMSTRUCT dis
;
1642 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1644 dis
.CtlType
= ODT_LISTBOX
;
1647 dis
.hwndItem
= descr
->self
;
1648 dis
.itemData
= item_data
;
1649 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1651 if (HAS_STRINGS(descr
))
1652 HeapFree( GetProcessHeap(), 0, item_str
);
1656 /***********************************************************************
1657 * LISTBOX_RemoveItem
1659 * Remove an item from the listbox and delete its content.
1661 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1666 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1668 /* We need to invalidate the original rect instead of the updated one. */
1669 LISTBOX_InvalidateItems( descr
, index
);
1672 LISTBOX_DeleteItem( descr
, index
);
1674 if (!descr
->nb_items
) return LB_OKAY
;
1676 /* Remove the item */
1678 item
= &descr
->items
[index
];
1679 if (index
< descr
->nb_items
)
1680 RtlMoveMemory( item
, item
+ 1,
1681 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1682 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1684 /* Shrink the item array if possible */
1686 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1687 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1689 max_items
-= LB_ARRAY_GRANULARITY
;
1690 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1691 max_items
* sizeof(LB_ITEMDATA
) );
1692 if (item
) descr
->items
= item
;
1694 /* Repaint the items */
1696 LISTBOX_UpdateScroll( descr
);
1697 /* if we removed the scrollbar, reset the top of the list
1698 (correct for owner-drawn ???) */
1699 if (descr
->nb_items
== descr
->page_size
)
1700 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1702 /* Move selection and focused item */
1703 if (!IS_MULTISELECT(descr
))
1705 if (index
== descr
->selected_item
)
1706 descr
->selected_item
= -1;
1707 else if (index
< descr
->selected_item
)
1709 descr
->selected_item
--;
1710 if (ISWIN31
) /* win 31 do not change the selected item number */
1711 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1715 if (descr
->focus_item
>= descr
->nb_items
)
1717 descr
->focus_item
= descr
->nb_items
- 1;
1718 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1724 /***********************************************************************
1725 * LISTBOX_ResetContent
1727 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1731 for(i
= descr
->nb_items
- 1; i
>=0; i
--) LISTBOX_DeleteItem( descr
, i
);
1732 HeapFree( GetProcessHeap(), 0, descr
->items
);
1733 descr
->nb_items
= 0;
1734 descr
->top_item
= 0;
1735 descr
->selected_item
= -1;
1736 descr
->focus_item
= 0;
1737 descr
->anchor_item
= -1;
1738 descr
->items
= NULL
;
1742 /***********************************************************************
1745 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, INT count
)
1749 if (HAS_STRINGS(descr
))
1751 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1755 /* FIXME: this is far from optimal... */
1756 if (count
> descr
->nb_items
)
1758 while (count
> descr
->nb_items
)
1759 if ((ret
= LISTBOX_InsertString( descr
, -1, 0 )) < 0)
1762 else if (count
< descr
->nb_items
)
1764 while (count
< descr
->nb_items
)
1765 if ((ret
= LISTBOX_RemoveItem( descr
, (descr
->nb_items
- 1) )) < 0)
1769 InvalidateRect( descr
->self
, NULL
, TRUE
);
1774 /***********************************************************************
1777 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1778 LPCWSTR filespec
, BOOL long_names
)
1781 LRESULT ret
= LB_OKAY
;
1782 WIN32_FIND_DATAW entry
;
1784 LRESULT maxinsert
= LB_ERR
;
1786 /* don't scan directory if we just want drives exclusively */
1787 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1788 /* scan directory */
1789 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1791 int le
= GetLastError();
1792 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1799 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1801 static const WCHAR bracketW
[] = { ']',0 };
1802 static const WCHAR dotW
[] = { '.',0 };
1803 if (!(attrib
& DDL_DIRECTORY
) ||
1804 !strcmpW( entry
.cFileName
, dotW
)) continue;
1806 if (!long_names
&& entry
.cAlternateFileName
[0])
1807 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1809 strcpyW( buffer
+ 1, entry
.cFileName
);
1810 strcatW(buffer
, bracketW
);
1812 else /* not a directory */
1814 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1815 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1817 if ((attrib
& DDL_EXCLUSIVE
) &&
1818 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1821 if (!long_names
&& entry
.cAlternateFileName
[0])
1822 strcpyW( buffer
, entry
.cAlternateFileName
);
1824 strcpyW( buffer
, entry
.cFileName
);
1826 if (!long_names
) CharLowerW( buffer
);
1827 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1828 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1830 if (ret
<= maxinsert
) maxinsert
++; else maxinsert
= ret
;
1831 } while (FindNextFileW( handle
, &entry
));
1832 FindClose( handle
);
1840 if (attrib
& DDL_DRIVES
)
1842 WCHAR buffer
[] = {'[','-','a','-',']',0};
1843 WCHAR root
[] = {'A',':','\\',0};
1845 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1847 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1848 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1857 /***********************************************************************
1858 * LISTBOX_HandleVScroll
1860 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1864 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1868 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1871 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1874 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1875 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1878 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1879 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1881 case SB_THUMBPOSITION
:
1882 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1885 info
.cbSize
= sizeof(info
);
1886 info
.fMask
= SIF_TRACKPOS
;
1887 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1888 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1891 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1894 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1901 /***********************************************************************
1902 * LISTBOX_HandleHScroll
1904 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1909 if (descr
->style
& LBS_MULTICOLUMN
)
1914 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
1918 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
1922 page
= descr
->width
/ descr
->column_width
;
1923 if (page
< 1) page
= 1;
1924 LISTBOX_SetTopItem( descr
,
1925 descr
->top_item
- page
* descr
->page_size
, TRUE
);
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
);
1933 case SB_THUMBPOSITION
:
1934 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
1937 info
.cbSize
= sizeof(info
);
1938 info
.fMask
= SIF_TRACKPOS
;
1939 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1940 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
1944 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1947 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1951 else if (descr
->horz_extent
)
1956 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
1959 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
1962 LISTBOX_SetHorizontalPos( descr
,
1963 descr
->horz_pos
- descr
->width
);
1966 LISTBOX_SetHorizontalPos( descr
,
1967 descr
->horz_pos
+ descr
->width
);
1969 case SB_THUMBPOSITION
:
1970 LISTBOX_SetHorizontalPos( descr
, pos
);
1973 info
.cbSize
= sizeof(info
);
1974 info
.fMask
= SIF_TRACKPOS
;
1975 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
1976 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
1979 LISTBOX_SetHorizontalPos( descr
, 0 );
1982 LISTBOX_SetHorizontalPos( descr
,
1983 descr
->horz_extent
- descr
->width
);
1990 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
1992 UINT pulScrollLines
= 3;
1994 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1996 /* if scrolling changes direction, ignore left overs */
1997 if ((delta
< 0 && descr
->wheel_remain
< 0) ||
1998 (delta
> 0 && descr
->wheel_remain
> 0))
1999 descr
->wheel_remain
+= delta
;
2001 descr
->wheel_remain
= delta
;
2003 if (descr
->wheel_remain
&& pulScrollLines
)
2006 pulScrollLines
= min((UINT
) descr
->page_size
, pulScrollLines
);
2007 cLineScroll
= pulScrollLines
* (float)descr
->wheel_remain
/ WHEEL_DELTA
;
2008 descr
->wheel_remain
-= WHEEL_DELTA
* cLineScroll
/ (int)pulScrollLines
;
2009 LISTBOX_SetTopItem( descr
, descr
->top_item
- cLineScroll
, TRUE
);
2014 /***********************************************************************
2015 * LISTBOX_HandleLButtonDown
2017 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
2019 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2021 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2022 descr
->self
, x
, y
, index
, descr
->focus_item
);
2024 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2026 if (!descr
->in_focus
)
2028 if( !descr
->lphc
) SetFocus( descr
->self
);
2029 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2032 if (index
== -1) return 0;
2036 if (descr
->style
& LBS_NOTIFY
)
2037 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2038 MAKELPARAM( x
, y
) );
2041 descr
->captured
= TRUE
;
2042 SetCapture( descr
->self
);
2044 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2046 /* we should perhaps make sure that all items are deselected
2047 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2048 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2049 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2052 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2053 if (keys
& MK_CONTROL
)
2055 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2056 LISTBOX_SetSelection( descr
, index
,
2057 !descr
->items
[index
].selected
,
2058 (descr
->style
& LBS_NOTIFY
) != 0);
2062 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2064 if (descr
->style
& LBS_EXTENDEDSEL
)
2066 LISTBOX_SetSelection( descr
, index
,
2067 descr
->items
[index
].selected
,
2068 (descr
->style
& LBS_NOTIFY
) != 0 );
2072 LISTBOX_SetSelection( descr
, index
,
2073 !descr
->items
[index
].selected
,
2074 (descr
->style
& LBS_NOTIFY
) != 0 );
2080 descr
->anchor_item
= index
;
2081 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2082 LISTBOX_SetSelection( descr
, index
,
2083 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2088 if (GetWindowLongW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2095 if (DragDetect( descr
->self
, pt
))
2096 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2103 /*************************************************************************
2104 * LISTBOX_HandleLButtonDownCombo [Internal]
2106 * Process LButtonDown message for the ComboListBox
2109 * pWnd [I] The windows internal structure
2110 * pDescr [I] The ListBox internal structure
2111 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2112 * x [I] X Mouse Coordinate
2113 * y [I] Y Mouse Coordinate
2116 * 0 since we are processing the WM_LBUTTONDOWN Message
2119 * This function is only to be used when a ListBox is a ComboListBox
2122 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2124 RECT clientRect
, screenRect
;
2130 GetClientRect(descr
->self
, &clientRect
);
2132 if(PtInRect(&clientRect
, mousePos
))
2134 /* MousePos is in client, resume normal processing */
2135 if (msg
== WM_LBUTTONDOWN
)
2137 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2138 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2140 else if (descr
->style
& LBS_NOTIFY
)
2141 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2145 POINT screenMousePos
;
2146 HWND hWndOldCapture
;
2148 /* Check the Non-Client Area */
2149 screenMousePos
= mousePos
;
2150 hWndOldCapture
= GetCapture();
2152 GetWindowRect(descr
->self
, &screenRect
);
2153 ClientToScreen(descr
->self
, &screenMousePos
);
2155 if(!PtInRect(&screenRect
, screenMousePos
))
2157 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2158 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2159 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2163 /* Check to see the NC is a scrollbar */
2165 LONG style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2166 /* Check Vertical scroll bar */
2167 if (style
& WS_VSCROLL
)
2169 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2170 if (PtInRect( &clientRect
, mousePos
))
2171 nHitTestType
= HTVSCROLL
;
2173 /* Check horizontal scroll bar */
2174 if (style
& WS_HSCROLL
)
2176 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2177 if (PtInRect( &clientRect
, mousePos
))
2178 nHitTestType
= HTHSCROLL
;
2180 /* Windows sends this message when a scrollbar is clicked
2183 if(nHitTestType
!= 0)
2185 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2186 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2188 /* Resume the Capture after scrolling is complete
2190 if(hWndOldCapture
!= 0)
2191 SetCapture(hWndOldCapture
);
2197 /***********************************************************************
2198 * LISTBOX_HandleLButtonUp
2200 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2202 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2203 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2204 LISTBOX_Timer
= LB_TIMER_NONE
;
2205 if (descr
->captured
)
2207 descr
->captured
= FALSE
;
2208 if (GetCapture() == descr
->self
) ReleaseCapture();
2209 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2210 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2216 /***********************************************************************
2217 * LISTBOX_HandleTimer
2219 * Handle scrolling upon a timer event.
2220 * Return TRUE if scrolling should continue.
2222 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2227 if (descr
->top_item
) index
= descr
->top_item
- 1;
2231 if (descr
->top_item
) index
-= descr
->page_size
;
2234 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2235 if (index
== descr
->focus_item
) index
++;
2236 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2238 case LB_TIMER_RIGHT
:
2239 if (index
+ descr
->page_size
< descr
->nb_items
)
2240 index
+= descr
->page_size
;
2245 if (index
== descr
->focus_item
) return FALSE
;
2246 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2251 /***********************************************************************
2252 * LISTBOX_HandleSystemTimer
2254 * WM_SYSTIMER handler.
2256 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2258 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2260 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2261 LISTBOX_Timer
= LB_TIMER_NONE
;
2267 /***********************************************************************
2268 * LISTBOX_HandleMouseMove
2270 * WM_MOUSEMOVE handler.
2272 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2276 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2278 if (!descr
->captured
) return;
2280 if (descr
->style
& LBS_MULTICOLUMN
)
2283 else if (y
>= descr
->item_height
* descr
->page_size
)
2284 y
= descr
->item_height
* descr
->page_size
- 1;
2288 dir
= LB_TIMER_LEFT
;
2291 else if (x
>= descr
->width
)
2293 dir
= LB_TIMER_RIGHT
;
2294 x
= descr
->width
- 1;
2299 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2300 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2303 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2304 if (index
== -1) index
= descr
->focus_item
;
2305 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2307 /* Start/stop the system timer */
2309 if (dir
!= LB_TIMER_NONE
)
2310 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2311 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2312 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2313 LISTBOX_Timer
= dir
;
2317 /***********************************************************************
2318 * LISTBOX_HandleKeyDown
2320 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2323 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2324 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2325 bForceSelection
= FALSE
; /* only for single select list */
2327 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2329 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2330 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2331 (LPARAM
)descr
->self
);
2332 if (caret
== -2) return 0;
2334 if (caret
== -1) switch(key
)
2337 if (descr
->style
& LBS_MULTICOLUMN
)
2339 bForceSelection
= FALSE
;
2340 if (descr
->focus_item
>= descr
->page_size
)
2341 caret
= descr
->focus_item
- descr
->page_size
;
2346 caret
= descr
->focus_item
- 1;
2347 if (caret
< 0) caret
= 0;
2350 if (descr
->style
& LBS_MULTICOLUMN
)
2352 bForceSelection
= FALSE
;
2353 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2354 caret
= descr
->focus_item
+ descr
->page_size
;
2359 caret
= descr
->focus_item
+ 1;
2360 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2364 if (descr
->style
& LBS_MULTICOLUMN
)
2366 INT page
= descr
->width
/ descr
->column_width
;
2367 if (page
< 1) page
= 1;
2368 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2370 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2371 if (caret
< 0) caret
= 0;
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
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2387 caret
= descr
->nb_items
- 1;
2390 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2391 else if (descr
->style
& LBS_MULTIPLESEL
)
2393 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2394 !descr
->items
[descr
->focus_item
].selected
,
2395 (descr
->style
& LBS_NOTIFY
) != 0 );
2399 bForceSelection
= FALSE
;
2401 if (bForceSelection
) /* focused item is used instead of key */
2402 caret
= descr
->focus_item
;
2405 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2406 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2407 !IS_MULTISELECT(descr
))
2408 descr
->anchor_item
= caret
;
2409 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2411 if (descr
->style
& LBS_MULTIPLESEL
)
2412 descr
->selected_item
= caret
;
2414 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2415 if (descr
->style
& LBS_NOTIFY
)
2417 if (descr
->lphc
&& IsWindowVisible( descr
->self
))
2419 /* make sure that combo parent doesn't hide us */
2420 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2422 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2429 /***********************************************************************
2430 * LISTBOX_HandleChar
2432 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2440 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2442 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2443 MAKEWPARAM(charW
, descr
->focus_item
),
2444 (LPARAM
)descr
->self
);
2445 if (caret
== -2) return 0;
2448 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2451 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2452 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2453 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2454 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2455 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2461 /***********************************************************************
2464 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2467 MEASUREITEMSTRUCT mis
;
2470 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2473 GetClientRect( hwnd
, &rect
);
2475 descr
->owner
= GetParent( descr
->self
);
2476 descr
->style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2477 descr
->width
= rect
.right
- rect
.left
;
2478 descr
->height
= rect
.bottom
- rect
.top
;
2479 descr
->items
= NULL
;
2480 descr
->nb_items
= 0;
2481 descr
->top_item
= 0;
2482 descr
->selected_item
= -1;
2483 descr
->focus_item
= 0;
2484 descr
->anchor_item
= -1;
2485 descr
->item_height
= 1;
2486 descr
->page_size
= 1;
2487 descr
->column_width
= 150;
2488 descr
->horz_extent
= (descr
->style
& WS_HSCROLL
) ? 1 : 0;
2489 descr
->horz_pos
= 0;
2492 descr
->wheel_remain
= 0;
2493 descr
->caret_on
= !lphc
;
2494 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2495 descr
->in_focus
= FALSE
;
2496 descr
->captured
= FALSE
;
2498 descr
->locale
= GetUserDefaultLCID();
2503 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2504 descr
->owner
= lphc
->self
;
2507 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2509 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2511 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2512 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2513 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2514 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2516 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2518 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2520 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2521 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2525 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2526 mis
.CtlType
= ODT_LISTBOX
;
2531 mis
.itemHeight
= descr
->item_height
;
2532 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2533 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2537 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2542 /***********************************************************************
2545 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2547 LISTBOX_ResetContent( descr
);
2548 SetWindowLongPtrW( descr
->self
, 0, 0 );
2549 HeapFree( GetProcessHeap(), 0, descr
);
2554 /***********************************************************************
2555 * ListBoxWndProc_common
2557 LRESULT
ListBoxWndProc_common( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2559 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2560 LPHEADCOMBO lphc
= 0;
2565 if (!IsWindow(hwnd
)) return 0;
2567 if (msg
== WM_CREATE
)
2569 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2570 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= lpcs
->lpCreateParams
;
2571 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2572 TRACE("creating hwnd %p descr %p\n", hwnd
, (void *)GetWindowLongPtrW( hwnd
, 0 ) );
2575 /* Ignore all other messages before we get a WM_CREATE */
2576 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2577 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2579 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2581 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2582 descr
->self
, SPY_GetMsgName(msg
, descr
->self
), wParam
, lParam
);
2586 case LB_RESETCONTENT
:
2587 LISTBOX_ResetContent( descr
);
2588 LISTBOX_UpdateScroll( descr
);
2589 InvalidateRect( descr
->self
, NULL
, TRUE
);
2596 if(unicode
|| !HAS_STRINGS(descr
))
2597 textW
= (LPWSTR
)lParam
;
2600 LPSTR textA
= (LPSTR
)lParam
;
2601 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2602 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2603 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2607 wParam
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2608 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2609 if (!unicode
&& HAS_STRINGS(descr
))
2610 HeapFree(GetProcessHeap(), 0, textW
);
2614 case LB_INSERTSTRING
:
2618 if(unicode
|| !HAS_STRINGS(descr
))
2619 textW
= (LPWSTR
)lParam
;
2622 LPSTR textA
= (LPSTR
)lParam
;
2623 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2624 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2625 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2629 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2630 if(!unicode
&& HAS_STRINGS(descr
))
2631 HeapFree(GetProcessHeap(), 0, textW
);
2639 if(unicode
|| !HAS_STRINGS(descr
))
2640 textW
= (LPWSTR
)lParam
;
2643 LPSTR textA
= (LPSTR
)lParam
;
2644 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2645 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2646 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2650 wParam
= LISTBOX_FindFileStrPos( descr
, textW
);
2651 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2652 if(!unicode
&& HAS_STRINGS(descr
))
2653 HeapFree(GetProcessHeap(), 0, textW
);
2657 case LB_DELETESTRING
:
2658 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2659 return descr
->nb_items
;
2662 SetLastError(ERROR_INVALID_INDEX
);
2666 case LB_GETITEMDATA
:
2667 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2669 SetLastError(ERROR_INVALID_INDEX
);
2672 return descr
->items
[wParam
].data
;
2674 case LB_SETITEMDATA
:
2675 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2677 SetLastError(ERROR_INVALID_INDEX
);
2680 descr
->items
[wParam
].data
= lParam
;
2681 /* undocumented: returns TRUE, not LB_OKAY (0) */
2685 return descr
->nb_items
;
2688 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, unicode
);
2691 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2693 SetLastError(ERROR_INVALID_INDEX
);
2696 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2697 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2698 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2699 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2702 if (descr
->nb_items
== 0)
2704 if (!IS_MULTISELECT(descr
))
2705 return descr
->selected_item
;
2706 if (descr
->selected_item
!= -1)
2707 return descr
->selected_item
;
2708 return descr
->focus_item
;
2709 /* otherwise, if the user tries to move the selection with the */
2710 /* arrow keys, we will give the application something to choke on */
2711 case LB_GETTOPINDEX
:
2712 return descr
->top_item
;
2714 case LB_GETITEMHEIGHT
:
2715 return LISTBOX_GetItemHeight( descr
, wParam
);
2717 case LB_SETITEMHEIGHT
:
2718 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2720 case LB_ITEMFROMPOINT
:
2727 /* The hiword of the return value is not a client area
2728 hittest as suggested by MSDN, but rather a hittest on
2729 the returned listbox item. */
2731 if(descr
->nb_items
== 0)
2732 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2734 pt
.x
= (short)LOWORD(lParam
);
2735 pt
.y
= (short)HIWORD(lParam
);
2737 SetRect(&rect
, 0, 0, descr
->width
, descr
->height
);
2739 if(!PtInRect(&rect
, pt
))
2741 pt
.x
= min(pt
.x
, rect
.right
- 1);
2742 pt
.x
= max(pt
.x
, 0);
2743 pt
.y
= min(pt
.y
, rect
.bottom
- 1);
2744 pt
.y
= max(pt
.y
, 0);
2748 index
= LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
);
2752 index
= descr
->nb_items
- 1;
2755 return MAKELONG(index
, hit
? 0 : 1);
2758 case LB_SETCARETINDEX
:
2759 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2760 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2767 case LB_GETCARETINDEX
:
2768 return descr
->focus_item
;
2770 case LB_SETTOPINDEX
:
2771 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2773 case LB_SETCOLUMNWIDTH
:
2774 return LISTBOX_SetColumnWidth( descr
, wParam
);
2776 case LB_GETITEMRECT
:
2777 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2783 if(unicode
|| !HAS_STRINGS(descr
))
2784 textW
= (LPWSTR
)lParam
;
2787 LPSTR textA
= (LPSTR
)lParam
;
2788 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2789 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2790 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2792 ret
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2793 if(!unicode
&& HAS_STRINGS(descr
))
2794 HeapFree(GetProcessHeap(), 0, textW
);
2798 case LB_FINDSTRINGEXACT
:
2802 if(unicode
|| !HAS_STRINGS(descr
))
2803 textW
= (LPWSTR
)lParam
;
2806 LPSTR textA
= (LPSTR
)lParam
;
2807 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2808 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2809 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2811 ret
= LISTBOX_FindString( descr
, wParam
, textW
, TRUE
);
2812 if(!unicode
&& HAS_STRINGS(descr
))
2813 HeapFree(GetProcessHeap(), 0, textW
);
2817 case LB_SELECTSTRING
:
2822 if(HAS_STRINGS(descr
))
2823 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2824 debugstr_a((LPSTR
)lParam
));
2825 if(unicode
|| !HAS_STRINGS(descr
))
2826 textW
= (LPWSTR
)lParam
;
2829 LPSTR textA
= (LPSTR
)lParam
;
2830 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2831 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2832 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2834 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2835 if(!unicode
&& HAS_STRINGS(descr
))
2836 HeapFree(GetProcessHeap(), 0, textW
);
2837 if (index
!= LB_ERR
)
2839 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2840 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2846 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2848 return descr
->items
[wParam
].selected
;
2851 return LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2854 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2855 LISTBOX_SetCaretIndex( descr
, wParam
, FALSE
);
2856 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2857 if (ret
!= LB_ERR
) ret
= descr
->selected_item
;
2860 case LB_GETSELCOUNT
:
2861 return LISTBOX_GetSelCount( descr
);
2863 case LB_GETSELITEMS
:
2864 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2866 case LB_SELITEMRANGE
:
2867 if (LOWORD(lParam
) <= HIWORD(lParam
))
2868 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2869 HIWORD(lParam
), wParam
);
2871 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2872 LOWORD(lParam
), wParam
);
2874 case LB_SELITEMRANGEEX
:
2875 if ((INT
)lParam
>= (INT
)wParam
)
2876 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2878 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
2880 case LB_GETHORIZONTALEXTENT
:
2881 return descr
->horz_extent
;
2883 case LB_SETHORIZONTALEXTENT
:
2884 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
2886 case LB_GETANCHORINDEX
:
2887 return descr
->anchor_item
;
2889 case LB_SETANCHORINDEX
:
2890 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2892 SetLastError(ERROR_INVALID_INDEX
);
2895 descr
->anchor_item
= (INT
)wParam
;
2903 textW
= (LPWSTR
)lParam
;
2906 LPSTR textA
= (LPSTR
)lParam
;
2907 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2908 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2909 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2911 ret
= LISTBOX_Directory( descr
, wParam
, textW
, msg
== LB_DIR
);
2913 HeapFree(GetProcessHeap(), 0, textW
);
2918 return descr
->locale
;
2923 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
2925 ret
= descr
->locale
;
2926 descr
->locale
= (LCID
)wParam
;
2930 case LB_INITSTORAGE
:
2931 return LISTBOX_InitStorage( descr
, wParam
);
2934 return LISTBOX_SetCount( descr
, (INT
)wParam
);
2936 case LB_SETTABSTOPS
:
2937 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
);
2940 if (descr
->caret_on
)
2942 descr
->caret_on
= TRUE
;
2943 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2944 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2948 if (!descr
->caret_on
)
2950 descr
->caret_on
= FALSE
;
2951 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2952 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2955 case LB_GETLISTBOXINFO
:
2956 return descr
->page_size
;
2959 return LISTBOX_Destroy( descr
);
2962 InvalidateRect( descr
->self
, NULL
, TRUE
);
2966 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
2970 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2972 case WM_PRINTCLIENT
:
2976 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
2977 ret
= LISTBOX_Paint( descr
, hdc
);
2978 if( !wParam
) EndPaint( descr
->self
, &ps
);
2982 LISTBOX_UpdateSize( descr
);
2985 return (LRESULT
)descr
->font
;
2987 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
2988 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
2991 descr
->in_focus
= TRUE
;
2992 descr
->caret_on
= TRUE
;
2993 if (descr
->focus_item
!= -1)
2994 LISTBOX_DrawFocusRect( descr
, TRUE
);
2995 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
2998 descr
->in_focus
= FALSE
;
2999 descr
->wheel_remain
= 0;
3000 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
3001 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3002 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
3005 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3007 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3009 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
3010 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
3011 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
3012 case WM_LBUTTONDOWN
:
3014 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3015 (INT16
)LOWORD(lParam
),
3016 (INT16
)HIWORD(lParam
) );
3017 return LISTBOX_HandleLButtonDown( descr
, wParam
,
3018 (INT16
)LOWORD(lParam
),
3019 (INT16
)HIWORD(lParam
) );
3020 case WM_LBUTTONDBLCLK
:
3022 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3023 (INT16
)LOWORD(lParam
),
3024 (INT16
)HIWORD(lParam
) );
3025 if (descr
->style
& LBS_NOTIFY
)
3026 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
3029 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
3031 BOOL captured
= descr
->captured
;
3035 mousePos
.x
= (INT16
)LOWORD(lParam
);
3036 mousePos
.y
= (INT16
)HIWORD(lParam
);
3039 * If we are in a dropdown combobox, we simulate that
3040 * the mouse is captured to show the tracking of the item.
3042 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
3043 descr
->captured
= TRUE
;
3045 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
3047 descr
->captured
= captured
;
3049 else if (GetCapture() == descr
->self
)
3051 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
3052 (INT16
)HIWORD(lParam
) );
3062 * If the mouse button "up" is not in the listbox,
3063 * we make sure there is no selection by re-selecting the
3064 * item that was selected when the listbox was made visible.
3066 mousePos
.x
= (INT16
)LOWORD(lParam
);
3067 mousePos
.y
= (INT16
)HIWORD(lParam
);
3069 GetClientRect(descr
->self
, &clientRect
);
3072 * When the user clicks outside the combobox and the focus
3073 * is lost, the owning combobox will send a fake buttonup with
3074 * 0xFFFFFFF as the mouse location, we must also revert the
3075 * selection to the original selection.
3077 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
3078 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
3080 return LISTBOX_HandleLButtonUp( descr
);
3082 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3084 /* for some reason Windows makes it possible to
3085 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3087 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3088 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3089 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3091 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3095 return LISTBOX_HandleKeyDown( descr
, wParam
);
3100 charW
= (WCHAR
)wParam
;
3103 CHAR charA
= (CHAR
)wParam
;
3104 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
3106 return LISTBOX_HandleChar( descr
, charW
);
3109 return LISTBOX_HandleSystemTimer( descr
);
3111 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3114 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3115 wParam
, (LPARAM
)descr
->self
);
3116 TRACE("hbrush = %p\n", hbrush
);
3118 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3121 GetClientRect(descr
->self
, &rect
);
3122 FillRect((HDC
)wParam
, &rect
, hbrush
);
3127 if( lphc
) return 0;
3128 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3129 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3132 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3141 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3142 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3143 hwnd
, msg
, wParam
, lParam
);
3146 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3147 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3150 DWORD WINAPI
GetListBoxInfo(HWND hwnd
)
3152 TRACE("%p\n", hwnd
);
3153 return SendMessageW(hwnd
, LB_GETLISTBOXINFO
, 0, 0);