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.
42 #include "wine/winuser16.h"
43 #include "wine/unicode.h"
44 #include "user_private.h"
46 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(listbox
);
50 /* Items array granularity */
51 #define LB_ARRAY_GRANULARITY 16
53 /* Scrolling timeout in ms */
54 #define LB_SCROLL_TIMEOUT 50
56 /* Listbox system timer id */
59 /* flag listbox changed while setredraw false - internal style */
60 #define LBS_DISPLAYCHANGED 0x80000000
65 LPWSTR str
; /* Item text */
66 BOOL selected
; /* Is item selected? */
67 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
68 ULONG_PTR data
; /* User data */
71 /* Listbox structure */
74 HWND self
; /* Our own window handle */
75 HWND owner
; /* Owner window to send notifications to */
76 UINT style
; /* Window style */
77 INT width
; /* Window width */
78 INT height
; /* Window height */
79 LB_ITEMDATA
*items
; /* Array of items */
80 INT nb_items
; /* Number of items */
81 INT top_item
; /* Top visible item */
82 INT selected_item
; /* Selected item */
83 INT focus_item
; /* Item that has the focus */
84 INT anchor_item
; /* Anchor item for extended selection */
85 INT item_height
; /* Default item height */
86 INT page_size
; /* Items per listbox page */
87 INT column_width
; /* Column width for multi-column listboxes */
88 INT horz_extent
; /* Horizontal extent (0 if no hscroll) */
89 INT horz_pos
; /* Horizontal position */
90 INT nb_tabs
; /* Number of tabs in array */
91 INT
*tabs
; /* Array of tabs */
92 INT avg_char_width
; /* Average width of characters */
93 BOOL caret_on
; /* Is caret on? */
94 BOOL captured
; /* Is mouse captured? */
96 HFONT font
; /* Current font */
97 LCID locale
; /* Current locale for string comparisons */
98 LPHEADCOMBO lphc
; /* ComboLBox */
102 #define IS_OWNERDRAW(descr) \
103 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
105 #define HAS_STRINGS(descr) \
106 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
109 #define IS_MULTISELECT(descr) \
110 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
111 !((descr)->style & LBS_NOSEL))
113 #define SEND_NOTIFICATION(descr,code) \
114 (SendMessageW( (descr)->owner, WM_COMMAND, \
115 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
117 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
119 /* Current timer status */
129 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
131 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
132 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
134 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
);
136 /*********************************************************************
137 * listbox class descriptor
139 static const WCHAR listboxW
[] = {'L','i','s','t','B','o','x',0};
140 const struct builtin_class_descr LISTBOX_builtin_class
=
143 CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
144 ListBoxWndProcA
, /* procA */
145 ListBoxWndProcW
, /* procW */
146 sizeof(LB_DESCR
*), /* extra */
147 IDC_ARROW
, /* cursor */
152 /*********************************************************************
153 * combolbox class descriptor
155 static const WCHAR combolboxW
[] = {'C','o','m','b','o','L','B','o','x',0};
156 const struct builtin_class_descr COMBOLBOX_builtin_class
=
158 combolboxW
, /* name */
159 CS_DBLCLKS
| CS_SAVEBITS
, /* style */
160 ListBoxWndProcA
, /* procA */
161 ListBoxWndProcW
, /* procW */
162 sizeof(LB_DESCR
*), /* extra */
163 IDC_ARROW
, /* cursor */
168 /* check whether app is a Win 3.1 app */
169 static inline BOOL
is_old_app( LB_DESCR
*descr
)
171 return (GetExpWinVer16( GetWindowLongPtrW(descr
->self
, GWLP_HINSTANCE
) ) & 0xFF00 ) == 0x0300;
175 /***********************************************************************
176 * LISTBOX_GetCurrentPageSize
178 * Return the current page size
180 static INT
LISTBOX_GetCurrentPageSize( const LB_DESCR
*descr
)
183 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
184 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
186 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
188 if (i
== descr
->top_item
) return 1;
189 else return i
- descr
->top_item
;
193 /***********************************************************************
194 * LISTBOX_GetMaxTopIndex
196 * Return the maximum possible index for the top of the listbox.
198 static INT
LISTBOX_GetMaxTopIndex( const LB_DESCR
*descr
)
202 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
204 page
= descr
->height
;
205 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
206 if ((page
-= descr
->items
[max
].height
) < 0) break;
207 if (max
< descr
->nb_items
- 1) max
++;
209 else if (descr
->style
& LBS_MULTICOLUMN
)
211 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
212 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
213 max
= (max
- page
) * descr
->page_size
;
217 max
= descr
->nb_items
- descr
->page_size
;
219 if (max
< 0) max
= 0;
224 /***********************************************************************
225 * LISTBOX_UpdateScroll
227 * Update the scrollbars. Should be called whenever the content
228 * of the listbox changes.
230 static void LISTBOX_UpdateScroll( LB_DESCR
*descr
)
234 /* Check the listbox scroll bar flags individually before we call
235 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
236 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
237 scroll bar when we do not need one.
238 if (!(descr->style & WS_VSCROLL)) return;
241 /* It is important that we check descr->style, and not wnd->dwStyle,
242 for WS_VSCROLL, as the former is exactly the one passed in
243 argument to CreateWindow.
244 In Windows (and from now on in Wine :) a listbox created
245 with such a style (no WS_SCROLL) does not update
246 the scrollbar with listbox-related data, thus letting
247 the programmer use it for his/her own purposes. */
249 if (descr
->style
& LBS_NOREDRAW
) return;
250 info
.cbSize
= sizeof(info
);
252 if (descr
->style
& LBS_MULTICOLUMN
)
255 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
256 info
.nPos
= descr
->top_item
/ descr
->page_size
;
257 info
.nPage
= descr
->width
/ descr
->column_width
;
258 if (info
.nPage
< 1) info
.nPage
= 1;
259 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
260 if (descr
->style
& LBS_DISABLENOSCROLL
)
261 info
.fMask
|= SIF_DISABLENOSCROLL
;
262 if (descr
->style
& WS_HSCROLL
)
263 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
265 info
.fMask
= SIF_RANGE
;
266 if (descr
->style
& WS_VSCROLL
)
267 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
272 info
.nMax
= descr
->nb_items
- 1;
273 info
.nPos
= descr
->top_item
;
274 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
275 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
276 if (descr
->style
& LBS_DISABLENOSCROLL
)
277 info
.fMask
|= SIF_DISABLENOSCROLL
;
278 if (descr
->style
& WS_VSCROLL
)
279 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
281 if (descr
->horz_extent
)
284 info
.nMax
= descr
->horz_extent
- 1;
285 info
.nPos
= descr
->horz_pos
;
286 info
.nPage
= descr
->width
;
287 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
288 if (descr
->style
& LBS_DISABLENOSCROLL
)
289 info
.fMask
|= SIF_DISABLENOSCROLL
;
290 if (descr
->style
& WS_HSCROLL
)
291 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
297 /***********************************************************************
300 * Set the top item of the listbox, scrolling up or down if necessary.
302 static LRESULT
LISTBOX_SetTopItem( LB_DESCR
*descr
, INT index
, BOOL scroll
)
304 INT max
= LISTBOX_GetMaxTopIndex( descr
);
306 TRACE("setting top item %d, scroll %d\n", index
, scroll
);
308 if (index
> max
) index
= max
;
309 if (index
< 0) index
= 0;
310 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
311 if (descr
->top_item
== index
) return LB_OKAY
;
312 if (descr
->style
& LBS_MULTICOLUMN
)
314 INT diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
315 if (scroll
&& (abs(diff
) < descr
->width
))
316 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
317 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
325 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
329 if (index
> descr
->top_item
)
331 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
332 diff
-= descr
->items
[i
].height
;
336 for (i
= index
; i
< descr
->top_item
; i
++)
337 diff
+= descr
->items
[i
].height
;
341 diff
= (descr
->top_item
- index
) * descr
->item_height
;
343 if (abs(diff
) < descr
->height
)
344 ScrollWindowEx( descr
->self
, 0, diff
, NULL
, NULL
, 0, NULL
,
345 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
349 if (!scroll
) InvalidateRect( descr
->self
, NULL
, TRUE
);
350 descr
->top_item
= index
;
351 LISTBOX_UpdateScroll( descr
);
356 /***********************************************************************
359 * Update the page size. Should be called when the size of
360 * the client area or the item height changes.
362 static void LISTBOX_UpdatePage( LB_DESCR
*descr
)
366 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
368 if (page_size
== descr
->page_size
) return;
369 descr
->page_size
= page_size
;
370 if (descr
->style
& LBS_MULTICOLUMN
)
371 InvalidateRect( descr
->self
, NULL
, TRUE
);
372 LISTBOX_SetTopItem( descr
, descr
->top_item
, FALSE
);
376 /***********************************************************************
379 * Update the size of the listbox. Should be called when the size of
380 * the client area changes.
382 static void LISTBOX_UpdateSize( LB_DESCR
*descr
)
386 GetClientRect( descr
->self
, &rect
);
387 descr
->width
= rect
.right
- rect
.left
;
388 descr
->height
= rect
.bottom
- rect
.top
;
389 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
394 GetWindowRect( descr
->self
, &rect
);
395 if(descr
->item_height
!= 0)
396 remaining
= descr
->height
% descr
->item_height
;
399 if ((descr
->height
> descr
->item_height
) && remaining
)
401 if (is_old_app(descr
))
402 { /* give a margin for error to 16 bits programs - if we need
403 less than the height of the nonclient area, round to the
404 *next* number of items */
405 int ncheight
= rect
.bottom
- rect
.top
- descr
->height
;
406 if ((descr
->item_height
- remaining
) <= ncheight
)
407 remaining
= remaining
- descr
->item_height
;
409 TRACE("[%p]: changing height %d -> %d\n",
410 descr
->self
, descr
->height
, descr
->height
- remaining
);
411 SetWindowPos( descr
->self
, 0, 0, 0, rect
.right
- rect
.left
,
412 rect
.bottom
- rect
.top
- remaining
,
413 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
417 TRACE("[%p]: new size = %d,%d\n", descr
->self
, descr
->width
, descr
->height
);
418 LISTBOX_UpdatePage( descr
);
419 LISTBOX_UpdateScroll( descr
);
421 /* Invalidate the focused item so it will be repainted correctly */
422 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
424 InvalidateRect( descr
->self
, &rect
, FALSE
);
429 /***********************************************************************
430 * LISTBOX_GetItemRect
432 * Get the rectangle enclosing an item, in listbox client coordinates.
433 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
435 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
)
437 /* Index <= 0 is legal even on empty listboxes */
438 if (index
&& (index
>= descr
->nb_items
))
440 memset(rect
, 0, sizeof(*rect
));
441 SetLastError(ERROR_INVALID_INDEX
);
444 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
445 if (descr
->style
& LBS_MULTICOLUMN
)
447 INT col
= (index
/ descr
->page_size
) -
448 (descr
->top_item
/ descr
->page_size
);
449 rect
->left
+= col
* descr
->column_width
;
450 rect
->right
= rect
->left
+ descr
->column_width
;
451 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
452 rect
->bottom
= rect
->top
+ descr
->item_height
;
454 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
457 rect
->right
+= descr
->horz_pos
;
458 if ((index
>= 0) && (index
< descr
->nb_items
))
460 if (index
< descr
->top_item
)
462 for (i
= descr
->top_item
-1; i
>= index
; i
--)
463 rect
->top
-= descr
->items
[i
].height
;
467 for (i
= descr
->top_item
; i
< index
; i
++)
468 rect
->top
+= descr
->items
[i
].height
;
470 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
476 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
477 rect
->bottom
= rect
->top
+ descr
->item_height
;
478 rect
->right
+= descr
->horz_pos
;
481 TRACE("item %d, rect %s\n", index
, wine_dbgstr_rect(rect
));
483 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
484 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
488 /***********************************************************************
489 * LISTBOX_GetItemFromPoint
491 * Return the item nearest from point (x,y) (in client coordinates).
493 static INT
LISTBOX_GetItemFromPoint( const LB_DESCR
*descr
, INT x
, INT y
)
495 INT index
= descr
->top_item
;
497 if (!descr
->nb_items
) return -1; /* No items */
498 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
503 while (index
< descr
->nb_items
)
505 if ((pos
+= descr
->items
[index
].height
) > y
) break;
514 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
518 else if (descr
->style
& LBS_MULTICOLUMN
)
520 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
521 if (y
>= 0) index
+= y
/ descr
->item_height
;
522 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
523 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
527 index
+= (y
/ descr
->item_height
);
529 if (index
< 0) return 0;
530 if (index
>= descr
->nb_items
) return -1;
535 /***********************************************************************
540 static void LISTBOX_PaintItem( LB_DESCR
*descr
, HDC hdc
, const RECT
*rect
,
541 INT index
, UINT action
, BOOL ignoreFocus
)
543 LB_ITEMDATA
*item
= NULL
;
544 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
546 if (IS_OWNERDRAW(descr
))
554 if (action
== ODA_FOCUS
)
555 DrawFocusRect( hdc
, rect
);
557 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
561 /* some programs mess with the clipping region when
562 drawing the item, *and* restore the previous region
563 after they are done, so a region has better to exist
564 else everything ends clipped */
565 GetClientRect(descr
->self
, &r
);
566 hrgn
= CreateRectRgnIndirect(&r
);
567 SelectClipRgn( hdc
, hrgn
);
568 DeleteObject( hrgn
);
570 dis
.CtlType
= ODT_LISTBOX
;
571 dis
.CtlID
= GetWindowLongPtrW( descr
->self
, GWLP_ID
);
572 dis
.hwndItem
= descr
->self
;
573 dis
.itemAction
= action
;
577 if (item
->selected
) dis
.itemState
|= ODS_SELECTED
;
578 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
580 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
581 if (!IsWindowEnabled(descr
->self
)) dis
.itemState
|= ODS_DISABLED
;
582 dis
.itemData
= item
->data
;
584 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
585 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
586 dis
.itemState
, wine_dbgstr_rect(rect
) );
587 SendMessageW(descr
->owner
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
591 COLORREF oldText
= 0, oldBk
= 0;
593 if (action
== ODA_FOCUS
)
595 DrawFocusRect( hdc
, rect
);
598 if (item
&& item
->selected
)
600 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
601 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
604 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
605 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
606 wine_dbgstr_rect(rect
) );
608 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
609 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
610 else if (!(descr
->style
& LBS_USETABSTOPS
))
611 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
612 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
613 strlenW(item
->str
), NULL
);
616 /* Output empty string to paint background in the full width. */
617 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
618 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
619 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
620 item
->str
, strlenW(item
->str
),
621 descr
->nb_tabs
, descr
->tabs
, 0);
623 if (item
&& item
->selected
)
625 SetBkColor( hdc
, oldBk
);
626 SetTextColor( hdc
, oldText
);
628 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
630 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
635 /***********************************************************************
638 * Change the redraw flag.
640 static void LISTBOX_SetRedraw( LB_DESCR
*descr
, BOOL on
)
644 if (!(descr
->style
& LBS_NOREDRAW
)) return;
645 descr
->style
&= ~LBS_NOREDRAW
;
646 if (descr
->style
& LBS_DISPLAYCHANGED
)
647 { /* page was changed while setredraw false, refresh automatically */
648 InvalidateRect(descr
->self
, NULL
, TRUE
);
649 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
650 { /* reset top of page if less than number of items/page */
651 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
652 if (descr
->top_item
< 0) descr
->top_item
= 0;
654 descr
->style
&= ~LBS_DISPLAYCHANGED
;
656 LISTBOX_UpdateScroll( descr
);
658 else descr
->style
|= LBS_NOREDRAW
;
662 /***********************************************************************
663 * LISTBOX_RepaintItem
665 * Repaint a single item synchronously.
667 static void LISTBOX_RepaintItem( LB_DESCR
*descr
, INT index
, UINT action
)
672 HBRUSH hbrush
, oldBrush
= 0;
674 /* Do not repaint the item if the item is not visible */
675 if (!IsWindowVisible(descr
->self
)) return;
676 if (descr
->style
& LBS_NOREDRAW
)
678 descr
->style
|= LBS_DISPLAYCHANGED
;
681 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
682 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
683 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
684 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
685 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
686 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
687 if (!IsWindowEnabled(descr
->self
))
688 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
689 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
690 LISTBOX_PaintItem( descr
, hdc
, &rect
, index
, action
, TRUE
);
691 if (oldFont
) SelectObject( hdc
, oldFont
);
692 if (oldBrush
) SelectObject( hdc
, oldBrush
);
693 ReleaseDC( descr
->self
, hdc
);
697 /***********************************************************************
698 * LISTBOX_DrawFocusRect
700 static void LISTBOX_DrawFocusRect( LB_DESCR
*descr
, BOOL on
)
706 /* Do not repaint the item if the item is not visible */
707 if (!IsWindowVisible(descr
->self
)) return;
709 if (descr
->focus_item
== -1) return;
710 if (!descr
->caret_on
|| !descr
->in_focus
) return;
712 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) != 1) return;
713 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
714 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
715 if (!IsWindowEnabled(descr
->self
))
716 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
717 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
718 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, on
? FALSE
: TRUE
);
719 if (oldFont
) SelectObject( hdc
, oldFont
);
720 ReleaseDC( descr
->self
, hdc
);
724 /***********************************************************************
725 * LISTBOX_InitStorage
727 static LRESULT
LISTBOX_InitStorage( LB_DESCR
*descr
, INT nb_items
)
731 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
732 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
734 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
735 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
736 nb_items
* sizeof(LB_ITEMDATA
));
739 item
= HeapAlloc( GetProcessHeap(), 0,
740 nb_items
* sizeof(LB_ITEMDATA
));
745 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
753 /***********************************************************************
754 * LISTBOX_SetTabStops
756 static BOOL
LISTBOX_SetTabStops( LB_DESCR
*descr
, INT count
, LPINT tabs
, BOOL short_ints
)
760 if (!(descr
->style
& LBS_USETABSTOPS
))
762 SetLastError(ERROR_LB_WITHOUT_TABSTOPS
);
766 HeapFree( GetProcessHeap(), 0, descr
->tabs
);
767 if (!(descr
->nb_tabs
= count
))
772 if (!(descr
->tabs
= HeapAlloc( GetProcessHeap(), 0,
773 descr
->nb_tabs
* sizeof(INT
) )))
778 LPINT16 p
= (LPINT16
)tabs
;
780 TRACE("[%p]: settabstops ", descr
->self
);
781 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
782 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
783 TRACE("%hd ", descr
->tabs
[i
]);
787 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
789 /* convert into "dialog units"*/
790 for (i
= 0; i
< descr
->nb_tabs
; i
++)
791 descr
->tabs
[i
] = MulDiv(descr
->tabs
[i
], descr
->avg_char_width
, 4);
797 /***********************************************************************
800 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPWSTR buffer
, BOOL unicode
)
802 if ((index
< 0) || (index
>= descr
->nb_items
))
804 SetLastError(ERROR_INVALID_INDEX
);
807 if (HAS_STRINGS(descr
))
811 DWORD len
= strlenW(descr
->items
[index
].str
);
814 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[index
].str
, len
,
815 NULL
, 0, NULL
, NULL
);
818 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
822 strcpyW( buffer
, descr
->items
[index
].str
);
823 return strlenW(buffer
);
827 return WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1, (LPSTR
)buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
831 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
832 return sizeof(DWORD
);
836 static inline INT
LISTBOX_lstrcmpiW( LCID lcid
, LPCWSTR str1
, LPCWSTR str2
)
838 INT ret
= CompareStringW( lcid
, NORM_IGNORECASE
, str1
, -1, str2
, -1 );
839 if (ret
== CSTR_LESS_THAN
)
841 if (ret
== CSTR_EQUAL
)
843 if (ret
== CSTR_GREATER_THAN
)
848 /***********************************************************************
849 * LISTBOX_FindStringPos
851 * Find the nearest string located before a given string in sort order.
852 * If 'exact' is TRUE, return an error if we don't get an exact match.
854 static INT
LISTBOX_FindStringPos( LB_DESCR
*descr
, LPCWSTR str
, BOOL exact
)
856 INT index
, min
, max
, res
= -1;
858 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
860 max
= descr
->nb_items
;
863 index
= (min
+ max
) / 2;
864 if (HAS_STRINGS(descr
))
865 res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, descr
->items
[index
].str
);
868 COMPAREITEMSTRUCT cis
;
869 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
871 cis
.CtlType
= ODT_LISTBOX
;
873 cis
.hwndItem
= descr
->self
;
874 /* note that some application (MetaStock) expects the second item
875 * to be in the listbox */
877 cis
.itemData1
= (ULONG_PTR
)str
;
879 cis
.itemData2
= descr
->items
[index
].data
;
880 cis
.dwLocaleId
= descr
->locale
;
881 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
883 if (!res
) return index
;
884 if (res
< 0) max
= index
;
885 else min
= index
+ 1;
887 return exact
? -1 : max
;
891 /***********************************************************************
892 * LISTBOX_FindFileStrPos
894 * Find the nearest string located before a given string in directory
895 * sort order (i.e. first files, then directories, then drives).
897 static INT
LISTBOX_FindFileStrPos( LB_DESCR
*descr
, LPCWSTR str
)
899 INT min
, max
, res
= -1;
901 if (!HAS_STRINGS(descr
))
902 return LISTBOX_FindStringPos( descr
, str
, FALSE
);
904 max
= descr
->nb_items
;
907 INT index
= (min
+ max
) / 2;
908 LPCWSTR p
= descr
->items
[index
].str
;
909 if (*p
== '[') /* drive or directory */
911 if (*str
!= '[') res
= -1;
912 else if (p
[1] == '-') /* drive */
914 if (str
[1] == '-') res
= str
[2] - p
[2];
919 if (str
[1] == '-') res
= 1;
920 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
925 if (*str
== '[') res
= 1;
926 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
928 if (!res
) return index
;
929 if (res
< 0) max
= index
;
930 else min
= index
+ 1;
936 /***********************************************************************
939 * Find the item beginning with a given string.
941 static INT
LISTBOX_FindString( LB_DESCR
*descr
, INT start
, LPCWSTR str
, BOOL exact
)
946 if (start
>= descr
->nb_items
) start
= -1;
947 item
= descr
->items
+ start
+ 1;
948 if (HAS_STRINGS(descr
))
950 if (!str
|| ! str
[0] ) return LB_ERR
;
953 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
954 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
955 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
956 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
960 /* Special case for drives and directories: ignore prefix */
961 #define CHECK_DRIVE(item) \
962 if ((item)->str[0] == '[') \
964 if (!strncmpiW( str, (item)->str+1, len )) return i; \
965 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
969 INT len
= strlenW(str
);
970 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
972 if (!strncmpiW( str
, item
->str
, len
)) return i
;
975 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
977 if (!strncmpiW( str
, item
->str
, len
)) return i
;
985 if (exact
&& (descr
->style
& LBS_SORT
))
986 /* If sorted, use a WM_COMPAREITEM binary search */
987 return LISTBOX_FindStringPos( descr
, str
, TRUE
);
989 /* Otherwise use a linear search */
990 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
991 if (item
->data
== (ULONG_PTR
)str
) return i
;
992 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
993 if (item
->data
== (ULONG_PTR
)str
) return i
;
999 /***********************************************************************
1000 * LISTBOX_GetSelCount
1002 static LRESULT
LISTBOX_GetSelCount( const LB_DESCR
*descr
)
1005 const LB_ITEMDATA
*item
= descr
->items
;
1007 if (!(descr
->style
& LBS_MULTIPLESEL
) ||
1008 (descr
->style
& LBS_NOSEL
))
1010 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
1011 if (item
->selected
) count
++;
1016 /***********************************************************************
1017 * LISTBOX_GetSelItems16
1019 static LRESULT
LISTBOX_GetSelItems16( const LB_DESCR
*descr
, INT16 max
, LPINT16 array
)
1022 const LB_ITEMDATA
*item
= descr
->items
;
1024 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1025 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
1026 if (item
->selected
) array
[count
++] = (INT16
)i
;
1031 /***********************************************************************
1032 * LISTBOX_GetSelItems
1034 static LRESULT
LISTBOX_GetSelItems( const LB_DESCR
*descr
, INT max
, LPINT array
)
1037 const LB_ITEMDATA
*item
= descr
->items
;
1039 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1040 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
1041 if (item
->selected
) array
[count
++] = i
;
1046 /***********************************************************************
1049 static LRESULT
LISTBOX_Paint( LB_DESCR
*descr
, HDC hdc
)
1051 INT i
, col_pos
= descr
->page_size
- 1;
1053 RECT focusRect
= {-1, -1, -1, -1};
1055 HBRUSH hbrush
, oldBrush
= 0;
1057 if (descr
->style
& LBS_NOREDRAW
) return 0;
1059 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1060 if (descr
->style
& LBS_MULTICOLUMN
)
1061 rect
.right
= rect
.left
+ descr
->column_width
;
1062 else if (descr
->horz_pos
)
1064 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1065 rect
.right
+= descr
->horz_pos
;
1068 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1069 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
1070 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
1071 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1072 if (!IsWindowEnabled(descr
->self
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1074 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1077 /* Special case for empty listbox: paint focus rect */
1078 rect
.bottom
= rect
.top
+ descr
->item_height
;
1079 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1080 &rect
, NULL
, 0, NULL
);
1081 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1082 rect
.top
= rect
.bottom
;
1085 /* Paint all the item, regarding the selection
1086 Focus state will be painted after */
1088 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1090 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1091 rect
.bottom
= rect
.top
+ descr
->item_height
;
1093 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1095 if (i
== descr
->focus_item
)
1097 /* keep the focus rect, to paint the focus item after */
1098 focusRect
.left
= rect
.left
;
1099 focusRect
.right
= rect
.right
;
1100 focusRect
.top
= rect
.top
;
1101 focusRect
.bottom
= rect
.bottom
;
1103 LISTBOX_PaintItem( descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1104 rect
.top
= rect
.bottom
;
1106 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1108 if (!IS_OWNERDRAW(descr
))
1110 /* Clear the bottom of the column */
1111 if (rect
.top
< descr
->height
)
1113 rect
.bottom
= descr
->height
;
1114 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1115 &rect
, NULL
, 0, NULL
);
1119 /* Go to the next column */
1120 rect
.left
+= descr
->column_width
;
1121 rect
.right
+= descr
->column_width
;
1123 col_pos
= descr
->page_size
- 1;
1128 if (rect
.top
>= descr
->height
) break;
1132 /* Paint the focus item now */
1133 if (focusRect
.top
!= focusRect
.bottom
&&
1134 descr
->caret_on
&& descr
->in_focus
)
1135 LISTBOX_PaintItem( descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1137 if (!IS_OWNERDRAW(descr
))
1139 /* Clear the remainder of the client area */
1140 if (rect
.top
< descr
->height
)
1142 rect
.bottom
= descr
->height
;
1143 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1144 &rect
, NULL
, 0, NULL
);
1146 if (rect
.right
< descr
->width
)
1148 rect
.left
= rect
.right
;
1149 rect
.right
= descr
->width
;
1151 rect
.bottom
= descr
->height
;
1152 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1153 &rect
, NULL
, 0, NULL
);
1156 if (oldFont
) SelectObject( hdc
, oldFont
);
1157 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1162 /***********************************************************************
1163 * LISTBOX_InvalidateItems
1165 * Invalidate all items from a given item. If the specified item is not
1166 * visible, nothing happens.
1168 static void LISTBOX_InvalidateItems( LB_DESCR
*descr
, INT index
)
1172 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1174 if (descr
->style
& LBS_NOREDRAW
)
1176 descr
->style
|= LBS_DISPLAYCHANGED
;
1179 rect
.bottom
= descr
->height
;
1180 InvalidateRect( descr
->self
, &rect
, TRUE
);
1181 if (descr
->style
& LBS_MULTICOLUMN
)
1183 /* Repaint the other columns */
1184 rect
.left
= rect
.right
;
1185 rect
.right
= descr
->width
;
1187 InvalidateRect( descr
->self
, &rect
, TRUE
);
1192 static void LISTBOX_InvalidateItemRect( LB_DESCR
*descr
, INT index
)
1196 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1197 InvalidateRect( descr
->self
, &rect
, TRUE
);
1200 /***********************************************************************
1201 * LISTBOX_GetItemHeight
1203 static LRESULT
LISTBOX_GetItemHeight( const LB_DESCR
*descr
, INT index
)
1205 if (descr
->style
& LBS_OWNERDRAWVARIABLE
&& descr
->nb_items
> 0)
1207 if ((index
< 0) || (index
>= descr
->nb_items
))
1209 SetLastError(ERROR_INVALID_INDEX
);
1212 return descr
->items
[index
].height
;
1214 else return descr
->item_height
;
1218 /***********************************************************************
1219 * LISTBOX_SetItemHeight
1221 static LRESULT
LISTBOX_SetItemHeight( LB_DESCR
*descr
, INT index
, INT height
, BOOL repaint
)
1223 if (height
> MAXBYTE
)
1226 if (!height
) height
= 1;
1228 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1230 if ((index
< 0) || (index
>= descr
->nb_items
))
1232 SetLastError(ERROR_INVALID_INDEX
);
1235 TRACE("[%p]: item %d height = %d\n", descr
->self
, index
, height
);
1236 descr
->items
[index
].height
= height
;
1237 LISTBOX_UpdateScroll( descr
);
1239 LISTBOX_InvalidateItems( descr
, index
);
1241 else if (height
!= descr
->item_height
)
1243 TRACE("[%p]: new height = %d\n", descr
->self
, height
);
1244 descr
->item_height
= height
;
1245 LISTBOX_UpdatePage( descr
);
1246 LISTBOX_UpdateScroll( descr
);
1248 InvalidateRect( descr
->self
, 0, TRUE
);
1254 /***********************************************************************
1255 * LISTBOX_SetHorizontalPos
1257 static void LISTBOX_SetHorizontalPos( LB_DESCR
*descr
, INT pos
)
1261 if (pos
> descr
->horz_extent
- descr
->width
)
1262 pos
= descr
->horz_extent
- descr
->width
;
1263 if (pos
< 0) pos
= 0;
1264 if (!(diff
= descr
->horz_pos
- pos
)) return;
1265 TRACE("[%p]: new horz pos = %d\n", descr
->self
, pos
);
1266 descr
->horz_pos
= pos
;
1267 LISTBOX_UpdateScroll( descr
);
1268 if (abs(diff
) < descr
->width
)
1271 /* Invalidate the focused item so it will be repainted correctly */
1272 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1273 InvalidateRect( descr
->self
, &rect
, TRUE
);
1274 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
1275 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1278 InvalidateRect( descr
->self
, NULL
, TRUE
);
1282 /***********************************************************************
1283 * LISTBOX_SetHorizontalExtent
1285 static LRESULT
LISTBOX_SetHorizontalExtent( LB_DESCR
*descr
, INT extent
)
1287 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1289 if (extent
<= 0) extent
= 1;
1290 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1291 TRACE("[%p]: new horz extent = %d\n", descr
->self
, extent
);
1292 descr
->horz_extent
= extent
;
1293 if (descr
->horz_pos
> extent
- descr
->width
)
1294 LISTBOX_SetHorizontalPos( descr
, extent
- descr
->width
);
1296 LISTBOX_UpdateScroll( descr
);
1301 /***********************************************************************
1302 * LISTBOX_SetColumnWidth
1304 static LRESULT
LISTBOX_SetColumnWidth( LB_DESCR
*descr
, INT width
)
1306 if (width
== descr
->column_width
) return LB_OKAY
;
1307 TRACE("[%p]: new column width = %d\n", descr
->self
, width
);
1308 descr
->column_width
= width
;
1309 LISTBOX_UpdatePage( descr
);
1314 /***********************************************************************
1317 * Returns the item height.
1319 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1323 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1328 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1330 ERR("unable to get DC.\n" );
1333 if (font
) oldFont
= SelectObject( hdc
, font
);
1334 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1335 if (oldFont
) SelectObject( hdc
, oldFont
);
1336 ReleaseDC( descr
->self
, hdc
);
1338 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1339 if (!IS_OWNERDRAW(descr
))
1340 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1345 /***********************************************************************
1346 * LISTBOX_MakeItemVisible
1348 * Make sure that a given item is partially or fully visible.
1350 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1354 TRACE("current top item %d, index %d, fully %d\n", descr
->top_item
, index
, fully
);
1356 if (index
<= descr
->top_item
) top
= index
;
1357 else if (descr
->style
& LBS_MULTICOLUMN
)
1359 INT cols
= descr
->width
;
1360 if (!fully
) cols
+= descr
->column_width
- 1;
1361 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1363 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1364 top
= index
- descr
->page_size
* (cols
- 1);
1366 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1368 INT height
= fully
? descr
->items
[index
].height
: 1;
1369 for (top
= index
; top
> descr
->top_item
; top
--)
1370 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1374 if (index
< descr
->top_item
+ descr
->page_size
) return;
1375 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1376 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1377 top
= index
- descr
->page_size
+ 1;
1379 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1382 /***********************************************************************
1383 * LISTBOX_SetCaretIndex
1386 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1389 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1391 INT oldfocus
= descr
->focus_item
;
1393 TRACE("old focus %d, index %d\n", oldfocus
, index
);
1395 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1396 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1397 if (index
== oldfocus
) return LB_OKAY
;
1399 LISTBOX_DrawFocusRect( descr
, FALSE
);
1400 descr
->focus_item
= index
;
1402 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1403 LISTBOX_DrawFocusRect( descr
, TRUE
);
1409 /***********************************************************************
1410 * LISTBOX_SelectItemRange
1412 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1414 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1419 /* A few sanity checks */
1421 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1422 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1424 if (!descr
->nb_items
) return LB_OKAY
;
1426 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1427 if (first
< 0) first
= 0;
1428 if (last
< first
) return LB_OKAY
;
1430 if (on
) /* Turn selection on */
1432 for (i
= first
; i
<= last
; i
++)
1434 if (descr
->items
[i
].selected
) continue;
1435 descr
->items
[i
].selected
= TRUE
;
1436 LISTBOX_InvalidateItemRect(descr
, i
);
1439 else /* Turn selection off */
1441 for (i
= first
; i
<= last
; i
++)
1443 if (!descr
->items
[i
].selected
) continue;
1444 descr
->items
[i
].selected
= FALSE
;
1445 LISTBOX_InvalidateItemRect(descr
, i
);
1451 /***********************************************************************
1452 * LISTBOX_SetSelection
1454 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1455 BOOL on
, BOOL send_notify
)
1457 TRACE( "cur_sel=%d index=%d notify=%s\n",
1458 descr
->selected_item
, index
, send_notify
? "YES" : "NO" );
1460 if (descr
->style
& LBS_NOSEL
)
1462 descr
->selected_item
= index
;
1465 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1466 if (descr
->style
& LBS_MULTIPLESEL
)
1468 if (index
== -1) /* Select all items */
1469 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1470 else /* Only one item */
1471 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1475 INT oldsel
= descr
->selected_item
;
1476 if (index
== oldsel
) return LB_OKAY
;
1477 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1478 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1479 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1480 descr
->selected_item
= index
;
1481 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1482 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1483 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1485 if( descr
->lphc
) /* set selection change flag for parent combo */
1486 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1492 /***********************************************************************
1495 * Change the caret position and extend the selection to the new caret.
1497 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1499 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1501 if ((index
< 0) || (index
>= descr
->nb_items
))
1504 /* Important, repaint needs to be done in this order if
1505 you want to mimic Windows behavior:
1506 1. Remove the focus and paint the item
1507 2. Remove the selection and paint the item(s)
1508 3. Set the selection and repaint the item(s)
1509 4. Set the focus to 'index' and repaint the item */
1511 /* 1. remove the focus and repaint the item */
1512 LISTBOX_DrawFocusRect( descr
, FALSE
);
1514 /* 2. then turn off the previous selection */
1515 /* 3. repaint the new selected item */
1516 if (descr
->style
& LBS_EXTENDEDSEL
)
1518 if (descr
->anchor_item
!= -1)
1520 INT first
= min( index
, descr
->anchor_item
);
1521 INT last
= max( index
, descr
->anchor_item
);
1523 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1524 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1525 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1528 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1530 /* Set selection to new caret item */
1531 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1534 /* 4. repaint the new item with the focus */
1535 descr
->focus_item
= index
;
1536 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1537 LISTBOX_DrawFocusRect( descr
, TRUE
);
1541 /***********************************************************************
1542 * LISTBOX_InsertItem
1544 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1545 LPWSTR str
, ULONG_PTR data
)
1549 INT oldfocus
= descr
->focus_item
;
1551 if (index
== -1) index
= descr
->nb_items
;
1552 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1553 if (!descr
->items
) max_items
= 0;
1554 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1555 if (descr
->nb_items
== max_items
)
1557 /* We need to grow the array */
1558 max_items
+= LB_ARRAY_GRANULARITY
;
1560 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1561 max_items
* sizeof(LB_ITEMDATA
) );
1563 item
= HeapAlloc( GetProcessHeap(), 0,
1564 max_items
* sizeof(LB_ITEMDATA
) );
1567 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1570 descr
->items
= item
;
1573 /* Insert the item structure */
1575 item
= &descr
->items
[index
];
1576 if (index
< descr
->nb_items
)
1577 RtlMoveMemory( item
+ 1, item
,
1578 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1582 item
->selected
= FALSE
;
1585 /* Get item height */
1587 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1589 MEASUREITEMSTRUCT mis
;
1590 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1592 mis
.CtlType
= ODT_LISTBOX
;
1595 mis
.itemData
= descr
->items
[index
].data
;
1596 mis
.itemHeight
= descr
->item_height
;
1597 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1598 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1599 TRACE("[%p]: measure item %d (%s) = %d\n",
1600 descr
->self
, index
, str
? debugstr_w(str
) : "", item
->height
);
1603 /* Repaint the items */
1605 LISTBOX_UpdateScroll( descr
);
1606 LISTBOX_InvalidateItems( descr
, index
);
1608 /* Move selection and focused item */
1609 /* If listbox was empty, set focus to the first item */
1610 if (descr
->nb_items
== 1)
1611 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1612 /* single select don't change selection index in win31 */
1613 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1615 descr
->selected_item
++;
1616 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1620 if (index
<= descr
->selected_item
)
1622 descr
->selected_item
++;
1623 descr
->focus_item
= oldfocus
; /* focus not changed */
1630 /***********************************************************************
1631 * LISTBOX_InsertString
1633 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
, LPCWSTR str
)
1635 LPWSTR new_str
= NULL
;
1639 if (HAS_STRINGS(descr
))
1641 static const WCHAR empty_stringW
[] = { 0 };
1642 if (!str
) str
= empty_stringW
;
1643 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1645 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1648 strcpyW(new_str
, str
);
1650 else data
= (ULONG_PTR
)str
;
1652 if (index
== -1) index
= descr
->nb_items
;
1653 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, data
)) != 0)
1655 HeapFree( GetProcessHeap(), 0, new_str
);
1659 TRACE("[%p]: added item %d %s\n",
1660 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1665 /***********************************************************************
1666 * LISTBOX_DeleteItem
1668 * Delete the content of an item. 'index' must be a valid index.
1670 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1672 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1673 * while Win95 sends it for all items with user data.
1674 * It's probably better to send it too often than not
1675 * often enough, so this is what we do here.
1677 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1679 DELETEITEMSTRUCT dis
;
1680 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1682 dis
.CtlType
= ODT_LISTBOX
;
1685 dis
.hwndItem
= descr
->self
;
1686 dis
.itemData
= descr
->items
[index
].data
;
1687 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1689 if (HAS_STRINGS(descr
))
1690 HeapFree( GetProcessHeap(), 0, descr
->items
[index
].str
);
1694 /***********************************************************************
1695 * LISTBOX_RemoveItem
1697 * Remove an item from the listbox and delete its content.
1699 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1704 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1706 /* We need to invalidate the original rect instead of the updated one. */
1707 LISTBOX_InvalidateItems( descr
, index
);
1709 LISTBOX_DeleteItem( descr
, index
);
1711 /* Remove the item */
1713 item
= &descr
->items
[index
];
1714 if (index
< descr
->nb_items
-1)
1715 RtlMoveMemory( item
, item
+ 1,
1716 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1718 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1720 /* Shrink the item array if possible */
1722 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1723 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1725 max_items
-= LB_ARRAY_GRANULARITY
;
1726 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1727 max_items
* sizeof(LB_ITEMDATA
) );
1728 if (item
) descr
->items
= item
;
1730 /* Repaint the items */
1732 LISTBOX_UpdateScroll( descr
);
1733 /* if we removed the scrollbar, reset the top of the list
1734 (correct for owner-drawn ???) */
1735 if (descr
->nb_items
== descr
->page_size
)
1736 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1738 /* Move selection and focused item */
1739 if (!IS_MULTISELECT(descr
))
1741 if (index
== descr
->selected_item
)
1742 descr
->selected_item
= -1;
1743 else if (index
< descr
->selected_item
)
1745 descr
->selected_item
--;
1746 if (ISWIN31
) /* win 31 do not change the selected item number */
1747 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1751 if (descr
->focus_item
>= descr
->nb_items
)
1753 descr
->focus_item
= descr
->nb_items
- 1;
1754 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1760 /***********************************************************************
1761 * LISTBOX_ResetContent
1763 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1767 for(i
= descr
->nb_items
- 1; i
>=0; i
--) LISTBOX_DeleteItem( descr
, i
);
1768 HeapFree( GetProcessHeap(), 0, descr
->items
);
1769 descr
->nb_items
= 0;
1770 descr
->top_item
= 0;
1771 descr
->selected_item
= -1;
1772 descr
->focus_item
= 0;
1773 descr
->anchor_item
= -1;
1774 descr
->items
= NULL
;
1778 /***********************************************************************
1781 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, INT count
)
1785 if (HAS_STRINGS(descr
))
1787 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1791 /* FIXME: this is far from optimal... */
1792 if (count
> descr
->nb_items
)
1794 while (count
> descr
->nb_items
)
1795 if ((ret
= LISTBOX_InsertString( descr
, -1, 0 )) < 0)
1798 else if (count
< descr
->nb_items
)
1800 while (count
< descr
->nb_items
)
1801 if ((ret
= LISTBOX_RemoveItem( descr
, (descr
->nb_items
- 1) )) < 0)
1808 /***********************************************************************
1811 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1812 LPCWSTR filespec
, BOOL long_names
)
1815 LRESULT ret
= LB_OKAY
;
1816 WIN32_FIND_DATAW entry
;
1818 LRESULT maxinsert
= LB_ERR
;
1820 /* don't scan directory if we just want drives exclusively */
1821 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1822 /* scan directory */
1823 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1825 int le
= GetLastError();
1826 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1833 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1835 static const WCHAR bracketW
[] = { ']',0 };
1836 static const WCHAR dotW
[] = { '.',0 };
1837 if (!(attrib
& DDL_DIRECTORY
) ||
1838 !strcmpW( entry
.cFileName
, dotW
)) continue;
1840 if (!long_names
&& entry
.cAlternateFileName
[0])
1841 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1843 strcpyW( buffer
+ 1, entry
.cFileName
);
1844 strcatW(buffer
, bracketW
);
1846 else /* not a directory */
1848 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1849 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1851 if ((attrib
& DDL_EXCLUSIVE
) &&
1852 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1855 if (!long_names
&& entry
.cAlternateFileName
[0])
1856 strcpyW( buffer
, entry
.cAlternateFileName
);
1858 strcpyW( buffer
, entry
.cFileName
);
1860 if (!long_names
) CharLowerW( buffer
);
1861 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1862 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1864 if (ret
<= maxinsert
) maxinsert
++; else maxinsert
= ret
;
1865 } while (FindNextFileW( handle
, &entry
));
1866 FindClose( handle
);
1874 if (attrib
& DDL_DRIVES
)
1876 WCHAR buffer
[] = {'[','-','a','-',']',0};
1877 WCHAR root
[] = {'A',':','\\',0};
1879 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1881 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1882 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1891 /***********************************************************************
1892 * LISTBOX_HandleVScroll
1894 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1898 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1902 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1905 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1908 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1909 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1912 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1913 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1915 case SB_THUMBPOSITION
:
1916 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1919 info
.cbSize
= sizeof(info
);
1920 info
.fMask
= SIF_TRACKPOS
;
1921 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1922 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1925 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1928 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1935 /***********************************************************************
1936 * LISTBOX_HandleHScroll
1938 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1943 if (descr
->style
& LBS_MULTICOLUMN
)
1948 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
1952 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
1956 page
= descr
->width
/ descr
->column_width
;
1957 if (page
< 1) page
= 1;
1958 LISTBOX_SetTopItem( descr
,
1959 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1962 page
= descr
->width
/ descr
->column_width
;
1963 if (page
< 1) page
= 1;
1964 LISTBOX_SetTopItem( descr
,
1965 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1967 case SB_THUMBPOSITION
:
1968 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
1971 info
.cbSize
= sizeof(info
);
1972 info
.fMask
= SIF_TRACKPOS
;
1973 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1974 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
1978 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1981 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1985 else if (descr
->horz_extent
)
1990 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
1993 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
1996 LISTBOX_SetHorizontalPos( descr
,
1997 descr
->horz_pos
- descr
->width
);
2000 LISTBOX_SetHorizontalPos( descr
,
2001 descr
->horz_pos
+ descr
->width
);
2003 case SB_THUMBPOSITION
:
2004 LISTBOX_SetHorizontalPos( descr
, pos
);
2007 info
.cbSize
= sizeof(info
);
2008 info
.fMask
= SIF_TRACKPOS
;
2009 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
2010 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
2013 LISTBOX_SetHorizontalPos( descr
, 0 );
2016 LISTBOX_SetHorizontalPos( descr
,
2017 descr
->horz_extent
- descr
->width
);
2024 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
2026 short gcWheelDelta
= 0;
2027 UINT pulScrollLines
= 3;
2029 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
2031 gcWheelDelta
-= delta
;
2033 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
2035 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
2036 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
2037 LISTBOX_SetTopItem( descr
, descr
->top_item
+ cLineScroll
, TRUE
);
2042 /***********************************************************************
2043 * LISTBOX_HandleLButtonDown
2045 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
2047 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2049 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2050 descr
->self
, x
, y
, index
, descr
->focus_item
);
2052 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2054 if (!descr
->in_focus
)
2056 if( !descr
->lphc
) SetFocus( descr
->self
);
2057 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2060 if (index
== -1) return 0;
2064 if (descr
->style
& LBS_NOTIFY
)
2065 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2066 MAKELPARAM( x
, y
) );
2069 descr
->captured
= TRUE
;
2070 SetCapture( descr
->self
);
2072 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2074 /* we should perhaps make sure that all items are deselected
2075 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2076 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2077 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2080 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2081 if (keys
& MK_CONTROL
)
2083 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2084 LISTBOX_SetSelection( descr
, index
,
2085 !descr
->items
[index
].selected
,
2086 (descr
->style
& LBS_NOTIFY
) != 0);
2090 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2092 if (descr
->style
& LBS_EXTENDEDSEL
)
2094 LISTBOX_SetSelection( descr
, index
,
2095 descr
->items
[index
].selected
,
2096 (descr
->style
& LBS_NOTIFY
) != 0 );
2100 LISTBOX_SetSelection( descr
, index
,
2101 !descr
->items
[index
].selected
,
2102 (descr
->style
& LBS_NOTIFY
) != 0 );
2108 descr
->anchor_item
= index
;
2109 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2110 LISTBOX_SetSelection( descr
, index
,
2111 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2116 if (GetWindowLongW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2123 if (DragDetect( descr
->self
, pt
))
2124 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2131 /*************************************************************************
2132 * LISTBOX_HandleLButtonDownCombo [Internal]
2134 * Process LButtonDown message for the ComboListBox
2137 * pWnd [I] The windows internal structure
2138 * pDescr [I] The ListBox internal structure
2139 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2140 * x [I] X Mouse Coordinate
2141 * y [I] Y Mouse Coordinate
2144 * 0 since we are processing the WM_LBUTTONDOWN Message
2147 * This function is only to be used when a ListBox is a ComboListBox
2150 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2152 RECT clientRect
, screenRect
;
2158 GetClientRect(descr
->self
, &clientRect
);
2160 if(PtInRect(&clientRect
, mousePos
))
2162 /* MousePos is in client, resume normal processing */
2163 if (msg
== WM_LBUTTONDOWN
)
2165 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2166 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2168 else if (descr
->style
& LBS_NOTIFY
)
2169 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2173 POINT screenMousePos
;
2174 HWND hWndOldCapture
;
2176 /* Check the Non-Client Area */
2177 screenMousePos
= mousePos
;
2178 hWndOldCapture
= GetCapture();
2180 GetWindowRect(descr
->self
, &screenRect
);
2181 ClientToScreen(descr
->self
, &screenMousePos
);
2183 if(!PtInRect(&screenRect
, screenMousePos
))
2185 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2186 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2187 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2191 /* Check to see the NC is a scrollbar */
2193 LONG style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2194 /* Check Vertical scroll bar */
2195 if (style
& WS_VSCROLL
)
2197 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2198 if (PtInRect( &clientRect
, mousePos
))
2199 nHitTestType
= HTVSCROLL
;
2201 /* Check horizontal scroll bar */
2202 if (style
& WS_HSCROLL
)
2204 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2205 if (PtInRect( &clientRect
, mousePos
))
2206 nHitTestType
= HTHSCROLL
;
2208 /* Windows sends this message when a scrollbar is clicked
2211 if(nHitTestType
!= 0)
2213 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2214 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2216 /* Resume the Capture after scrolling is complete
2218 if(hWndOldCapture
!= 0)
2219 SetCapture(hWndOldCapture
);
2225 /***********************************************************************
2226 * LISTBOX_HandleLButtonUp
2228 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2230 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2231 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2232 LISTBOX_Timer
= LB_TIMER_NONE
;
2233 if (descr
->captured
)
2235 descr
->captured
= FALSE
;
2236 if (GetCapture() == descr
->self
) ReleaseCapture();
2237 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2238 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2244 /***********************************************************************
2245 * LISTBOX_HandleTimer
2247 * Handle scrolling upon a timer event.
2248 * Return TRUE if scrolling should continue.
2250 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2255 if (descr
->top_item
) index
= descr
->top_item
- 1;
2259 if (descr
->top_item
) index
-= descr
->page_size
;
2262 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2263 if (index
== descr
->focus_item
) index
++;
2264 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2266 case LB_TIMER_RIGHT
:
2267 if (index
+ descr
->page_size
< descr
->nb_items
)
2268 index
+= descr
->page_size
;
2273 if (index
== descr
->focus_item
) return FALSE
;
2274 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2279 /***********************************************************************
2280 * LISTBOX_HandleSystemTimer
2282 * WM_SYSTIMER handler.
2284 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2286 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2288 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2289 LISTBOX_Timer
= LB_TIMER_NONE
;
2295 /***********************************************************************
2296 * LISTBOX_HandleMouseMove
2298 * WM_MOUSEMOVE handler.
2300 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2304 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2306 if (!descr
->captured
) return;
2308 if (descr
->style
& LBS_MULTICOLUMN
)
2311 else if (y
>= descr
->item_height
* descr
->page_size
)
2312 y
= descr
->item_height
* descr
->page_size
- 1;
2316 dir
= LB_TIMER_LEFT
;
2319 else if (x
>= descr
->width
)
2321 dir
= LB_TIMER_RIGHT
;
2322 x
= descr
->width
- 1;
2327 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2328 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2331 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2332 if (index
== -1) index
= descr
->focus_item
;
2333 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2335 /* Start/stop the system timer */
2337 if (dir
!= LB_TIMER_NONE
)
2338 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2339 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2340 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2341 LISTBOX_Timer
= dir
;
2345 /***********************************************************************
2346 * LISTBOX_HandleKeyDown
2348 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2351 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2352 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2353 bForceSelection
= FALSE
; /* only for single select list */
2355 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2357 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2358 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2359 (LPARAM
)descr
->self
);
2360 if (caret
== -2) return 0;
2362 if (caret
== -1) switch(key
)
2365 if (descr
->style
& LBS_MULTICOLUMN
)
2367 bForceSelection
= FALSE
;
2368 if (descr
->focus_item
>= descr
->page_size
)
2369 caret
= descr
->focus_item
- descr
->page_size
;
2374 caret
= descr
->focus_item
- 1;
2375 if (caret
< 0) caret
= 0;
2378 if (descr
->style
& LBS_MULTICOLUMN
)
2380 bForceSelection
= FALSE
;
2381 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2382 caret
= descr
->focus_item
+ descr
->page_size
;
2387 caret
= descr
->focus_item
+ 1;
2388 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2392 if (descr
->style
& LBS_MULTICOLUMN
)
2394 INT page
= descr
->width
/ descr
->column_width
;
2395 if (page
< 1) page
= 1;
2396 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2398 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2399 if (caret
< 0) caret
= 0;
2402 if (descr
->style
& LBS_MULTICOLUMN
)
2404 INT page
= descr
->width
/ descr
->column_width
;
2405 if (page
< 1) page
= 1;
2406 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2408 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2409 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2415 caret
= descr
->nb_items
- 1;
2418 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2419 else if (descr
->style
& LBS_MULTIPLESEL
)
2421 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2422 !descr
->items
[descr
->focus_item
].selected
,
2423 (descr
->style
& LBS_NOTIFY
) != 0 );
2427 bForceSelection
= FALSE
;
2429 if (bForceSelection
) /* focused item is used instead of key */
2430 caret
= descr
->focus_item
;
2433 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2434 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2435 !IS_MULTISELECT(descr
))
2436 descr
->anchor_item
= caret
;
2437 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2439 if (descr
->style
& LBS_MULTIPLESEL
)
2440 descr
->selected_item
= caret
;
2442 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2443 if (descr
->style
& LBS_NOTIFY
)
2447 /* make sure that combo parent doesn't hide us */
2448 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2450 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2457 /***********************************************************************
2458 * LISTBOX_HandleChar
2460 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2468 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2470 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2471 MAKEWPARAM(charW
, descr
->focus_item
),
2472 (LPARAM
)descr
->self
);
2473 if (caret
== -2) return 0;
2476 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2479 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2480 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2481 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2482 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2483 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2489 /***********************************************************************
2492 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2495 MEASUREITEMSTRUCT mis
;
2498 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2501 GetClientRect( hwnd
, &rect
);
2503 descr
->owner
= GetParent( descr
->self
);
2504 descr
->style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2505 descr
->width
= rect
.right
- rect
.left
;
2506 descr
->height
= rect
.bottom
- rect
.top
;
2507 descr
->items
= NULL
;
2508 descr
->nb_items
= 0;
2509 descr
->top_item
= 0;
2510 descr
->selected_item
= -1;
2511 descr
->focus_item
= 0;
2512 descr
->anchor_item
= -1;
2513 descr
->item_height
= 1;
2514 descr
->page_size
= 1;
2515 descr
->column_width
= 150;
2516 descr
->horz_extent
= (descr
->style
& WS_HSCROLL
) ? 1 : 0;
2517 descr
->horz_pos
= 0;
2520 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2521 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2522 descr
->in_focus
= FALSE
;
2523 descr
->captured
= FALSE
;
2525 descr
->locale
= GetUserDefaultLCID();
2528 if (is_old_app(descr
) && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2530 /* Win95 document "List Box Differences" from MSDN:
2531 If a list box in a version 3.x application has either the
2532 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2533 horizontal and vertical scroll bars.
2535 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2540 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2541 descr
->owner
= lphc
->self
;
2544 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2546 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2548 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2549 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2550 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2551 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2553 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2555 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2557 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2558 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2562 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2563 mis
.CtlType
= ODT_LISTBOX
;
2568 mis
.itemHeight
= descr
->item_height
;
2569 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2570 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2574 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2579 /***********************************************************************
2582 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2584 LISTBOX_ResetContent( descr
);
2585 SetWindowLongPtrW( descr
->self
, 0, 0 );
2586 HeapFree( GetProcessHeap(), 0, descr
);
2591 /***********************************************************************
2592 * ListBoxWndProc_common
2594 static LRESULT WINAPI
ListBoxWndProc_common( HWND hwnd
, UINT msg
,
2595 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2597 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2598 LPHEADCOMBO lphc
= 0;
2603 if (!IsWindow(hwnd
)) return 0;
2605 if (msg
== WM_CREATE
)
2607 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2608 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= (LPHEADCOMBO
)lpcs
->lpCreateParams
;
2609 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2610 TRACE("creating wnd=%p descr=%x\n", hwnd
, GetWindowLongPtrW( hwnd
, 0 ) );
2613 /* Ignore all other messages before we get a WM_CREATE */
2614 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2615 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2617 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2619 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2620 descr
->self
, SPY_GetMsgName(msg
, descr
->self
), wParam
, lParam
);
2624 case LB_RESETCONTENT16
:
2625 case LB_RESETCONTENT
:
2626 LISTBOX_ResetContent( descr
);
2627 LISTBOX_UpdateScroll( descr
);
2628 InvalidateRect( descr
->self
, NULL
, TRUE
);
2631 case LB_ADDSTRING16
:
2632 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2638 if(unicode
|| !HAS_STRINGS(descr
))
2639 textW
= (LPWSTR
)lParam
;
2642 LPSTR textA
= (LPSTR
)lParam
;
2643 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2644 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2645 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2647 wParam
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2648 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2649 if (!unicode
&& HAS_STRINGS(descr
))
2650 HeapFree(GetProcessHeap(), 0, textW
);
2654 case LB_INSERTSTRING16
:
2655 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2656 wParam
= (INT
)(INT16
)wParam
;
2658 case LB_INSERTSTRING
:
2662 if(unicode
|| !HAS_STRINGS(descr
))
2663 textW
= (LPWSTR
)lParam
;
2666 LPSTR textA
= (LPSTR
)lParam
;
2667 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2668 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2669 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2671 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2672 if(!unicode
&& HAS_STRINGS(descr
))
2673 HeapFree(GetProcessHeap(), 0, textW
);
2678 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2684 if(unicode
|| !HAS_STRINGS(descr
))
2685 textW
= (LPWSTR
)lParam
;
2688 LPSTR textA
= (LPSTR
)lParam
;
2689 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2690 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2691 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2693 wParam
= LISTBOX_FindFileStrPos( descr
, textW
);
2694 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2695 if(!unicode
&& HAS_STRINGS(descr
))
2696 HeapFree(GetProcessHeap(), 0, textW
);
2700 case LB_DELETESTRING16
:
2701 case LB_DELETESTRING
:
2702 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2703 return descr
->nb_items
;
2706 SetLastError(ERROR_INVALID_INDEX
);
2710 case LB_GETITEMDATA16
:
2711 case LB_GETITEMDATA
:
2712 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2714 SetLastError(ERROR_INVALID_INDEX
);
2717 return descr
->items
[wParam
].data
;
2719 case LB_SETITEMDATA16
:
2720 case LB_SETITEMDATA
:
2721 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2723 SetLastError(ERROR_INVALID_INDEX
);
2726 descr
->items
[wParam
].data
= lParam
;
2727 /* undocumented: returns TRUE, not LB_OKAY (0) */
2732 return descr
->nb_items
;
2735 lParam
= (LPARAM
)MapSL(lParam
);
2738 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, unicode
);
2740 case LB_GETTEXTLEN16
:
2743 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2745 SetLastError(ERROR_INVALID_INDEX
);
2748 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2749 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2750 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2751 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2753 case LB_GETCURSEL16
:
2755 if (descr
->nb_items
== 0)
2757 if (!IS_MULTISELECT(descr
))
2758 return descr
->selected_item
;
2759 if (descr
->selected_item
!= -1)
2760 return descr
->selected_item
;
2761 return descr
->focus_item
;
2762 /* otherwise, if the user tries to move the selection with the */
2763 /* arrow keys, we will give the application something to choke on */
2764 case LB_GETTOPINDEX16
:
2765 case LB_GETTOPINDEX
:
2766 return descr
->top_item
;
2768 case LB_GETITEMHEIGHT16
:
2769 case LB_GETITEMHEIGHT
:
2770 return LISTBOX_GetItemHeight( descr
, wParam
);
2772 case LB_SETITEMHEIGHT16
:
2773 lParam
= LOWORD(lParam
);
2775 case LB_SETITEMHEIGHT
:
2776 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2778 case LB_ITEMFROMPOINT
:
2785 /* The hiword of the return value is not a client area
2786 hittest as suggested by MSDN, but rather a hittest on
2787 the returned listbox item. */
2789 if(descr
->nb_items
== 0)
2790 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2792 pt
.x
= (short)LOWORD(lParam
);
2793 pt
.y
= (short)HIWORD(lParam
);
2795 SetRect(&rect
, 0, 0, descr
->width
, descr
->height
);
2797 if(!PtInRect(&rect
, pt
))
2799 pt
.x
= min(pt
.x
, rect
.right
- 1);
2800 pt
.x
= max(pt
.x
, 0);
2801 pt
.y
= min(pt
.y
, rect
.bottom
- 1);
2802 pt
.y
= max(pt
.y
, 0);
2806 index
= LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
);
2810 index
= descr
->nb_items
- 1;
2813 return MAKELONG(index
, hit
? 0 : 1);
2816 case LB_SETCARETINDEX16
:
2817 case LB_SETCARETINDEX
:
2818 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2819 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2826 case LB_GETCARETINDEX16
:
2827 case LB_GETCARETINDEX
:
2828 return descr
->focus_item
;
2830 case LB_SETTOPINDEX16
:
2831 case LB_SETTOPINDEX
:
2832 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2834 case LB_SETCOLUMNWIDTH16
:
2835 case LB_SETCOLUMNWIDTH
:
2836 return LISTBOX_SetColumnWidth( descr
, wParam
);
2838 case LB_GETITEMRECT16
:
2841 RECT16
*r16
= MapSL(lParam
);
2842 ret
= LISTBOX_GetItemRect( descr
, (INT16
)wParam
, &rect
);
2843 r16
->left
= rect
.left
;
2844 r16
->top
= rect
.top
;
2845 r16
->right
= rect
.right
;
2846 r16
->bottom
= rect
.bottom
;
2850 case LB_GETITEMRECT
:
2851 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2853 case LB_FINDSTRING16
:
2854 wParam
= (INT
)(INT16
)wParam
;
2855 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2861 if(unicode
|| !HAS_STRINGS(descr
))
2862 textW
= (LPWSTR
)lParam
;
2865 LPSTR textA
= (LPSTR
)lParam
;
2866 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2867 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2868 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2870 ret
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2871 if(!unicode
&& HAS_STRINGS(descr
))
2872 HeapFree(GetProcessHeap(), 0, textW
);
2876 case LB_FINDSTRINGEXACT16
:
2877 wParam
= (INT
)(INT16
)wParam
;
2878 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2880 case LB_FINDSTRINGEXACT
:
2884 if(unicode
|| !HAS_STRINGS(descr
))
2885 textW
= (LPWSTR
)lParam
;
2888 LPSTR textA
= (LPSTR
)lParam
;
2889 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2890 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2891 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2893 ret
= LISTBOX_FindString( descr
, wParam
, textW
, TRUE
);
2894 if(!unicode
&& HAS_STRINGS(descr
))
2895 HeapFree(GetProcessHeap(), 0, textW
);
2899 case LB_SELECTSTRING16
:
2900 wParam
= (INT
)(INT16
)wParam
;
2901 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2903 case LB_SELECTSTRING
:
2908 if(HAS_STRINGS(descr
))
2909 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2910 debugstr_a((LPSTR
)lParam
));
2911 if(unicode
|| !HAS_STRINGS(descr
))
2912 textW
= (LPWSTR
)lParam
;
2915 LPSTR textA
= (LPSTR
)lParam
;
2916 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2917 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2918 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2920 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2921 if(!unicode
&& HAS_STRINGS(descr
))
2922 HeapFree(GetProcessHeap(), 0, textW
);
2923 if (index
!= LB_ERR
)
2925 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2926 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2932 wParam
= (INT
)(INT16
)wParam
;
2935 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2937 return descr
->items
[wParam
].selected
;
2940 lParam
= (INT
)(INT16
)lParam
;
2943 return LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2945 case LB_SETCURSEL16
:
2946 wParam
= (INT
)(INT16
)wParam
;
2949 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2950 LISTBOX_SetCaretIndex( descr
, wParam
, FALSE
);
2951 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2952 if (ret
!= LB_ERR
) ret
= descr
->selected_item
;
2955 case LB_GETSELCOUNT16
:
2956 case LB_GETSELCOUNT
:
2957 return LISTBOX_GetSelCount( descr
);
2959 case LB_GETSELITEMS16
:
2960 return LISTBOX_GetSelItems16( descr
, wParam
, (LPINT16
)MapSL(lParam
) );
2962 case LB_GETSELITEMS
:
2963 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2965 case LB_SELITEMRANGE16
:
2966 case LB_SELITEMRANGE
:
2967 if (LOWORD(lParam
) <= HIWORD(lParam
))
2968 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2969 HIWORD(lParam
), wParam
);
2971 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2972 LOWORD(lParam
), wParam
);
2974 case LB_SELITEMRANGEEX16
:
2975 case LB_SELITEMRANGEEX
:
2976 if ((INT
)lParam
>= (INT
)wParam
)
2977 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2979 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
2981 case LB_GETHORIZONTALEXTENT16
:
2982 case LB_GETHORIZONTALEXTENT
:
2983 return descr
->horz_extent
;
2985 case LB_SETHORIZONTALEXTENT16
:
2986 case LB_SETHORIZONTALEXTENT
:
2987 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
2989 case LB_GETANCHORINDEX16
:
2990 case LB_GETANCHORINDEX
:
2991 return descr
->anchor_item
;
2993 case LB_SETANCHORINDEX16
:
2994 wParam
= (INT
)(INT16
)wParam
;
2996 case LB_SETANCHORINDEX
:
2997 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2999 SetLastError(ERROR_INVALID_INDEX
);
3002 descr
->anchor_item
= (INT
)wParam
;
3006 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
3007 * be set automatically (this is different in Win32) */
3008 if (wParam
& DDL_DRIVES
) wParam
|= DDL_EXCLUSIVE
;
3009 lParam
= (LPARAM
)MapSL(lParam
);
3016 textW
= (LPWSTR
)lParam
;
3019 LPSTR textA
= (LPSTR
)lParam
;
3020 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
3021 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
3022 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
3024 ret
= LISTBOX_Directory( descr
, wParam
, textW
, msg
== LB_DIR
);
3026 HeapFree(GetProcessHeap(), 0, textW
);
3031 return descr
->locale
;
3036 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
3038 ret
= descr
->locale
;
3039 descr
->locale
= (LCID
)wParam
;
3043 case LB_INITSTORAGE
:
3044 return LISTBOX_InitStorage( descr
, wParam
);
3047 return LISTBOX_SetCount( descr
, (INT
)wParam
);
3049 case LB_SETTABSTOPS16
:
3050 return LISTBOX_SetTabStops( descr
, (INT
)(INT16
)wParam
, MapSL(lParam
), TRUE
);
3052 case LB_SETTABSTOPS
:
3053 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
, FALSE
);
3057 if (descr
->caret_on
)
3059 descr
->caret_on
= TRUE
;
3060 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3061 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3066 if (!descr
->caret_on
)
3068 descr
->caret_on
= FALSE
;
3069 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3070 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3073 case LB_GETLISTBOXINFO
:
3074 FIXME("LB_GETLISTBOXINFO: stub!\n");
3078 return LISTBOX_Destroy( descr
);
3081 InvalidateRect( descr
->self
, NULL
, TRUE
);
3085 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
3089 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
3091 case WM_PRINTCLIENT
:
3095 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
3096 ret
= LISTBOX_Paint( descr
, hdc
);
3097 if( !wParam
) EndPaint( descr
->self
, &ps
);
3101 LISTBOX_UpdateSize( descr
);
3104 return (LRESULT
)descr
->font
;
3106 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
3107 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
3110 descr
->in_focus
= TRUE
;
3111 descr
->caret_on
= TRUE
;
3112 if (descr
->focus_item
!= -1)
3113 LISTBOX_DrawFocusRect( descr
, TRUE
);
3114 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
3117 descr
->in_focus
= FALSE
;
3118 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
3119 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3120 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
3123 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3125 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3127 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
3128 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
3129 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
3130 case WM_LBUTTONDOWN
:
3132 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3133 (INT16
)LOWORD(lParam
),
3134 (INT16
)HIWORD(lParam
) );
3135 return LISTBOX_HandleLButtonDown( descr
, wParam
,
3136 (INT16
)LOWORD(lParam
),
3137 (INT16
)HIWORD(lParam
) );
3138 case WM_LBUTTONDBLCLK
:
3140 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3141 (INT16
)LOWORD(lParam
),
3142 (INT16
)HIWORD(lParam
) );
3143 if (descr
->style
& LBS_NOTIFY
)
3144 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
3147 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
3149 BOOL captured
= descr
->captured
;
3153 mousePos
.x
= (INT16
)LOWORD(lParam
);
3154 mousePos
.y
= (INT16
)HIWORD(lParam
);
3157 * If we are in a dropdown combobox, we simulate that
3158 * the mouse is captured to show the tracking of the item.
3160 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
3161 descr
->captured
= TRUE
;
3163 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
3165 descr
->captured
= captured
;
3167 else if (GetCapture() == descr
->self
)
3169 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
3170 (INT16
)HIWORD(lParam
) );
3180 * If the mouse button "up" is not in the listbox,
3181 * we make sure there is no selection by re-selecting the
3182 * item that was selected when the listbox was made visible.
3184 mousePos
.x
= (INT16
)LOWORD(lParam
);
3185 mousePos
.y
= (INT16
)HIWORD(lParam
);
3187 GetClientRect(descr
->self
, &clientRect
);
3190 * When the user clicks outside the combobox and the focus
3191 * is lost, the owning combobox will send a fake buttonup with
3192 * 0xFFFFFFF as the mouse location, we must also revert the
3193 * selection to the original selection.
3195 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
3196 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
3198 return LISTBOX_HandleLButtonUp( descr
);
3200 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3202 /* for some reason Windows makes it possible to
3203 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3205 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3206 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3207 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3209 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3213 return LISTBOX_HandleKeyDown( descr
, wParam
);
3218 charW
= (WCHAR
)wParam
;
3221 CHAR charA
= (CHAR
)wParam
;
3222 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
3224 return LISTBOX_HandleChar( descr
, charW
);
3227 return LISTBOX_HandleSystemTimer( descr
);
3229 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3232 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3233 wParam
, (LPARAM
)descr
->self
);
3234 TRACE("hbrush = %p\n", hbrush
);
3236 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3239 GetClientRect(descr
->self
, &rect
);
3240 FillRect((HDC
)wParam
, &rect
, hbrush
);
3245 if( lphc
) return 0;
3246 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3247 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3250 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3259 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3260 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3261 hwnd
, msg
, wParam
, lParam
);
3264 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3265 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3268 /***********************************************************************
3271 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3273 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3276 /***********************************************************************
3279 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3281 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);