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( LB_DESCR
*descr
, INT index
, RECT
*rect
);
136 /*********************************************************************
137 * listbox class descriptor
139 const struct builtin_class_descr LISTBOX_builtin_class
=
141 "ListBox", /* name */
142 CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
143 ListBoxWndProcA
, /* procA */
144 ListBoxWndProcW
, /* procW */
145 sizeof(LB_DESCR
*), /* extra */
146 IDC_ARROW
, /* cursor */
151 /*********************************************************************
152 * combolbox class descriptor
154 const struct builtin_class_descr COMBOLBOX_builtin_class
=
156 "ComboLBox", /* name */
157 CS_DBLCLKS
| CS_SAVEBITS
, /* style */
158 ListBoxWndProcA
, /* procA */
159 ListBoxWndProcW
, /* procW */
160 sizeof(LB_DESCR
*), /* extra */
161 IDC_ARROW
, /* cursor */
166 /* check whether app is a Win 3.1 app */
167 static inline BOOL
is_old_app( LB_DESCR
*descr
)
169 return (GetExpWinVer16( GetWindowLongPtrW(descr
->self
, GWLP_HINSTANCE
) ) & 0xFF00 ) == 0x0300;
173 /***********************************************************************
174 * LISTBOX_GetCurrentPageSize
176 * Return the current page size
178 static INT
LISTBOX_GetCurrentPageSize( LB_DESCR
*descr
)
181 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
182 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
184 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
186 if (i
== descr
->top_item
) return 1;
187 else return i
- descr
->top_item
;
191 /***********************************************************************
192 * LISTBOX_GetMaxTopIndex
194 * Return the maximum possible index for the top of the listbox.
196 static INT
LISTBOX_GetMaxTopIndex( LB_DESCR
*descr
)
200 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
202 page
= descr
->height
;
203 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
204 if ((page
-= descr
->items
[max
].height
) < 0) break;
205 if (max
< descr
->nb_items
- 1) max
++;
207 else if (descr
->style
& LBS_MULTICOLUMN
)
209 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
210 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
211 max
= (max
- page
) * descr
->page_size
;
215 max
= descr
->nb_items
- descr
->page_size
;
217 if (max
< 0) max
= 0;
222 /***********************************************************************
223 * LISTBOX_UpdateScroll
225 * Update the scrollbars. Should be called whenever the content
226 * of the listbox changes.
228 static void LISTBOX_UpdateScroll( LB_DESCR
*descr
)
232 /* Check the listbox scroll bar flags individually before we call
233 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
234 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
235 scroll bar when we do not need one.
236 if (!(descr->style & WS_VSCROLL)) return;
239 /* It is important that we check descr->style, and not wnd->dwStyle,
240 for WS_VSCROLL, as the former is exactly the one passed in
241 argument to CreateWindow.
242 In Windows (and from now on in Wine :) a listbox created
243 with such a style (no WS_SCROLL) does not update
244 the scrollbar with listbox-related data, thus letting
245 the programmer use it for his/her own purposes. */
247 if (descr
->style
& LBS_NOREDRAW
) return;
248 info
.cbSize
= sizeof(info
);
250 if (descr
->style
& LBS_MULTICOLUMN
)
253 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
254 info
.nPos
= descr
->top_item
/ descr
->page_size
;
255 info
.nPage
= descr
->width
/ descr
->column_width
;
256 if (info
.nPage
< 1) info
.nPage
= 1;
257 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
258 if (descr
->style
& LBS_DISABLENOSCROLL
)
259 info
.fMask
|= SIF_DISABLENOSCROLL
;
260 if (descr
->style
& WS_HSCROLL
)
261 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
263 info
.fMask
= SIF_RANGE
;
264 if (descr
->style
& WS_VSCROLL
)
265 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
270 info
.nMax
= descr
->nb_items
- 1;
271 info
.nPos
= descr
->top_item
;
272 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
273 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
274 if (descr
->style
& LBS_DISABLENOSCROLL
)
275 info
.fMask
|= SIF_DISABLENOSCROLL
;
276 if (descr
->style
& WS_VSCROLL
)
277 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
279 if (descr
->horz_extent
)
282 info
.nMax
= descr
->horz_extent
- 1;
283 info
.nPos
= descr
->horz_pos
;
284 info
.nPage
= descr
->width
;
285 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
286 if (descr
->style
& LBS_DISABLENOSCROLL
)
287 info
.fMask
|= SIF_DISABLENOSCROLL
;
288 if (descr
->style
& WS_HSCROLL
)
289 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
295 /***********************************************************************
298 * Set the top item of the listbox, scrolling up or down if necessary.
300 static LRESULT
LISTBOX_SetTopItem( LB_DESCR
*descr
, INT index
, BOOL scroll
)
302 INT max
= LISTBOX_GetMaxTopIndex( descr
);
304 TRACE("setting top item %d, scroll %d\n", index
, scroll
);
306 if (index
> max
) index
= max
;
307 if (index
< 0) index
= 0;
308 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
309 if (descr
->top_item
== index
) return LB_OKAY
;
310 if (descr
->style
& LBS_MULTICOLUMN
)
312 INT diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
313 if (scroll
&& (abs(diff
) < descr
->width
))
314 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
315 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
323 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
327 if (index
> descr
->top_item
)
329 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
330 diff
-= descr
->items
[i
].height
;
334 for (i
= index
; i
< descr
->top_item
; i
++)
335 diff
+= descr
->items
[i
].height
;
339 diff
= (descr
->top_item
- index
) * descr
->item_height
;
341 if (abs(diff
) < descr
->height
)
342 ScrollWindowEx( descr
->self
, 0, diff
, NULL
, NULL
, 0, NULL
,
343 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
347 if (!scroll
) InvalidateRect( descr
->self
, NULL
, TRUE
);
348 descr
->top_item
= index
;
349 LISTBOX_UpdateScroll( descr
);
354 /***********************************************************************
357 * Update the page size. Should be called when the size of
358 * the client area or the item height changes.
360 static void LISTBOX_UpdatePage( LB_DESCR
*descr
)
364 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
366 if (page_size
== descr
->page_size
) return;
367 descr
->page_size
= page_size
;
368 if (descr
->style
& LBS_MULTICOLUMN
)
369 InvalidateRect( descr
->self
, NULL
, TRUE
);
370 LISTBOX_SetTopItem( descr
, descr
->top_item
, FALSE
);
374 /***********************************************************************
377 * Update the size of the listbox. Should be called when the size of
378 * the client area changes.
380 static void LISTBOX_UpdateSize( LB_DESCR
*descr
)
384 GetClientRect( descr
->self
, &rect
);
385 descr
->width
= rect
.right
- rect
.left
;
386 descr
->height
= rect
.bottom
- rect
.top
;
387 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
392 GetWindowRect( descr
->self
, &rect
);
393 if(descr
->item_height
!= 0)
394 remaining
= descr
->height
% descr
->item_height
;
397 if ((descr
->height
> descr
->item_height
) && remaining
)
399 if (is_old_app(descr
))
400 { /* give a margin for error to 16 bits programs - if we need
401 less than the height of the nonclient area, round to the
402 *next* number of items */
403 int ncheight
= rect
.bottom
- rect
.top
- descr
->height
;
404 if ((descr
->item_height
- remaining
) <= ncheight
)
405 remaining
= remaining
- descr
->item_height
;
407 TRACE("[%p]: changing height %d -> %d\n",
408 descr
->self
, descr
->height
, descr
->height
- remaining
);
409 SetWindowPos( descr
->self
, 0, 0, 0, rect
.right
- rect
.left
,
410 rect
.bottom
- rect
.top
- remaining
,
411 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
415 TRACE("[%p]: new size = %d,%d\n", descr
->self
, descr
->width
, descr
->height
);
416 LISTBOX_UpdatePage( descr
);
417 LISTBOX_UpdateScroll( descr
);
419 /* Invalidate the focused item so it will be repainted correctly */
420 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
422 InvalidateRect( descr
->self
, &rect
, FALSE
);
427 /***********************************************************************
428 * LISTBOX_GetItemRect
430 * Get the rectangle enclosing an item, in listbox client coordinates.
431 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
433 static LRESULT
LISTBOX_GetItemRect( LB_DESCR
*descr
, INT index
, RECT
*rect
)
435 /* Index <= 0 is legal even on empty listboxes */
436 if (index
&& (index
>= descr
->nb_items
))
438 memset(rect
, 0, sizeof(*rect
));
439 SetLastError(ERROR_INVALID_INDEX
);
442 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
443 if (descr
->style
& LBS_MULTICOLUMN
)
445 INT col
= (index
/ descr
->page_size
) -
446 (descr
->top_item
/ descr
->page_size
);
447 rect
->left
+= col
* descr
->column_width
;
448 rect
->right
= rect
->left
+ descr
->column_width
;
449 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
450 rect
->bottom
= rect
->top
+ descr
->item_height
;
452 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
455 rect
->right
+= descr
->horz_pos
;
456 if ((index
>= 0) && (index
< descr
->nb_items
))
458 if (index
< descr
->top_item
)
460 for (i
= descr
->top_item
-1; i
>= index
; i
--)
461 rect
->top
-= descr
->items
[i
].height
;
465 for (i
= descr
->top_item
; i
< index
; i
++)
466 rect
->top
+= descr
->items
[i
].height
;
468 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
474 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
475 rect
->bottom
= rect
->top
+ descr
->item_height
;
476 rect
->right
+= descr
->horz_pos
;
479 TRACE("item %d, rect %s\n", index
, wine_dbgstr_rect(rect
));
481 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
482 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
486 /***********************************************************************
487 * LISTBOX_GetItemFromPoint
489 * Return the item nearest from point (x,y) (in client coordinates).
491 static INT
LISTBOX_GetItemFromPoint( LB_DESCR
*descr
, INT x
, INT y
)
493 INT index
= descr
->top_item
;
495 if (!descr
->nb_items
) return -1; /* No items */
496 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
501 while (index
< descr
->nb_items
)
503 if ((pos
+= descr
->items
[index
].height
) > y
) break;
512 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
516 else if (descr
->style
& LBS_MULTICOLUMN
)
518 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
519 if (y
>= 0) index
+= y
/ descr
->item_height
;
520 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
521 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
525 index
+= (y
/ descr
->item_height
);
527 if (index
< 0) return 0;
528 if (index
>= descr
->nb_items
) return -1;
533 /***********************************************************************
538 static void LISTBOX_PaintItem( LB_DESCR
*descr
, HDC hdc
, const RECT
*rect
,
539 INT index
, UINT action
, BOOL ignoreFocus
)
541 LB_ITEMDATA
*item
= NULL
;
542 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
544 if (IS_OWNERDRAW(descr
))
552 if (action
== ODA_FOCUS
)
553 DrawFocusRect( hdc
, rect
);
555 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
559 /* some programs mess with the clipping region when
560 drawing the item, *and* restore the previous region
561 after they are done, so a region has better to exist
562 else everything ends clipped */
563 GetClientRect(descr
->self
, &r
);
564 hrgn
= CreateRectRgnIndirect(&r
);
565 SelectClipRgn( hdc
, hrgn
);
566 DeleteObject( hrgn
);
568 dis
.CtlType
= ODT_LISTBOX
;
569 dis
.CtlID
= GetWindowLongPtrW( descr
->self
, GWLP_ID
);
570 dis
.hwndItem
= descr
->self
;
571 dis
.itemAction
= action
;
575 if (item
->selected
) dis
.itemState
|= ODS_SELECTED
;
576 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
578 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
579 if (!IsWindowEnabled(descr
->self
)) dis
.itemState
|= ODS_DISABLED
;
580 dis
.itemData
= item
->data
;
582 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%d,%d-%d,%d\n",
583 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
584 dis
.itemState
, rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
585 SendMessageW(descr
->owner
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
589 COLORREF oldText
= 0, oldBk
= 0;
591 if (action
== ODA_FOCUS
)
593 DrawFocusRect( hdc
, rect
);
596 if (item
&& item
->selected
)
598 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
599 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
602 TRACE("[%p]: painting %d (%s) action=%02x rect=%d,%d-%d,%d\n",
603 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
604 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
606 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
607 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
608 else if (!(descr
->style
& LBS_USETABSTOPS
))
609 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
610 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
611 strlenW(item
->str
), NULL
);
614 /* Output empty string to paint background in the full width. */
615 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
616 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
617 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
618 item
->str
, strlenW(item
->str
),
619 descr
->nb_tabs
, descr
->tabs
, 0);
621 if (item
&& item
->selected
)
623 SetBkColor( hdc
, oldBk
);
624 SetTextColor( hdc
, oldText
);
626 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
628 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
633 /***********************************************************************
636 * Change the redraw flag.
638 static void LISTBOX_SetRedraw( LB_DESCR
*descr
, BOOL on
)
642 if (!(descr
->style
& LBS_NOREDRAW
)) return;
643 descr
->style
&= ~LBS_NOREDRAW
;
644 if (descr
->style
& LBS_DISPLAYCHANGED
)
645 { /* page was changed while setredraw false, refresh automatically */
646 InvalidateRect(descr
->self
, NULL
, TRUE
);
647 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
648 { /* reset top of page if less than number of items/page */
649 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
650 if (descr
->top_item
< 0) descr
->top_item
= 0;
652 descr
->style
&= ~LBS_DISPLAYCHANGED
;
654 LISTBOX_UpdateScroll( descr
);
656 else descr
->style
|= LBS_NOREDRAW
;
660 /***********************************************************************
661 * LISTBOX_RepaintItem
663 * Repaint a single item synchronously.
665 static void LISTBOX_RepaintItem( LB_DESCR
*descr
, INT index
, UINT action
)
670 HBRUSH hbrush
, oldBrush
= 0;
672 /* Do not repaint the item if the item is not visible */
673 if (!IsWindowVisible(descr
->self
)) return;
674 if (descr
->style
& LBS_NOREDRAW
)
676 descr
->style
|= LBS_DISPLAYCHANGED
;
679 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
680 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
681 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
682 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
683 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
684 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
685 if (!IsWindowEnabled(descr
->self
))
686 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
687 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
688 LISTBOX_PaintItem( descr
, hdc
, &rect
, index
, action
, TRUE
);
689 if (oldFont
) SelectObject( hdc
, oldFont
);
690 if (oldBrush
) SelectObject( hdc
, oldBrush
);
691 ReleaseDC( descr
->self
, hdc
);
695 /***********************************************************************
696 * LISTBOX_DrawFocusRect
698 static void LISTBOX_DrawFocusRect( LB_DESCR
*descr
, BOOL on
)
704 /* Do not repaint the item if the item is not visible */
705 if (!IsWindowVisible(descr
->self
)) return;
707 if (descr
->focus_item
== -1) return;
708 if (!descr
->caret_on
|| !descr
->in_focus
) return;
710 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) != 1) return;
711 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
712 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
713 if (!IsWindowEnabled(descr
->self
))
714 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
715 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
716 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, on
? FALSE
: TRUE
);
717 if (oldFont
) SelectObject( hdc
, oldFont
);
718 ReleaseDC( descr
->self
, hdc
);
722 /***********************************************************************
723 * LISTBOX_InitStorage
725 static LRESULT
LISTBOX_InitStorage( LB_DESCR
*descr
, INT nb_items
)
729 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
730 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
732 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
733 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
734 nb_items
* sizeof(LB_ITEMDATA
));
737 item
= HeapAlloc( GetProcessHeap(), 0,
738 nb_items
* sizeof(LB_ITEMDATA
));
743 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
751 /***********************************************************************
752 * LISTBOX_SetTabStops
754 static BOOL
LISTBOX_SetTabStops( LB_DESCR
*descr
, INT count
, LPINT tabs
, BOOL short_ints
)
758 if (!(descr
->style
& LBS_USETABSTOPS
))
760 SetLastError(ERROR_LB_WITHOUT_TABSTOPS
);
764 HeapFree( GetProcessHeap(), 0, descr
->tabs
);
765 if (!(descr
->nb_tabs
= count
))
770 if (!(descr
->tabs
= HeapAlloc( GetProcessHeap(), 0,
771 descr
->nb_tabs
* sizeof(INT
) )))
776 LPINT16 p
= (LPINT16
)tabs
;
778 TRACE("[%p]: settabstops ", descr
->self
);
779 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
780 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
781 TRACE("%hd ", descr
->tabs
[i
]);
785 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
787 /* convert into "dialog units"*/
788 for (i
= 0; i
< descr
->nb_tabs
; i
++)
789 descr
->tabs
[i
] = MulDiv(descr
->tabs
[i
], descr
->avg_char_width
, 4);
795 /***********************************************************************
798 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPWSTR buffer
, BOOL unicode
)
800 if ((index
< 0) || (index
>= descr
->nb_items
))
802 SetLastError(ERROR_INVALID_INDEX
);
805 if (HAS_STRINGS(descr
))
809 DWORD len
= strlenW(descr
->items
[index
].str
);
812 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[index
].str
, len
,
813 NULL
, 0, NULL
, NULL
);
816 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
820 strcpyW( buffer
, descr
->items
[index
].str
);
821 return strlenW(buffer
);
825 return WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1, (LPSTR
)buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
829 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
830 return sizeof(DWORD
);
834 static inline INT
LISTBOX_lstrcmpiW( LCID lcid
, LPCWSTR str1
, LPCWSTR str2
)
836 INT ret
= CompareStringW( lcid
, NORM_IGNORECASE
, str1
, -1, str2
, -1 );
837 if (ret
== CSTR_LESS_THAN
)
839 if (ret
== CSTR_EQUAL
)
841 if (ret
== CSTR_GREATER_THAN
)
846 /***********************************************************************
847 * LISTBOX_FindStringPos
849 * Find the nearest string located before a given string in sort order.
850 * If 'exact' is TRUE, return an error if we don't get an exact match.
852 static INT
LISTBOX_FindStringPos( LB_DESCR
*descr
, LPCWSTR str
, BOOL exact
)
854 INT index
, min
, max
, res
= -1;
856 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
858 max
= descr
->nb_items
;
861 index
= (min
+ max
) / 2;
862 if (HAS_STRINGS(descr
))
863 res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, descr
->items
[index
].str
);
866 COMPAREITEMSTRUCT cis
;
867 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
869 cis
.CtlType
= ODT_LISTBOX
;
871 cis
.hwndItem
= descr
->self
;
872 /* note that some application (MetaStock) expects the second item
873 * to be in the listbox */
875 cis
.itemData1
= (ULONG_PTR
)str
;
877 cis
.itemData2
= descr
->items
[index
].data
;
878 cis
.dwLocaleId
= descr
->locale
;
879 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
881 if (!res
) return index
;
882 if (res
< 0) max
= index
;
883 else min
= index
+ 1;
885 return exact
? -1 : max
;
889 /***********************************************************************
890 * LISTBOX_FindFileStrPos
892 * Find the nearest string located before a given string in directory
893 * sort order (i.e. first files, then directories, then drives).
895 static INT
LISTBOX_FindFileStrPos( LB_DESCR
*descr
, LPCWSTR str
)
897 INT min
, max
, res
= -1;
899 if (!HAS_STRINGS(descr
))
900 return LISTBOX_FindStringPos( descr
, str
, FALSE
);
902 max
= descr
->nb_items
;
905 INT index
= (min
+ max
) / 2;
906 LPCWSTR p
= descr
->items
[index
].str
;
907 if (*p
== '[') /* drive or directory */
909 if (*str
!= '[') res
= -1;
910 else if (p
[1] == '-') /* drive */
912 if (str
[1] == '-') res
= str
[2] - p
[2];
917 if (str
[1] == '-') res
= 1;
918 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
923 if (*str
== '[') res
= 1;
924 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
926 if (!res
) return index
;
927 if (res
< 0) max
= index
;
928 else min
= index
+ 1;
934 /***********************************************************************
937 * Find the item beginning with a given string.
939 static INT
LISTBOX_FindString( LB_DESCR
*descr
, INT start
, LPCWSTR str
, BOOL exact
)
944 if (start
>= descr
->nb_items
) start
= -1;
945 item
= descr
->items
+ start
+ 1;
946 if (HAS_STRINGS(descr
))
948 if (!str
|| ! str
[0] ) return LB_ERR
;
951 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
952 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
953 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
954 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
958 /* Special case for drives and directories: ignore prefix */
959 #define CHECK_DRIVE(item) \
960 if ((item)->str[0] == '[') \
962 if (!strncmpiW( str, (item)->str+1, len )) return i; \
963 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
967 INT len
= strlenW(str
);
968 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
970 if (!strncmpiW( str
, item
->str
, len
)) return i
;
973 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
975 if (!strncmpiW( str
, item
->str
, len
)) return i
;
983 if (exact
&& (descr
->style
& LBS_SORT
))
984 /* If sorted, use a WM_COMPAREITEM binary search */
985 return LISTBOX_FindStringPos( descr
, str
, TRUE
);
987 /* Otherwise use a linear search */
988 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
989 if (item
->data
== (ULONG_PTR
)str
) return i
;
990 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
991 if (item
->data
== (ULONG_PTR
)str
) return i
;
997 /***********************************************************************
998 * LISTBOX_GetSelCount
1000 static LRESULT
LISTBOX_GetSelCount( LB_DESCR
*descr
)
1003 LB_ITEMDATA
*item
= descr
->items
;
1005 if (!(descr
->style
& LBS_MULTIPLESEL
) ||
1006 (descr
->style
& LBS_NOSEL
))
1008 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
1009 if (item
->selected
) count
++;
1014 /***********************************************************************
1015 * LISTBOX_GetSelItems16
1017 static LRESULT
LISTBOX_GetSelItems16( LB_DESCR
*descr
, INT16 max
, LPINT16 array
)
1020 LB_ITEMDATA
*item
= descr
->items
;
1022 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1023 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
1024 if (item
->selected
) array
[count
++] = (INT16
)i
;
1029 /***********************************************************************
1030 * LISTBOX_GetSelItems
1032 static LRESULT
LISTBOX_GetSelItems( LB_DESCR
*descr
, INT max
, LPINT array
)
1035 LB_ITEMDATA
*item
= descr
->items
;
1037 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1038 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
1039 if (item
->selected
) array
[count
++] = i
;
1044 /***********************************************************************
1047 static LRESULT
LISTBOX_Paint( LB_DESCR
*descr
, HDC hdc
)
1049 INT i
, col_pos
= descr
->page_size
- 1;
1051 RECT focusRect
= {-1, -1, -1, -1};
1053 HBRUSH hbrush
, oldBrush
= 0;
1055 if (descr
->style
& LBS_NOREDRAW
) return 0;
1057 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1058 if (descr
->style
& LBS_MULTICOLUMN
)
1059 rect
.right
= rect
.left
+ descr
->column_width
;
1060 else if (descr
->horz_pos
)
1062 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1063 rect
.right
+= descr
->horz_pos
;
1066 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1067 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
1068 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
1069 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1070 if (!IsWindowEnabled(descr
->self
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1072 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1075 /* Special case for empty listbox: paint focus rect */
1076 rect
.bottom
= rect
.top
+ descr
->item_height
;
1077 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1078 &rect
, NULL
, 0, NULL
);
1079 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1080 rect
.top
= rect
.bottom
;
1083 /* Paint all the item, regarding the selection
1084 Focus state will be painted after */
1086 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1088 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1089 rect
.bottom
= rect
.top
+ descr
->item_height
;
1091 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1093 if (i
== descr
->focus_item
)
1095 /* keep the focus rect, to paint the focus item after */
1096 focusRect
.left
= rect
.left
;
1097 focusRect
.right
= rect
.right
;
1098 focusRect
.top
= rect
.top
;
1099 focusRect
.bottom
= rect
.bottom
;
1101 LISTBOX_PaintItem( descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1102 rect
.top
= rect
.bottom
;
1104 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1106 if (!IS_OWNERDRAW(descr
))
1108 /* Clear the bottom of the column */
1109 if (rect
.top
< descr
->height
)
1111 rect
.bottom
= descr
->height
;
1112 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1113 &rect
, NULL
, 0, NULL
);
1117 /* Go to the next column */
1118 rect
.left
+= descr
->column_width
;
1119 rect
.right
+= descr
->column_width
;
1121 col_pos
= descr
->page_size
- 1;
1126 if (rect
.top
>= descr
->height
) break;
1130 /* Paint the focus item now */
1131 if (focusRect
.top
!= focusRect
.bottom
&&
1132 descr
->caret_on
&& descr
->in_focus
)
1133 LISTBOX_PaintItem( descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1135 if (!IS_OWNERDRAW(descr
))
1137 /* Clear the remainder of the client area */
1138 if (rect
.top
< descr
->height
)
1140 rect
.bottom
= descr
->height
;
1141 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1142 &rect
, NULL
, 0, NULL
);
1144 if (rect
.right
< descr
->width
)
1146 rect
.left
= rect
.right
;
1147 rect
.right
= descr
->width
;
1149 rect
.bottom
= descr
->height
;
1150 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1151 &rect
, NULL
, 0, NULL
);
1154 if (oldFont
) SelectObject( hdc
, oldFont
);
1155 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1160 /***********************************************************************
1161 * LISTBOX_InvalidateItems
1163 * Invalidate all items from a given item. If the specified item is not
1164 * visible, nothing happens.
1166 static void LISTBOX_InvalidateItems( LB_DESCR
*descr
, INT index
)
1170 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1172 if (descr
->style
& LBS_NOREDRAW
)
1174 descr
->style
|= LBS_DISPLAYCHANGED
;
1177 rect
.bottom
= descr
->height
;
1178 InvalidateRect( descr
->self
, &rect
, TRUE
);
1179 if (descr
->style
& LBS_MULTICOLUMN
)
1181 /* Repaint the other columns */
1182 rect
.left
= rect
.right
;
1183 rect
.right
= descr
->width
;
1185 InvalidateRect( descr
->self
, &rect
, TRUE
);
1190 static void LISTBOX_InvalidateItemRect( LB_DESCR
*descr
, INT index
)
1194 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1195 InvalidateRect( descr
->self
, &rect
, TRUE
);
1198 /***********************************************************************
1199 * LISTBOX_GetItemHeight
1201 static LRESULT
LISTBOX_GetItemHeight( LB_DESCR
*descr
, INT index
)
1203 if (descr
->style
& LBS_OWNERDRAWVARIABLE
&& descr
->nb_items
> 0)
1205 if ((index
< 0) || (index
>= descr
->nb_items
))
1207 SetLastError(ERROR_INVALID_INDEX
);
1210 return descr
->items
[index
].height
;
1212 else return descr
->item_height
;
1216 /***********************************************************************
1217 * LISTBOX_SetItemHeight
1219 static LRESULT
LISTBOX_SetItemHeight( LB_DESCR
*descr
, INT index
, INT height
, BOOL repaint
)
1221 if (height
> MAXBYTE
)
1224 if (!height
) height
= 1;
1226 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1228 if ((index
< 0) || (index
>= descr
->nb_items
))
1230 SetLastError(ERROR_INVALID_INDEX
);
1233 TRACE("[%p]: item %d height = %d\n", descr
->self
, index
, height
);
1234 descr
->items
[index
].height
= height
;
1235 LISTBOX_UpdateScroll( descr
);
1237 LISTBOX_InvalidateItems( descr
, index
);
1239 else if (height
!= descr
->item_height
)
1241 TRACE("[%p]: new height = %d\n", descr
->self
, height
);
1242 descr
->item_height
= height
;
1243 LISTBOX_UpdatePage( descr
);
1244 LISTBOX_UpdateScroll( descr
);
1246 InvalidateRect( descr
->self
, 0, TRUE
);
1252 /***********************************************************************
1253 * LISTBOX_SetHorizontalPos
1255 static void LISTBOX_SetHorizontalPos( LB_DESCR
*descr
, INT pos
)
1259 if (pos
> descr
->horz_extent
- descr
->width
)
1260 pos
= descr
->horz_extent
- descr
->width
;
1261 if (pos
< 0) pos
= 0;
1262 if (!(diff
= descr
->horz_pos
- pos
)) return;
1263 TRACE("[%p]: new horz pos = %d\n", descr
->self
, pos
);
1264 descr
->horz_pos
= pos
;
1265 LISTBOX_UpdateScroll( descr
);
1266 if (abs(diff
) < descr
->width
)
1269 /* Invalidate the focused item so it will be repainted correctly */
1270 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1271 InvalidateRect( descr
->self
, &rect
, TRUE
);
1272 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
1273 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1276 InvalidateRect( descr
->self
, NULL
, TRUE
);
1280 /***********************************************************************
1281 * LISTBOX_SetHorizontalExtent
1283 static LRESULT
LISTBOX_SetHorizontalExtent( LB_DESCR
*descr
, INT extent
)
1285 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1287 if (extent
<= 0) extent
= 1;
1288 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1289 TRACE("[%p]: new horz extent = %d\n", descr
->self
, extent
);
1290 descr
->horz_extent
= extent
;
1291 if (descr
->horz_pos
> extent
- descr
->width
)
1292 LISTBOX_SetHorizontalPos( descr
, extent
- descr
->width
);
1294 LISTBOX_UpdateScroll( descr
);
1299 /***********************************************************************
1300 * LISTBOX_SetColumnWidth
1302 static LRESULT
LISTBOX_SetColumnWidth( LB_DESCR
*descr
, INT width
)
1304 if (width
== descr
->column_width
) return LB_OKAY
;
1305 TRACE("[%p]: new column width = %d\n", descr
->self
, width
);
1306 descr
->column_width
= width
;
1307 LISTBOX_UpdatePage( descr
);
1312 /***********************************************************************
1315 * Returns the item height.
1317 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1321 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1326 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1328 ERR("unable to get DC.\n" );
1331 if (font
) oldFont
= SelectObject( hdc
, font
);
1332 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1333 if (oldFont
) SelectObject( hdc
, oldFont
);
1334 ReleaseDC( descr
->self
, hdc
);
1336 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1337 if (!IS_OWNERDRAW(descr
))
1338 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1343 /***********************************************************************
1344 * LISTBOX_MakeItemVisible
1346 * Make sure that a given item is partially or fully visible.
1348 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1352 TRACE("current top item %d, index %d, fully %d\n", descr
->top_item
, index
, fully
);
1354 if (index
<= descr
->top_item
) top
= index
;
1355 else if (descr
->style
& LBS_MULTICOLUMN
)
1357 INT cols
= descr
->width
;
1358 if (!fully
) cols
+= descr
->column_width
- 1;
1359 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1361 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1362 top
= index
- descr
->page_size
* (cols
- 1);
1364 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1366 INT height
= fully
? descr
->items
[index
].height
: 1;
1367 for (top
= index
; top
> descr
->top_item
; top
--)
1368 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1372 if (index
< descr
->top_item
+ descr
->page_size
) return;
1373 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1374 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1375 top
= index
- descr
->page_size
+ 1;
1377 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1380 /***********************************************************************
1381 * LISTBOX_SetCaretIndex
1384 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1387 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1389 INT oldfocus
= descr
->focus_item
;
1391 TRACE("old focus %d, index %d\n", oldfocus
, index
);
1393 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1394 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1395 if (index
== oldfocus
) return LB_OKAY
;
1397 LISTBOX_DrawFocusRect( descr
, FALSE
);
1398 descr
->focus_item
= index
;
1400 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1401 LISTBOX_DrawFocusRect( descr
, TRUE
);
1407 /***********************************************************************
1408 * LISTBOX_SelectItemRange
1410 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1412 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1417 /* A few sanity checks */
1419 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1420 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1422 if (!descr
->nb_items
) return LB_OKAY
;
1424 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1425 if (first
< 0) first
= 0;
1426 if (last
< first
) return LB_OKAY
;
1428 if (on
) /* Turn selection on */
1430 for (i
= first
; i
<= last
; i
++)
1432 if (descr
->items
[i
].selected
) continue;
1433 descr
->items
[i
].selected
= TRUE
;
1434 LISTBOX_InvalidateItemRect(descr
, i
);
1437 else /* Turn selection off */
1439 for (i
= first
; i
<= last
; i
++)
1441 if (!descr
->items
[i
].selected
) continue;
1442 descr
->items
[i
].selected
= FALSE
;
1443 LISTBOX_InvalidateItemRect(descr
, i
);
1449 /***********************************************************************
1450 * LISTBOX_SetSelection
1452 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1453 BOOL on
, BOOL send_notify
)
1455 TRACE( "cur_sel=%d index=%d notify=%s\n",
1456 descr
->selected_item
, index
, send_notify
? "YES" : "NO" );
1458 if (descr
->style
& LBS_NOSEL
)
1460 descr
->selected_item
= index
;
1463 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1464 if (descr
->style
& LBS_MULTIPLESEL
)
1466 if (index
== -1) /* Select all items */
1467 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1468 else /* Only one item */
1469 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1473 INT oldsel
= descr
->selected_item
;
1474 if (index
== oldsel
) return LB_OKAY
;
1475 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1476 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1477 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1478 descr
->selected_item
= index
;
1479 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1480 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1481 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1483 if( descr
->lphc
) /* set selection change flag for parent combo */
1484 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1490 /***********************************************************************
1493 * Change the caret position and extend the selection to the new caret.
1495 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1497 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1499 if ((index
< 0) || (index
>= descr
->nb_items
))
1502 /* Important, repaint needs to be done in this order if
1503 you want to mimic Windows behavior:
1504 1. Remove the focus and paint the item
1505 2. Remove the selection and paint the item(s)
1506 3. Set the selection and repaint the item(s)
1507 4. Set the focus to 'index' and repaint the item */
1509 /* 1. remove the focus and repaint the item */
1510 LISTBOX_DrawFocusRect( descr
, FALSE
);
1512 /* 2. then turn off the previous selection */
1513 /* 3. repaint the new selected item */
1514 if (descr
->style
& LBS_EXTENDEDSEL
)
1516 if (descr
->anchor_item
!= -1)
1518 INT first
= min( index
, descr
->anchor_item
);
1519 INT last
= max( index
, descr
->anchor_item
);
1521 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1522 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1523 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1526 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1528 /* Set selection to new caret item */
1529 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1532 /* 4. repaint the new item with the focus */
1533 descr
->focus_item
= index
;
1534 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1535 LISTBOX_DrawFocusRect( descr
, TRUE
);
1539 /***********************************************************************
1540 * LISTBOX_InsertItem
1542 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1543 LPWSTR str
, ULONG_PTR data
)
1547 INT oldfocus
= descr
->focus_item
;
1549 if (index
== -1) index
= descr
->nb_items
;
1550 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1551 if (!descr
->items
) max_items
= 0;
1552 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1553 if (descr
->nb_items
== max_items
)
1555 /* We need to grow the array */
1556 max_items
+= LB_ARRAY_GRANULARITY
;
1558 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1559 max_items
* sizeof(LB_ITEMDATA
) );
1561 item
= HeapAlloc( GetProcessHeap(), 0,
1562 max_items
* sizeof(LB_ITEMDATA
) );
1565 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1568 descr
->items
= item
;
1571 /* Insert the item structure */
1573 item
= &descr
->items
[index
];
1574 if (index
< descr
->nb_items
)
1575 RtlMoveMemory( item
+ 1, item
,
1576 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1580 item
->selected
= FALSE
;
1583 /* Get item height */
1585 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1587 MEASUREITEMSTRUCT mis
;
1588 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1590 mis
.CtlType
= ODT_LISTBOX
;
1593 mis
.itemData
= descr
->items
[index
].data
;
1594 mis
.itemHeight
= descr
->item_height
;
1595 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1596 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1597 TRACE("[%p]: measure item %d (%s) = %d\n",
1598 descr
->self
, index
, str
? debugstr_w(str
) : "", item
->height
);
1601 /* Repaint the items */
1603 LISTBOX_UpdateScroll( descr
);
1604 LISTBOX_InvalidateItems( descr
, index
);
1606 /* Move selection and focused item */
1607 /* If listbox was empty, set focus to the first item */
1608 if (descr
->nb_items
== 1)
1609 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1610 /* single select don't change selection index in win31 */
1611 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1613 descr
->selected_item
++;
1614 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1618 if (index
<= descr
->selected_item
)
1620 descr
->selected_item
++;
1621 descr
->focus_item
= oldfocus
; /* focus not changed */
1628 /***********************************************************************
1629 * LISTBOX_InsertString
1631 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
, LPCWSTR str
)
1633 LPWSTR new_str
= NULL
;
1637 if (HAS_STRINGS(descr
))
1639 static const WCHAR empty_stringW
[] = { 0 };
1640 if (!str
) str
= empty_stringW
;
1641 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1643 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1646 strcpyW(new_str
, str
);
1648 else data
= (ULONG_PTR
)str
;
1650 if (index
== -1) index
= descr
->nb_items
;
1651 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, data
)) != 0)
1653 HeapFree( GetProcessHeap(), 0, new_str
);
1657 TRACE("[%p]: added item %d %s\n",
1658 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1663 /***********************************************************************
1664 * LISTBOX_DeleteItem
1666 * Delete the content of an item. 'index' must be a valid index.
1668 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1670 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1671 * while Win95 sends it for all items with user data.
1672 * It's probably better to send it too often than not
1673 * often enough, so this is what we do here.
1675 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1677 DELETEITEMSTRUCT dis
;
1678 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1680 dis
.CtlType
= ODT_LISTBOX
;
1683 dis
.hwndItem
= descr
->self
;
1684 dis
.itemData
= descr
->items
[index
].data
;
1685 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1687 if (HAS_STRINGS(descr
))
1688 HeapFree( GetProcessHeap(), 0, descr
->items
[index
].str
);
1692 /***********************************************************************
1693 * LISTBOX_RemoveItem
1695 * Remove an item from the listbox and delete its content.
1697 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1702 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1704 /* We need to invalidate the original rect instead of the updated one. */
1705 LISTBOX_InvalidateItems( descr
, index
);
1707 LISTBOX_DeleteItem( descr
, index
);
1709 /* Remove the item */
1711 item
= &descr
->items
[index
];
1712 if (index
< descr
->nb_items
-1)
1713 RtlMoveMemory( item
, item
+ 1,
1714 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1716 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1718 /* Shrink the item array if possible */
1720 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1721 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1723 max_items
-= LB_ARRAY_GRANULARITY
;
1724 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1725 max_items
* sizeof(LB_ITEMDATA
) );
1726 if (item
) descr
->items
= item
;
1728 /* Repaint the items */
1730 LISTBOX_UpdateScroll( descr
);
1731 /* if we removed the scrollbar, reset the top of the list
1732 (correct for owner-drawn ???) */
1733 if (descr
->nb_items
== descr
->page_size
)
1734 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1736 /* Move selection and focused item */
1737 if (!IS_MULTISELECT(descr
))
1739 if (index
== descr
->selected_item
)
1740 descr
->selected_item
= -1;
1741 else if (index
< descr
->selected_item
)
1743 descr
->selected_item
--;
1744 if (ISWIN31
) /* win 31 do not change the selected item number */
1745 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1749 if (descr
->focus_item
>= descr
->nb_items
)
1751 descr
->focus_item
= descr
->nb_items
- 1;
1752 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1758 /***********************************************************************
1759 * LISTBOX_ResetContent
1761 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1765 for(i
= descr
->nb_items
- 1; i
>=0; i
--) LISTBOX_DeleteItem( descr
, i
);
1766 HeapFree( GetProcessHeap(), 0, descr
->items
);
1767 descr
->nb_items
= 0;
1768 descr
->top_item
= 0;
1769 descr
->selected_item
= -1;
1770 descr
->focus_item
= 0;
1771 descr
->anchor_item
= -1;
1772 descr
->items
= NULL
;
1776 /***********************************************************************
1779 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, INT count
)
1783 if (HAS_STRINGS(descr
))
1785 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1789 /* FIXME: this is far from optimal... */
1790 if (count
> descr
->nb_items
)
1792 while (count
> descr
->nb_items
)
1793 if ((ret
= LISTBOX_InsertString( descr
, -1, 0 )) < 0)
1796 else if (count
< descr
->nb_items
)
1798 while (count
< descr
->nb_items
)
1799 if ((ret
= LISTBOX_RemoveItem( descr
, (descr
->nb_items
- 1) )) < 0)
1806 /***********************************************************************
1809 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1810 LPCWSTR filespec
, BOOL long_names
)
1813 LRESULT ret
= LB_OKAY
;
1814 WIN32_FIND_DATAW entry
;
1817 /* don't scan directory if we just want drives exclusively */
1818 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1819 /* scan directory */
1820 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1822 int le
= GetLastError();
1823 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1830 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1832 static const WCHAR bracketW
[] = { ']',0 };
1833 static const WCHAR dotW
[] = { '.',0 };
1834 if (!(attrib
& DDL_DIRECTORY
) ||
1835 !strcmpW( entry
.cFileName
, dotW
)) continue;
1837 if (!long_names
&& entry
.cAlternateFileName
[0])
1838 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1840 strcpyW( buffer
+ 1, entry
.cFileName
);
1841 strcatW(buffer
, bracketW
);
1843 else /* not a directory */
1845 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1846 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1848 if ((attrib
& DDL_EXCLUSIVE
) &&
1849 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1852 if (!long_names
&& entry
.cAlternateFileName
[0])
1853 strcpyW( buffer
, entry
.cAlternateFileName
);
1855 strcpyW( buffer
, entry
.cFileName
);
1857 if (!long_names
) CharLowerW( buffer
);
1858 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1859 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1861 } while (FindNextFileW( handle
, &entry
));
1862 FindClose( handle
);
1867 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1869 WCHAR buffer
[] = {'[','-','a','-',']',0};
1870 WCHAR root
[] = {'A',':','\\',0};
1872 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1874 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1875 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1883 /***********************************************************************
1884 * LISTBOX_HandleVScroll
1886 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1890 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1894 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1897 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1900 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1901 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1904 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1905 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1907 case SB_THUMBPOSITION
:
1908 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1911 info
.cbSize
= sizeof(info
);
1912 info
.fMask
= SIF_TRACKPOS
;
1913 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1914 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1917 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1920 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1927 /***********************************************************************
1928 * LISTBOX_HandleHScroll
1930 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1935 if (descr
->style
& LBS_MULTICOLUMN
)
1940 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
1944 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
1948 page
= descr
->width
/ descr
->column_width
;
1949 if (page
< 1) page
= 1;
1950 LISTBOX_SetTopItem( descr
,
1951 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1954 page
= descr
->width
/ descr
->column_width
;
1955 if (page
< 1) page
= 1;
1956 LISTBOX_SetTopItem( descr
,
1957 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1959 case SB_THUMBPOSITION
:
1960 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
1963 info
.cbSize
= sizeof(info
);
1964 info
.fMask
= SIF_TRACKPOS
;
1965 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1966 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
1970 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1973 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1977 else if (descr
->horz_extent
)
1982 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
1985 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
1988 LISTBOX_SetHorizontalPos( descr
,
1989 descr
->horz_pos
- descr
->width
);
1992 LISTBOX_SetHorizontalPos( descr
,
1993 descr
->horz_pos
+ descr
->width
);
1995 case SB_THUMBPOSITION
:
1996 LISTBOX_SetHorizontalPos( descr
, pos
);
1999 info
.cbSize
= sizeof(info
);
2000 info
.fMask
= SIF_TRACKPOS
;
2001 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
2002 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
2005 LISTBOX_SetHorizontalPos( descr
, 0 );
2008 LISTBOX_SetHorizontalPos( descr
,
2009 descr
->horz_extent
- descr
->width
);
2016 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
2018 short gcWheelDelta
= 0;
2019 UINT pulScrollLines
= 3;
2021 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
2023 gcWheelDelta
-= delta
;
2025 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
2027 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
2028 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
2029 LISTBOX_SetTopItem( descr
, descr
->top_item
+ cLineScroll
, TRUE
);
2034 /***********************************************************************
2035 * LISTBOX_HandleLButtonDown
2037 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
2039 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2041 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2042 descr
->self
, x
, y
, index
, descr
->focus_item
);
2044 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2046 if (!descr
->in_focus
)
2048 if( !descr
->lphc
) SetFocus( descr
->self
);
2049 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2052 if (index
== -1) return 0;
2056 if (descr
->style
& LBS_NOTIFY
)
2057 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2058 MAKELPARAM( x
, y
) );
2061 descr
->captured
= TRUE
;
2062 SetCapture( descr
->self
);
2064 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2066 /* we should perhaps make sure that all items are deselected
2067 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2068 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2069 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2072 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2073 if (keys
& MK_CONTROL
)
2075 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2076 LISTBOX_SetSelection( descr
, index
,
2077 !descr
->items
[index
].selected
,
2078 (descr
->style
& LBS_NOTIFY
) != 0);
2082 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2084 if (descr
->style
& LBS_EXTENDEDSEL
)
2086 LISTBOX_SetSelection( descr
, index
,
2087 descr
->items
[index
].selected
,
2088 (descr
->style
& LBS_NOTIFY
) != 0 );
2092 LISTBOX_SetSelection( descr
, index
,
2093 !descr
->items
[index
].selected
,
2094 (descr
->style
& LBS_NOTIFY
) != 0 );
2100 descr
->anchor_item
= index
;
2101 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2102 LISTBOX_SetSelection( descr
, index
,
2103 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2108 if (GetWindowLongW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2115 if (DragDetect( descr
->self
, pt
))
2116 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2123 /*************************************************************************
2124 * LISTBOX_HandleLButtonDownCombo [Internal]
2126 * Process LButtonDown message for the ComboListBox
2129 * pWnd [I] The windows internal structure
2130 * pDescr [I] The ListBox internal structure
2131 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2132 * x [I] X Mouse Coordinate
2133 * y [I] Y Mouse Coordinate
2136 * 0 since we are processing the WM_LBUTTONDOWN Message
2139 * This function is only to be used when a ListBox is a ComboListBox
2142 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2144 RECT clientRect
, screenRect
;
2150 GetClientRect(descr
->self
, &clientRect
);
2152 if(PtInRect(&clientRect
, mousePos
))
2154 /* MousePos is in client, resume normal processing */
2155 if (msg
== WM_LBUTTONDOWN
)
2157 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2158 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2160 else if (descr
->style
& LBS_NOTIFY
)
2161 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2165 POINT screenMousePos
;
2166 HWND hWndOldCapture
;
2168 /* Check the Non-Client Area */
2169 screenMousePos
= mousePos
;
2170 hWndOldCapture
= GetCapture();
2172 GetWindowRect(descr
->self
, &screenRect
);
2173 ClientToScreen(descr
->self
, &screenMousePos
);
2175 if(!PtInRect(&screenRect
, screenMousePos
))
2177 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2178 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2179 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2183 /* Check to see the NC is a scrollbar */
2185 LONG style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2186 /* Check Vertical scroll bar */
2187 if (style
& WS_VSCROLL
)
2189 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2190 if (PtInRect( &clientRect
, mousePos
))
2191 nHitTestType
= HTVSCROLL
;
2193 /* Check horizontal scroll bar */
2194 if (style
& WS_HSCROLL
)
2196 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2197 if (PtInRect( &clientRect
, mousePos
))
2198 nHitTestType
= HTHSCROLL
;
2200 /* Windows sends this message when a scrollbar is clicked
2203 if(nHitTestType
!= 0)
2205 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2206 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2208 /* Resume the Capture after scrolling is complete
2210 if(hWndOldCapture
!= 0)
2211 SetCapture(hWndOldCapture
);
2217 /***********************************************************************
2218 * LISTBOX_HandleLButtonUp
2220 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2222 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2223 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2224 LISTBOX_Timer
= LB_TIMER_NONE
;
2225 if (descr
->captured
)
2227 descr
->captured
= FALSE
;
2228 if (GetCapture() == descr
->self
) ReleaseCapture();
2229 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2230 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2236 /***********************************************************************
2237 * LISTBOX_HandleTimer
2239 * Handle scrolling upon a timer event.
2240 * Return TRUE if scrolling should continue.
2242 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2247 if (descr
->top_item
) index
= descr
->top_item
- 1;
2251 if (descr
->top_item
) index
-= descr
->page_size
;
2254 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2255 if (index
== descr
->focus_item
) index
++;
2256 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2258 case LB_TIMER_RIGHT
:
2259 if (index
+ descr
->page_size
< descr
->nb_items
)
2260 index
+= descr
->page_size
;
2265 if (index
== descr
->focus_item
) return FALSE
;
2266 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2271 /***********************************************************************
2272 * LISTBOX_HandleSystemTimer
2274 * WM_SYSTIMER handler.
2276 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2278 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2280 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2281 LISTBOX_Timer
= LB_TIMER_NONE
;
2287 /***********************************************************************
2288 * LISTBOX_HandleMouseMove
2290 * WM_MOUSEMOVE handler.
2292 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2296 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2298 if (!descr
->captured
) return;
2300 if (descr
->style
& LBS_MULTICOLUMN
)
2303 else if (y
>= descr
->item_height
* descr
->page_size
)
2304 y
= descr
->item_height
* descr
->page_size
- 1;
2308 dir
= LB_TIMER_LEFT
;
2311 else if (x
>= descr
->width
)
2313 dir
= LB_TIMER_RIGHT
;
2314 x
= descr
->width
- 1;
2319 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2320 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2323 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2324 if (index
== -1) index
= descr
->focus_item
;
2325 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2327 /* Start/stop the system timer */
2329 if (dir
!= LB_TIMER_NONE
)
2330 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2331 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2332 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2333 LISTBOX_Timer
= dir
;
2337 /***********************************************************************
2338 * LISTBOX_HandleKeyDown
2340 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2343 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2344 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2345 bForceSelection
= FALSE
; /* only for single select list */
2347 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2349 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2350 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2351 (LPARAM
)descr
->self
);
2352 if (caret
== -2) return 0;
2354 if (caret
== -1) switch(key
)
2357 if (descr
->style
& LBS_MULTICOLUMN
)
2359 bForceSelection
= FALSE
;
2360 if (descr
->focus_item
>= descr
->page_size
)
2361 caret
= descr
->focus_item
- descr
->page_size
;
2366 caret
= descr
->focus_item
- 1;
2367 if (caret
< 0) caret
= 0;
2370 if (descr
->style
& LBS_MULTICOLUMN
)
2372 bForceSelection
= FALSE
;
2373 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2374 caret
= descr
->focus_item
+ descr
->page_size
;
2379 caret
= descr
->focus_item
+ 1;
2380 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2384 if (descr
->style
& LBS_MULTICOLUMN
)
2386 INT page
= descr
->width
/ descr
->column_width
;
2387 if (page
< 1) page
= 1;
2388 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2390 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2391 if (caret
< 0) caret
= 0;
2394 if (descr
->style
& LBS_MULTICOLUMN
)
2396 INT page
= descr
->width
/ descr
->column_width
;
2397 if (page
< 1) page
= 1;
2398 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2400 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2401 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2407 caret
= descr
->nb_items
- 1;
2410 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2411 else if (descr
->style
& LBS_MULTIPLESEL
)
2413 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2414 !descr
->items
[descr
->focus_item
].selected
,
2415 (descr
->style
& LBS_NOTIFY
) != 0 );
2419 bForceSelection
= FALSE
;
2421 if (bForceSelection
) /* focused item is used instead of key */
2422 caret
= descr
->focus_item
;
2425 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2426 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2427 !IS_MULTISELECT(descr
))
2428 descr
->anchor_item
= caret
;
2429 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2431 if (descr
->style
& LBS_MULTIPLESEL
)
2432 descr
->selected_item
= caret
;
2434 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2435 if (descr
->style
& LBS_NOTIFY
)
2439 /* make sure that combo parent doesn't hide us */
2440 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2442 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2449 /***********************************************************************
2450 * LISTBOX_HandleChar
2452 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2460 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2462 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2463 MAKEWPARAM(charW
, descr
->focus_item
),
2464 (LPARAM
)descr
->self
);
2465 if (caret
== -2) return 0;
2468 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2471 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2472 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2473 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2474 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2475 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2481 /***********************************************************************
2484 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2487 MEASUREITEMSTRUCT mis
;
2490 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2493 GetClientRect( hwnd
, &rect
);
2495 descr
->owner
= GetParent( descr
->self
);
2496 descr
->style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2497 descr
->width
= rect
.right
- rect
.left
;
2498 descr
->height
= rect
.bottom
- rect
.top
;
2499 descr
->items
= NULL
;
2500 descr
->nb_items
= 0;
2501 descr
->top_item
= 0;
2502 descr
->selected_item
= -1;
2503 descr
->focus_item
= 0;
2504 descr
->anchor_item
= -1;
2505 descr
->item_height
= 1;
2506 descr
->page_size
= 1;
2507 descr
->column_width
= 150;
2508 descr
->horz_extent
= (descr
->style
& WS_HSCROLL
) ? 1 : 0;
2509 descr
->horz_pos
= 0;
2512 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2513 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2514 descr
->in_focus
= FALSE
;
2515 descr
->captured
= FALSE
;
2517 descr
->locale
= GetUserDefaultLCID();
2520 if (is_old_app(descr
) && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2522 /* Win95 document "List Box Differences" from MSDN:
2523 If a list box in a version 3.x application has either the
2524 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2525 horizontal and vertical scroll bars.
2527 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2532 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2533 descr
->owner
= lphc
->self
;
2536 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2538 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2540 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2541 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2542 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2543 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2545 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2547 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2549 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2550 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2554 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2555 mis
.CtlType
= ODT_LISTBOX
;
2560 mis
.itemHeight
= descr
->item_height
;
2561 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2562 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2566 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2571 /***********************************************************************
2574 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2576 LISTBOX_ResetContent( descr
);
2577 SetWindowLongPtrW( descr
->self
, 0, 0 );
2578 HeapFree( GetProcessHeap(), 0, descr
);
2583 /***********************************************************************
2584 * ListBoxWndProc_common
2586 static LRESULT WINAPI
ListBoxWndProc_common( HWND hwnd
, UINT msg
,
2587 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2589 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2590 LPHEADCOMBO lphc
= 0;
2595 if (!IsWindow(hwnd
)) return 0;
2597 if (msg
== WM_CREATE
)
2599 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2600 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= (LPHEADCOMBO
)lpcs
->lpCreateParams
;
2601 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2602 TRACE("creating wnd=%p descr=%x\n", hwnd
, GetWindowLongPtrW( hwnd
, 0 ) );
2605 /* Ignore all other messages before we get a WM_CREATE */
2606 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2607 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2609 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2611 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2612 descr
->self
, SPY_GetMsgName(msg
, descr
->self
), wParam
, lParam
);
2616 case LB_RESETCONTENT16
:
2617 case LB_RESETCONTENT
:
2618 LISTBOX_ResetContent( descr
);
2619 LISTBOX_UpdateScroll( descr
);
2620 InvalidateRect( descr
->self
, NULL
, TRUE
);
2623 case LB_ADDSTRING16
:
2624 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2630 if(unicode
|| !HAS_STRINGS(descr
))
2631 textW
= (LPWSTR
)lParam
;
2634 LPSTR textA
= (LPSTR
)lParam
;
2635 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2636 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2637 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2639 wParam
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2640 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2641 if (!unicode
&& HAS_STRINGS(descr
))
2642 HeapFree(GetProcessHeap(), 0, textW
);
2646 case LB_INSERTSTRING16
:
2647 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2648 wParam
= (INT
)(INT16
)wParam
;
2650 case LB_INSERTSTRING
:
2654 if(unicode
|| !HAS_STRINGS(descr
))
2655 textW
= (LPWSTR
)lParam
;
2658 LPSTR textA
= (LPSTR
)lParam
;
2659 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2660 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2661 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2663 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2664 if(!unicode
&& HAS_STRINGS(descr
))
2665 HeapFree(GetProcessHeap(), 0, textW
);
2670 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2676 if(unicode
|| !HAS_STRINGS(descr
))
2677 textW
= (LPWSTR
)lParam
;
2680 LPSTR textA
= (LPSTR
)lParam
;
2681 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2682 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2683 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2685 wParam
= LISTBOX_FindFileStrPos( descr
, textW
);
2686 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2687 if(!unicode
&& HAS_STRINGS(descr
))
2688 HeapFree(GetProcessHeap(), 0, textW
);
2692 case LB_DELETESTRING16
:
2693 case LB_DELETESTRING
:
2694 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2695 return descr
->nb_items
;
2698 SetLastError(ERROR_INVALID_INDEX
);
2702 case LB_GETITEMDATA16
:
2703 case LB_GETITEMDATA
:
2704 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2706 SetLastError(ERROR_INVALID_INDEX
);
2709 return descr
->items
[wParam
].data
;
2711 case LB_SETITEMDATA16
:
2712 case LB_SETITEMDATA
:
2713 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2715 SetLastError(ERROR_INVALID_INDEX
);
2718 descr
->items
[wParam
].data
= lParam
;
2719 /* undocumented: returns TRUE, not LB_OKAY (0) */
2724 return descr
->nb_items
;
2727 lParam
= (LPARAM
)MapSL(lParam
);
2730 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, unicode
);
2732 case LB_GETTEXTLEN16
:
2735 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2737 SetLastError(ERROR_INVALID_INDEX
);
2740 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2741 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2742 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2743 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2745 case LB_GETCURSEL16
:
2747 if (descr
->nb_items
== 0)
2749 if (!IS_MULTISELECT(descr
))
2750 return descr
->selected_item
;
2751 if (descr
->selected_item
!= -1)
2752 return descr
->selected_item
;
2753 return descr
->focus_item
;
2754 /* otherwise, if the user tries to move the selection with the */
2755 /* arrow keys, we will give the application something to choke on */
2756 case LB_GETTOPINDEX16
:
2757 case LB_GETTOPINDEX
:
2758 return descr
->top_item
;
2760 case LB_GETITEMHEIGHT16
:
2761 case LB_GETITEMHEIGHT
:
2762 return LISTBOX_GetItemHeight( descr
, wParam
);
2764 case LB_SETITEMHEIGHT16
:
2765 lParam
= LOWORD(lParam
);
2767 case LB_SETITEMHEIGHT
:
2768 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2770 case LB_ITEMFROMPOINT
:
2777 /* The hiword of the return value is not a client area
2778 hittest as suggested by MSDN, but rather a hittest on
2779 the returned listbox item. */
2781 if(descr
->nb_items
== 0)
2782 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2784 pt
.x
= (short)LOWORD(lParam
);
2785 pt
.y
= (short)HIWORD(lParam
);
2787 SetRect(&rect
, 0, 0, descr
->width
, descr
->height
);
2789 if(!PtInRect(&rect
, pt
))
2791 pt
.x
= min(pt
.x
, rect
.right
- 1);
2792 pt
.x
= max(pt
.x
, 0);
2793 pt
.y
= min(pt
.y
, rect
.bottom
- 1);
2794 pt
.y
= max(pt
.y
, 0);
2798 index
= LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
);
2802 index
= descr
->nb_items
- 1;
2805 return MAKELONG(index
, hit
? 0 : 1);
2808 case LB_SETCARETINDEX16
:
2809 case LB_SETCARETINDEX
:
2810 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2811 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2818 case LB_GETCARETINDEX16
:
2819 case LB_GETCARETINDEX
:
2820 return descr
->focus_item
;
2822 case LB_SETTOPINDEX16
:
2823 case LB_SETTOPINDEX
:
2824 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2826 case LB_SETCOLUMNWIDTH16
:
2827 case LB_SETCOLUMNWIDTH
:
2828 return LISTBOX_SetColumnWidth( descr
, wParam
);
2830 case LB_GETITEMRECT16
:
2833 RECT16
*r16
= MapSL(lParam
);
2834 ret
= LISTBOX_GetItemRect( descr
, (INT16
)wParam
, &rect
);
2835 r16
->left
= rect
.left
;
2836 r16
->top
= rect
.top
;
2837 r16
->right
= rect
.right
;
2838 r16
->bottom
= rect
.bottom
;
2842 case LB_GETITEMRECT
:
2843 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2845 case LB_FINDSTRING16
:
2846 wParam
= (INT
)(INT16
)wParam
;
2847 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2853 if(unicode
|| !HAS_STRINGS(descr
))
2854 textW
= (LPWSTR
)lParam
;
2857 LPSTR textA
= (LPSTR
)lParam
;
2858 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2859 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2860 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2862 ret
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2863 if(!unicode
&& HAS_STRINGS(descr
))
2864 HeapFree(GetProcessHeap(), 0, textW
);
2868 case LB_FINDSTRINGEXACT16
:
2869 wParam
= (INT
)(INT16
)wParam
;
2870 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2872 case LB_FINDSTRINGEXACT
:
2876 if(unicode
|| !HAS_STRINGS(descr
))
2877 textW
= (LPWSTR
)lParam
;
2880 LPSTR textA
= (LPSTR
)lParam
;
2881 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2882 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2883 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2885 ret
= LISTBOX_FindString( descr
, wParam
, textW
, TRUE
);
2886 if(!unicode
&& HAS_STRINGS(descr
))
2887 HeapFree(GetProcessHeap(), 0, textW
);
2891 case LB_SELECTSTRING16
:
2892 wParam
= (INT
)(INT16
)wParam
;
2893 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2895 case LB_SELECTSTRING
:
2900 if(HAS_STRINGS(descr
))
2901 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2902 debugstr_a((LPSTR
)lParam
));
2903 if(unicode
|| !HAS_STRINGS(descr
))
2904 textW
= (LPWSTR
)lParam
;
2907 LPSTR textA
= (LPSTR
)lParam
;
2908 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2909 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2910 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2912 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2913 if(!unicode
&& HAS_STRINGS(descr
))
2914 HeapFree(GetProcessHeap(), 0, textW
);
2915 if (index
!= LB_ERR
)
2917 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2918 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2924 wParam
= (INT
)(INT16
)wParam
;
2927 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2929 return descr
->items
[wParam
].selected
;
2932 lParam
= (INT
)(INT16
)lParam
;
2935 return LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2937 case LB_SETCURSEL16
:
2938 wParam
= (INT
)(INT16
)wParam
;
2941 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2942 LISTBOX_SetCaretIndex( descr
, wParam
, FALSE
);
2943 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2944 if (ret
!= LB_ERR
) ret
= descr
->selected_item
;
2947 case LB_GETSELCOUNT16
:
2948 case LB_GETSELCOUNT
:
2949 return LISTBOX_GetSelCount( descr
);
2951 case LB_GETSELITEMS16
:
2952 return LISTBOX_GetSelItems16( descr
, wParam
, (LPINT16
)MapSL(lParam
) );
2954 case LB_GETSELITEMS
:
2955 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2957 case LB_SELITEMRANGE16
:
2958 case LB_SELITEMRANGE
:
2959 if (LOWORD(lParam
) <= HIWORD(lParam
))
2960 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2961 HIWORD(lParam
), wParam
);
2963 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2964 LOWORD(lParam
), wParam
);
2966 case LB_SELITEMRANGEEX16
:
2967 case LB_SELITEMRANGEEX
:
2968 if ((INT
)lParam
>= (INT
)wParam
)
2969 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2971 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
2973 case LB_GETHORIZONTALEXTENT16
:
2974 case LB_GETHORIZONTALEXTENT
:
2975 return descr
->horz_extent
;
2977 case LB_SETHORIZONTALEXTENT16
:
2978 case LB_SETHORIZONTALEXTENT
:
2979 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
2981 case LB_GETANCHORINDEX16
:
2982 case LB_GETANCHORINDEX
:
2983 return descr
->anchor_item
;
2985 case LB_SETANCHORINDEX16
:
2986 wParam
= (INT
)(INT16
)wParam
;
2988 case LB_SETANCHORINDEX
:
2989 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2991 SetLastError(ERROR_INVALID_INDEX
);
2994 descr
->anchor_item
= (INT
)wParam
;
2998 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2999 * be set automatically (this is different in Win32) */
3000 if (wParam
& DDL_DRIVES
) wParam
|= DDL_EXCLUSIVE
;
3001 lParam
= (LPARAM
)MapSL(lParam
);
3008 textW
= (LPWSTR
)lParam
;
3011 LPSTR textA
= (LPSTR
)lParam
;
3012 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
3013 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
3014 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
3016 ret
= LISTBOX_Directory( descr
, wParam
, textW
, msg
== LB_DIR
);
3018 HeapFree(GetProcessHeap(), 0, textW
);
3023 return descr
->locale
;
3028 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
3030 ret
= descr
->locale
;
3031 descr
->locale
= (LCID
)wParam
;
3035 case LB_INITSTORAGE
:
3036 return LISTBOX_InitStorage( descr
, wParam
);
3039 return LISTBOX_SetCount( descr
, (INT
)wParam
);
3041 case LB_SETTABSTOPS16
:
3042 return LISTBOX_SetTabStops( descr
, (INT
)(INT16
)wParam
, MapSL(lParam
), TRUE
);
3044 case LB_SETTABSTOPS
:
3045 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
, FALSE
);
3049 if (descr
->caret_on
)
3051 descr
->caret_on
= TRUE
;
3052 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3053 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3058 if (!descr
->caret_on
)
3060 descr
->caret_on
= FALSE
;
3061 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3062 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3065 case LB_GETLISTBOXINFO
:
3066 FIXME("LB_GETLISTBOXINFO: stub!\n");
3070 return LISTBOX_Destroy( descr
);
3073 InvalidateRect( descr
->self
, NULL
, TRUE
);
3077 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
3081 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
3083 case WM_PRINTCLIENT
:
3087 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
3088 ret
= LISTBOX_Paint( descr
, hdc
);
3089 if( !wParam
) EndPaint( descr
->self
, &ps
);
3093 LISTBOX_UpdateSize( descr
);
3096 return (LRESULT
)descr
->font
;
3098 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
3099 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
3102 descr
->in_focus
= TRUE
;
3103 descr
->caret_on
= TRUE
;
3104 if (descr
->focus_item
!= -1)
3105 LISTBOX_DrawFocusRect( descr
, TRUE
);
3106 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
3109 descr
->in_focus
= FALSE
;
3110 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
3111 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3112 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
3115 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3117 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3119 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
3120 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
3121 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
3122 case WM_LBUTTONDOWN
:
3124 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3125 (INT16
)LOWORD(lParam
),
3126 (INT16
)HIWORD(lParam
) );
3127 return LISTBOX_HandleLButtonDown( descr
, wParam
,
3128 (INT16
)LOWORD(lParam
),
3129 (INT16
)HIWORD(lParam
) );
3130 case WM_LBUTTONDBLCLK
:
3132 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3133 (INT16
)LOWORD(lParam
),
3134 (INT16
)HIWORD(lParam
) );
3135 if (descr
->style
& LBS_NOTIFY
)
3136 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
3139 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
3141 BOOL captured
= descr
->captured
;
3145 mousePos
.x
= (INT16
)LOWORD(lParam
);
3146 mousePos
.y
= (INT16
)HIWORD(lParam
);
3149 * If we are in a dropdown combobox, we simulate that
3150 * the mouse is captured to show the tracking of the item.
3152 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
3153 descr
->captured
= TRUE
;
3155 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
3157 descr
->captured
= captured
;
3159 else if (GetCapture() == descr
->self
)
3161 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
3162 (INT16
)HIWORD(lParam
) );
3172 * If the mouse button "up" is not in the listbox,
3173 * we make sure there is no selection by re-selecting the
3174 * item that was selected when the listbox was made visible.
3176 mousePos
.x
= (INT16
)LOWORD(lParam
);
3177 mousePos
.y
= (INT16
)HIWORD(lParam
);
3179 GetClientRect(descr
->self
, &clientRect
);
3182 * When the user clicks outside the combobox and the focus
3183 * is lost, the owning combobox will send a fake buttonup with
3184 * 0xFFFFFFF as the mouse location, we must also revert the
3185 * selection to the original selection.
3187 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
3188 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
3190 return LISTBOX_HandleLButtonUp( descr
);
3192 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3194 /* for some reason Windows makes it possible to
3195 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3197 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3198 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3199 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3201 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3205 return LISTBOX_HandleKeyDown( descr
, wParam
);
3210 charW
= (WCHAR
)wParam
;
3213 CHAR charA
= (CHAR
)wParam
;
3214 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
3216 return LISTBOX_HandleChar( descr
, charW
);
3219 return LISTBOX_HandleSystemTimer( descr
);
3221 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3224 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3225 wParam
, (LPARAM
)descr
->self
);
3226 TRACE("hbrush = %p\n", hbrush
);
3228 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3231 GetClientRect(descr
->self
, &rect
);
3232 FillRect((HDC
)wParam
, &rect
, hbrush
);
3237 if( lphc
) return 0;
3238 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3239 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3242 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3251 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3252 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3253 hwnd
, msg
, wParam
, lParam
);
3256 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3257 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3260 /***********************************************************************
3263 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3265 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3268 /***********************************************************************
3271 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3273 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);