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/winbase16.h"
44 #include "wine/unicode.h"
45 #include "user_private.h"
47 #include "wine/debug.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(listbox
);
51 /* Items array granularity */
52 #define LB_ARRAY_GRANULARITY 16
54 /* Scrolling timeout in ms */
55 #define LB_SCROLL_TIMEOUT 50
57 /* Listbox system timer id */
60 /* flag listbox changed while setredraw false - internal style */
61 #define LBS_DISPLAYCHANGED 0x80000000
66 LPWSTR str
; /* Item text */
67 BOOL selected
; /* Is item selected? */
68 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
69 ULONG_PTR data
; /* User data */
72 /* Listbox structure */
75 HWND self
; /* Our own window handle */
76 HWND owner
; /* Owner window to send notifications to */
77 UINT style
; /* Window style */
78 INT width
; /* Window width */
79 INT height
; /* Window height */
80 LB_ITEMDATA
*items
; /* Array of items */
81 INT nb_items
; /* Number of items */
82 INT top_item
; /* Top visible item */
83 INT selected_item
; /* Selected item */
84 INT focus_item
; /* Item that has the focus */
85 INT anchor_item
; /* Anchor item for extended selection */
86 INT item_height
; /* Default item height */
87 INT page_size
; /* Items per listbox page */
88 INT column_width
; /* Column width for multi-column listboxes */
89 INT horz_extent
; /* Horizontal extent (0 if no hscroll) */
90 INT horz_pos
; /* Horizontal position */
91 INT nb_tabs
; /* Number of tabs in array */
92 INT
*tabs
; /* Array of tabs */
93 INT avg_char_width
; /* Average width of characters */
94 BOOL caret_on
; /* Is caret on? */
95 BOOL captured
; /* Is mouse captured? */
97 HFONT font
; /* Current font */
98 LCID locale
; /* Current locale for string comparisons */
99 LPHEADCOMBO lphc
; /* ComboLBox */
103 #define IS_OWNERDRAW(descr) \
104 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
106 #define HAS_STRINGS(descr) \
107 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
110 #define IS_MULTISELECT(descr) \
111 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
112 !((descr)->style & LBS_NOSEL))
114 #define SEND_NOTIFICATION(descr,code) \
115 (SendMessageW( (descr)->owner, WM_COMMAND, \
116 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
118 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
120 /* Current timer status */
130 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
132 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
133 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
135 static LRESULT
LISTBOX_GetItemRect( LB_DESCR
*descr
, INT index
, RECT
*rect
);
137 /*********************************************************************
138 * listbox class descriptor
140 const struct builtin_class_descr LISTBOX_builtin_class
=
142 "ListBox", /* name */
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 const struct builtin_class_descr COMBOLBOX_builtin_class
=
157 "ComboLBox", /* name */
158 CS_DBLCLKS
| CS_SAVEBITS
, /* style */
159 ListBoxWndProcA
, /* procA */
160 ListBoxWndProcW
, /* procW */
161 sizeof(LB_DESCR
*), /* extra */
162 IDC_ARROW
, /* cursor */
167 /* check whether app is a Win 3.1 app */
168 inline static BOOL
is_old_app( LB_DESCR
*descr
)
170 return (GetExpWinVer16( GetWindowLongPtrW(descr
->self
, GWLP_HINSTANCE
) ) & 0xFF00 ) == 0x0300;
174 /***********************************************************************
175 * LISTBOX_GetCurrentPageSize
177 * Return the current page size
179 static INT
LISTBOX_GetCurrentPageSize( LB_DESCR
*descr
)
182 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
183 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
185 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
187 if (i
== descr
->top_item
) return 1;
188 else return i
- descr
->top_item
;
192 /***********************************************************************
193 * LISTBOX_GetMaxTopIndex
195 * Return the maximum possible index for the top of the listbox.
197 static INT
LISTBOX_GetMaxTopIndex( LB_DESCR
*descr
)
201 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
203 page
= descr
->height
;
204 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
205 if ((page
-= descr
->items
[max
].height
) < 0) break;
206 if (max
< descr
->nb_items
- 1) max
++;
208 else if (descr
->style
& LBS_MULTICOLUMN
)
210 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
211 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
212 max
= (max
- page
) * descr
->page_size
;
216 max
= descr
->nb_items
- descr
->page_size
;
218 if (max
< 0) max
= 0;
223 /***********************************************************************
224 * LISTBOX_UpdateScroll
226 * Update the scrollbars. Should be called whenever the content
227 * of the listbox changes.
229 static void LISTBOX_UpdateScroll( LB_DESCR
*descr
)
233 /* Check the listbox scroll bar flags individually before we call
234 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
235 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
236 scroll bar when we do not need one.
237 if (!(descr->style & WS_VSCROLL)) return;
240 /* It is important that we check descr->style, and not wnd->dwStyle,
241 for WS_VSCROLL, as the former is exactly the one passed in
242 argument to CreateWindow.
243 In Windows (and from now on in Wine :) a listbox created
244 with such a style (no WS_SCROLL) does not update
245 the scrollbar with listbox-related data, thus letting
246 the programmer use it for his/her own purposes. */
248 if (descr
->style
& LBS_NOREDRAW
) return;
249 info
.cbSize
= sizeof(info
);
251 if (descr
->style
& LBS_MULTICOLUMN
)
254 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
255 info
.nPos
= descr
->top_item
/ descr
->page_size
;
256 info
.nPage
= descr
->width
/ descr
->column_width
;
257 if (info
.nPage
< 1) info
.nPage
= 1;
258 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
259 if (descr
->style
& LBS_DISABLENOSCROLL
)
260 info
.fMask
|= SIF_DISABLENOSCROLL
;
261 if (descr
->style
& WS_HSCROLL
)
262 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
264 info
.fMask
= SIF_RANGE
;
265 if (descr
->style
& WS_VSCROLL
)
266 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
271 info
.nMax
= descr
->nb_items
- 1;
272 info
.nPos
= descr
->top_item
;
273 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
274 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
275 if (descr
->style
& LBS_DISABLENOSCROLL
)
276 info
.fMask
|= SIF_DISABLENOSCROLL
;
277 if (descr
->style
& WS_VSCROLL
)
278 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
280 if (descr
->horz_extent
)
283 info
.nMax
= descr
->horz_extent
- 1;
284 info
.nPos
= descr
->horz_pos
;
285 info
.nPage
= descr
->width
;
286 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
287 if (descr
->style
& LBS_DISABLENOSCROLL
)
288 info
.fMask
|= SIF_DISABLENOSCROLL
;
289 if (descr
->style
& WS_HSCROLL
)
290 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
296 /***********************************************************************
299 * Set the top item of the listbox, scrolling up or down if necessary.
301 static LRESULT
LISTBOX_SetTopItem( LB_DESCR
*descr
, INT index
, BOOL scroll
)
303 INT max
= LISTBOX_GetMaxTopIndex( descr
);
304 if (index
> max
) index
= max
;
305 if (index
< 0) index
= 0;
306 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
307 if (descr
->top_item
== index
) return LB_OKAY
;
308 if (descr
->style
& LBS_MULTICOLUMN
)
310 INT diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
311 if (scroll
&& (abs(diff
) < descr
->width
))
312 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
313 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
321 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
325 if (index
> descr
->top_item
)
327 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
328 diff
-= descr
->items
[i
].height
;
332 for (i
= index
; i
< descr
->top_item
; i
++)
333 diff
+= descr
->items
[i
].height
;
337 diff
= (descr
->top_item
- index
) * descr
->item_height
;
339 if (abs(diff
) < descr
->height
)
340 ScrollWindowEx( descr
->self
, 0, diff
, NULL
, NULL
, 0, NULL
,
341 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
345 if (!scroll
) InvalidateRect( descr
->self
, NULL
, TRUE
);
346 descr
->top_item
= index
;
347 LISTBOX_UpdateScroll( descr
);
352 /***********************************************************************
355 * Update the page size. Should be called when the size of
356 * the client area or the item height changes.
358 static void LISTBOX_UpdatePage( LB_DESCR
*descr
)
362 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
364 if (page_size
== descr
->page_size
) return;
365 descr
->page_size
= page_size
;
366 if (descr
->style
& LBS_MULTICOLUMN
)
367 InvalidateRect( descr
->self
, NULL
, TRUE
);
368 LISTBOX_SetTopItem( descr
, descr
->top_item
, FALSE
);
372 /***********************************************************************
375 * Update the size of the listbox. Should be called when the size of
376 * the client area changes.
378 static void LISTBOX_UpdateSize( LB_DESCR
*descr
)
382 GetClientRect( descr
->self
, &rect
);
383 descr
->width
= rect
.right
- rect
.left
;
384 descr
->height
= rect
.bottom
- rect
.top
;
385 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
390 GetWindowRect( descr
->self
, &rect
);
391 if(descr
->item_height
!= 0)
392 remaining
= descr
->height
% descr
->item_height
;
395 if ((descr
->height
> descr
->item_height
) && remaining
)
397 if (is_old_app(descr
))
398 { /* give a margin for error to 16 bits programs - if we need
399 less than the height of the nonclient area, round to the
400 *next* number of items */
401 int ncheight
= rect
.bottom
- rect
.top
- descr
->height
;
402 if ((descr
->item_height
- remaining
) <= ncheight
)
403 remaining
= remaining
- descr
->item_height
;
405 TRACE("[%p]: changing height %d -> %d\n",
406 descr
->self
, descr
->height
, descr
->height
- remaining
);
407 SetWindowPos( descr
->self
, 0, 0, 0, rect
.right
- rect
.left
,
408 rect
.bottom
- rect
.top
- remaining
,
409 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
413 TRACE("[%p]: new size = %d,%d\n", descr
->self
, descr
->width
, descr
->height
);
414 LISTBOX_UpdatePage( descr
);
415 LISTBOX_UpdateScroll( descr
);
417 /* Invalidate the focused item so it will be repainted correctly */
418 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
420 InvalidateRect( descr
->self
, &rect
, FALSE
);
425 /***********************************************************************
426 * LISTBOX_GetItemRect
428 * Get the rectangle enclosing an item, in listbox client coordinates.
429 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
431 static LRESULT
LISTBOX_GetItemRect( LB_DESCR
*descr
, INT index
, RECT
*rect
)
433 /* Index <= 0 is legal even on empty listboxes */
434 if (index
&& (index
>= descr
->nb_items
))
436 memset(rect
, 0, sizeof(*rect
));
437 SetLastError(ERROR_INVALID_INDEX
);
440 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
441 if (descr
->style
& LBS_MULTICOLUMN
)
443 INT col
= (index
/ descr
->page_size
) -
444 (descr
->top_item
/ descr
->page_size
);
445 rect
->left
+= col
* descr
->column_width
;
446 rect
->right
= rect
->left
+ descr
->column_width
;
447 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
448 rect
->bottom
= rect
->top
+ descr
->item_height
;
450 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
453 rect
->right
+= descr
->horz_pos
;
454 if ((index
>= 0) && (index
< descr
->nb_items
))
456 if (index
< descr
->top_item
)
458 for (i
= descr
->top_item
-1; i
>= index
; i
--)
459 rect
->top
-= descr
->items
[i
].height
;
463 for (i
= descr
->top_item
; i
< index
; i
++)
464 rect
->top
+= descr
->items
[i
].height
;
466 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
472 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
473 rect
->bottom
= rect
->top
+ descr
->item_height
;
474 rect
->right
+= descr
->horz_pos
;
477 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
478 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
482 /***********************************************************************
483 * LISTBOX_GetItemFromPoint
485 * Return the item nearest from point (x,y) (in client coordinates).
487 static INT
LISTBOX_GetItemFromPoint( LB_DESCR
*descr
, INT x
, INT y
)
489 INT index
= descr
->top_item
;
491 if (!descr
->nb_items
) return -1; /* No items */
492 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
497 while (index
< descr
->nb_items
)
499 if ((pos
+= descr
->items
[index
].height
) > y
) break;
508 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
512 else if (descr
->style
& LBS_MULTICOLUMN
)
514 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
515 if (y
>= 0) index
+= y
/ descr
->item_height
;
516 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
517 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
521 index
+= (y
/ descr
->item_height
);
523 if (index
< 0) return 0;
524 if (index
>= descr
->nb_items
) return -1;
529 /***********************************************************************
534 static void LISTBOX_PaintItem( LB_DESCR
*descr
, HDC hdc
, const RECT
*rect
,
535 INT index
, UINT action
, BOOL ignoreFocus
)
537 LB_ITEMDATA
*item
= NULL
;
538 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
540 if (IS_OWNERDRAW(descr
))
548 if (action
== ODA_FOCUS
)
549 DrawFocusRect( hdc
, rect
);
551 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
555 /* some programs mess with the clipping region when
556 drawing the item, *and* restore the previous region
557 after they are done, so a region has better to exist
558 else everything ends clipped */
559 GetClientRect(descr
->self
, &r
);
560 hrgn
= CreateRectRgnIndirect(&r
);
561 SelectClipRgn( hdc
, hrgn
);
562 DeleteObject( hrgn
);
564 dis
.CtlType
= ODT_LISTBOX
;
565 dis
.CtlID
= GetWindowLongPtrW( descr
->self
, GWLP_ID
);
566 dis
.hwndItem
= descr
->self
;
567 dis
.itemAction
= action
;
571 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
572 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
574 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
575 if (!IsWindowEnabled(descr
->self
)) dis
.itemState
|= ODS_DISABLED
;
576 dis
.itemData
= item
? item
->data
: 0;
578 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%ld,%ld-%ld,%ld\n",
579 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
580 dis
.itemState
, rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
581 SendMessageW(descr
->owner
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
585 COLORREF oldText
= 0, oldBk
= 0;
587 if (action
== ODA_FOCUS
)
589 DrawFocusRect( hdc
, rect
);
592 if (item
&& item
->selected
)
594 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
595 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
598 TRACE("[%p]: painting %d (%s) action=%02x rect=%ld,%ld-%ld,%ld\n",
599 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
600 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
602 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
603 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
604 else if (!(descr
->style
& LBS_USETABSTOPS
))
605 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
606 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
607 strlenW(item
->str
), NULL
);
610 /* Output empty string to paint background in the full width. */
611 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
612 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
613 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
614 item
->str
, strlenW(item
->str
),
615 descr
->nb_tabs
, descr
->tabs
, 0);
617 if (item
&& item
->selected
)
619 SetBkColor( hdc
, oldBk
);
620 SetTextColor( hdc
, oldText
);
622 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
624 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
629 /***********************************************************************
632 * Change the redraw flag.
634 static void LISTBOX_SetRedraw( LB_DESCR
*descr
, BOOL on
)
638 if (!(descr
->style
& LBS_NOREDRAW
)) return;
639 descr
->style
&= ~LBS_NOREDRAW
;
640 if (descr
->style
& LBS_DISPLAYCHANGED
)
641 { /* page was changed while setredraw false, refresh automatically */
642 InvalidateRect(descr
->self
, NULL
, TRUE
);
643 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
644 { /* reset top of page if less than number of items/page */
645 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
646 if (descr
->top_item
< 0) descr
->top_item
= 0;
648 descr
->style
&= ~LBS_DISPLAYCHANGED
;
650 LISTBOX_UpdateScroll( descr
);
652 else descr
->style
|= LBS_NOREDRAW
;
656 /***********************************************************************
657 * LISTBOX_RepaintItem
659 * Repaint a single item synchronously.
661 static void LISTBOX_RepaintItem( LB_DESCR
*descr
, INT index
, UINT action
)
666 HBRUSH hbrush
, oldBrush
= 0;
668 /* Do not repaint the item if the item is not visible */
669 if (!IsWindowVisible(descr
->self
)) return;
670 if (descr
->style
& LBS_NOREDRAW
)
672 descr
->style
|= LBS_DISPLAYCHANGED
;
675 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
676 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
677 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
678 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
679 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
680 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
681 if (!IsWindowEnabled(descr
->self
))
682 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
683 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
684 LISTBOX_PaintItem( descr
, hdc
, &rect
, index
, action
, FALSE
);
685 if (oldFont
) SelectObject( hdc
, oldFont
);
686 if (oldBrush
) SelectObject( hdc
, oldBrush
);
687 ReleaseDC( descr
->self
, hdc
);
691 /***********************************************************************
692 * LISTBOX_InitStorage
694 static LRESULT
LISTBOX_InitStorage( LB_DESCR
*descr
, INT nb_items
)
698 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
699 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
701 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
702 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
703 nb_items
* sizeof(LB_ITEMDATA
));
706 item
= HeapAlloc( GetProcessHeap(), 0,
707 nb_items
* sizeof(LB_ITEMDATA
));
712 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
720 /***********************************************************************
721 * LISTBOX_SetTabStops
723 static BOOL
LISTBOX_SetTabStops( LB_DESCR
*descr
, INT count
, LPINT tabs
, BOOL short_ints
)
727 if (!(descr
->style
& LBS_USETABSTOPS
))
729 SetLastError(ERROR_LB_WITHOUT_TABSTOPS
);
733 HeapFree( GetProcessHeap(), 0, descr
->tabs
);
734 if (!(descr
->nb_tabs
= count
))
739 if (!(descr
->tabs
= HeapAlloc( GetProcessHeap(), 0,
740 descr
->nb_tabs
* sizeof(INT
) )))
745 LPINT16 p
= (LPINT16
)tabs
;
747 TRACE("[%p]: settabstops ", descr
->self
);
748 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
749 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
750 TRACE("%hd ", descr
->tabs
[i
]);
754 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
756 /* convert into "dialog units"*/
757 for (i
= 0; i
< descr
->nb_tabs
; i
++)
758 descr
->tabs
[i
] = MulDiv(descr
->tabs
[i
], descr
->avg_char_width
, 4);
764 /***********************************************************************
767 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPWSTR buffer
, BOOL unicode
)
769 if ((index
< 0) || (index
>= descr
->nb_items
))
771 SetLastError(ERROR_INVALID_INDEX
);
774 if (HAS_STRINGS(descr
))
778 DWORD len
= strlenW(descr
->items
[index
].str
);
781 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[index
].str
, len
,
782 NULL
, 0, NULL
, NULL
);
785 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
789 strcpyW( buffer
, descr
->items
[index
].str
);
790 return strlenW(buffer
);
794 return WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1, (LPSTR
)buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
798 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
799 return sizeof(DWORD
);
803 static inline INT
LISTBOX_lstrcmpiW( LCID lcid
, LPCWSTR str1
, LPCWSTR str2
)
805 INT ret
= CompareStringW( lcid
, NORM_IGNORECASE
, str1
, -1, str2
, -1 );
806 if (ret
== CSTR_LESS_THAN
)
808 if (ret
== CSTR_EQUAL
)
810 if (ret
== CSTR_GREATER_THAN
)
815 /***********************************************************************
816 * LISTBOX_FindStringPos
818 * Find the nearest string located before a given string in sort order.
819 * If 'exact' is TRUE, return an error if we don't get an exact match.
821 static INT
LISTBOX_FindStringPos( LB_DESCR
*descr
, LPCWSTR str
, BOOL exact
)
823 INT index
, min
, max
, res
= -1;
825 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
827 max
= descr
->nb_items
;
830 index
= (min
+ max
) / 2;
831 if (HAS_STRINGS(descr
))
832 res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, descr
->items
[index
].str
);
835 COMPAREITEMSTRUCT cis
;
836 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
838 cis
.CtlType
= ODT_LISTBOX
;
840 cis
.hwndItem
= descr
->self
;
841 /* note that some application (MetaStock) expects the second item
842 * to be in the listbox */
844 cis
.itemData1
= (ULONG_PTR
)str
;
846 cis
.itemData2
= descr
->items
[index
].data
;
847 cis
.dwLocaleId
= descr
->locale
;
848 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
850 if (!res
) return index
;
851 if (res
< 0) max
= index
;
852 else min
= index
+ 1;
854 return exact
? -1 : max
;
858 /***********************************************************************
859 * LISTBOX_FindFileStrPos
861 * Find the nearest string located before a given string in directory
862 * sort order (i.e. first files, then directories, then drives).
864 static INT
LISTBOX_FindFileStrPos( LB_DESCR
*descr
, LPCWSTR str
)
866 INT min
, max
, res
= -1;
868 if (!HAS_STRINGS(descr
))
869 return LISTBOX_FindStringPos( descr
, str
, FALSE
);
871 max
= descr
->nb_items
;
874 INT index
= (min
+ max
) / 2;
875 LPCWSTR p
= descr
->items
[index
].str
;
876 if (*p
== '[') /* drive or directory */
878 if (*str
!= '[') res
= -1;
879 else if (p
[1] == '-') /* drive */
881 if (str
[1] == '-') res
= str
[2] - p
[2];
886 if (str
[1] == '-') res
= 1;
887 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
892 if (*str
== '[') res
= 1;
893 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
895 if (!res
) return index
;
896 if (res
< 0) max
= index
;
897 else min
= index
+ 1;
903 /***********************************************************************
906 * Find the item beginning with a given string.
908 static INT
LISTBOX_FindString( LB_DESCR
*descr
, INT start
, LPCWSTR str
, BOOL exact
)
913 if (start
>= descr
->nb_items
) start
= -1;
914 item
= descr
->items
+ start
+ 1;
915 if (HAS_STRINGS(descr
))
917 if (!str
|| ! str
[0] ) return LB_ERR
;
920 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
921 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
922 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
923 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
927 /* Special case for drives and directories: ignore prefix */
928 #define CHECK_DRIVE(item) \
929 if ((item)->str[0] == '[') \
931 if (!strncmpiW( str, (item)->str+1, len )) return i; \
932 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
936 INT len
= strlenW(str
);
937 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
939 if (!strncmpiW( str
, item
->str
, len
)) return i
;
942 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
944 if (!strncmpiW( str
, item
->str
, len
)) return i
;
952 if (exact
&& (descr
->style
& LBS_SORT
))
953 /* If sorted, use a WM_COMPAREITEM binary search */
954 return LISTBOX_FindStringPos( descr
, str
, TRUE
);
956 /* Otherwise use a linear search */
957 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
958 if (item
->data
== (ULONG_PTR
)str
) return i
;
959 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
960 if (item
->data
== (ULONG_PTR
)str
) return i
;
966 /***********************************************************************
967 * LISTBOX_GetSelCount
969 static LRESULT
LISTBOX_GetSelCount( LB_DESCR
*descr
)
972 LB_ITEMDATA
*item
= descr
->items
;
974 if (!(descr
->style
& LBS_MULTIPLESEL
) ||
975 (descr
->style
& LBS_NOSEL
))
977 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
978 if (item
->selected
) count
++;
983 /***********************************************************************
984 * LISTBOX_GetSelItems16
986 static LRESULT
LISTBOX_GetSelItems16( LB_DESCR
*descr
, INT16 max
, LPINT16 array
)
989 LB_ITEMDATA
*item
= descr
->items
;
991 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
992 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
993 if (item
->selected
) array
[count
++] = (INT16
)i
;
998 /***********************************************************************
999 * LISTBOX_GetSelItems
1001 static LRESULT
LISTBOX_GetSelItems( LB_DESCR
*descr
, INT max
, LPINT array
)
1004 LB_ITEMDATA
*item
= descr
->items
;
1006 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1007 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
1008 if (item
->selected
) array
[count
++] = i
;
1013 /***********************************************************************
1016 static LRESULT
LISTBOX_Paint( LB_DESCR
*descr
, HDC hdc
)
1018 INT i
, col_pos
= descr
->page_size
- 1;
1020 RECT focusRect
= {-1, -1, -1, -1};
1022 HBRUSH hbrush
, oldBrush
= 0;
1024 if (descr
->style
& LBS_NOREDRAW
) return 0;
1026 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1027 if (descr
->style
& LBS_MULTICOLUMN
)
1028 rect
.right
= rect
.left
+ descr
->column_width
;
1029 else if (descr
->horz_pos
)
1031 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1032 rect
.right
+= descr
->horz_pos
;
1035 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1036 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
1037 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
1038 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1039 if (!IsWindowEnabled(descr
->self
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1041 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1044 /* Special case for empty listbox: paint focus rect */
1045 rect
.bottom
= rect
.top
+ descr
->item_height
;
1046 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1047 &rect
, NULL
, 0, NULL
);
1048 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1049 rect
.top
= rect
.bottom
;
1052 /* Paint all the item, regarding the selection
1053 Focus state will be painted after */
1055 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1057 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1058 rect
.bottom
= rect
.top
+ descr
->item_height
;
1060 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1062 if (i
== descr
->focus_item
)
1064 /* keep the focus rect, to paint the focus item after */
1065 focusRect
.left
= rect
.left
;
1066 focusRect
.right
= rect
.right
;
1067 focusRect
.top
= rect
.top
;
1068 focusRect
.bottom
= rect
.bottom
;
1070 LISTBOX_PaintItem( descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1071 rect
.top
= rect
.bottom
;
1073 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1075 if (!IS_OWNERDRAW(descr
))
1077 /* Clear the bottom of the column */
1078 if (rect
.top
< descr
->height
)
1080 rect
.bottom
= descr
->height
;
1081 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1082 &rect
, NULL
, 0, NULL
);
1086 /* Go to the next column */
1087 rect
.left
+= descr
->column_width
;
1088 rect
.right
+= descr
->column_width
;
1090 col_pos
= descr
->page_size
- 1;
1095 if (rect
.top
>= descr
->height
) break;
1099 /* Paint the focus item now */
1100 if (focusRect
.top
!= focusRect
.bottom
&&
1101 descr
->caret_on
&& descr
->in_focus
)
1102 LISTBOX_PaintItem( descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1104 if (!IS_OWNERDRAW(descr
))
1106 /* Clear the remainder of the client area */
1107 if (rect
.top
< descr
->height
)
1109 rect
.bottom
= descr
->height
;
1110 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1111 &rect
, NULL
, 0, NULL
);
1113 if (rect
.right
< descr
->width
)
1115 rect
.left
= rect
.right
;
1116 rect
.right
= descr
->width
;
1118 rect
.bottom
= descr
->height
;
1119 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1120 &rect
, NULL
, 0, NULL
);
1123 if (oldFont
) SelectObject( hdc
, oldFont
);
1124 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1129 /***********************************************************************
1130 * LISTBOX_InvalidateItems
1132 * Invalidate all items from a given item. If the specified item is not
1133 * visible, nothing happens.
1135 static void LISTBOX_InvalidateItems( LB_DESCR
*descr
, INT index
)
1139 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1141 if (descr
->style
& LBS_NOREDRAW
)
1143 descr
->style
|= LBS_DISPLAYCHANGED
;
1146 rect
.bottom
= descr
->height
;
1147 InvalidateRect( descr
->self
, &rect
, TRUE
);
1148 if (descr
->style
& LBS_MULTICOLUMN
)
1150 /* Repaint the other columns */
1151 rect
.left
= rect
.right
;
1152 rect
.right
= descr
->width
;
1154 InvalidateRect( descr
->self
, &rect
, TRUE
);
1159 static void LISTBOX_InvalidateItemRect( LB_DESCR
*descr
, INT index
)
1163 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1164 InvalidateRect( descr
->self
, &rect
, TRUE
);
1167 /***********************************************************************
1168 * LISTBOX_GetItemHeight
1170 static LRESULT
LISTBOX_GetItemHeight( LB_DESCR
*descr
, INT index
)
1172 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1174 if ((index
< 0) || (index
>= descr
->nb_items
))
1176 SetLastError(ERROR_INVALID_INDEX
);
1179 return descr
->items
[index
].height
;
1181 else return descr
->item_height
;
1185 /***********************************************************************
1186 * LISTBOX_SetItemHeight
1188 static LRESULT
LISTBOX_SetItemHeight( LB_DESCR
*descr
, INT index
, INT height
, BOOL repaint
)
1190 if (height
> MAXBYTE
)
1193 if (!height
) height
= 1;
1195 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1197 if ((index
< 0) || (index
>= descr
->nb_items
))
1199 SetLastError(ERROR_INVALID_INDEX
);
1202 TRACE("[%p]: item %d height = %d\n", descr
->self
, index
, height
);
1203 descr
->items
[index
].height
= height
;
1204 LISTBOX_UpdateScroll( descr
);
1206 LISTBOX_InvalidateItems( descr
, index
);
1208 else if (height
!= descr
->item_height
)
1210 TRACE("[%p]: new height = %d\n", descr
->self
, height
);
1211 descr
->item_height
= height
;
1212 LISTBOX_UpdatePage( descr
);
1213 LISTBOX_UpdateScroll( descr
);
1215 InvalidateRect( descr
->self
, 0, TRUE
);
1221 /***********************************************************************
1222 * LISTBOX_SetHorizontalPos
1224 static void LISTBOX_SetHorizontalPos( LB_DESCR
*descr
, INT pos
)
1228 if (pos
> descr
->horz_extent
- descr
->width
)
1229 pos
= descr
->horz_extent
- descr
->width
;
1230 if (pos
< 0) pos
= 0;
1231 if (!(diff
= descr
->horz_pos
- pos
)) return;
1232 TRACE("[%p]: new horz pos = %d\n", descr
->self
, pos
);
1233 descr
->horz_pos
= pos
;
1234 LISTBOX_UpdateScroll( descr
);
1235 if (abs(diff
) < descr
->width
)
1238 /* Invalidate the focused item so it will be repainted correctly */
1239 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1240 InvalidateRect( descr
->self
, &rect
, TRUE
);
1241 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
1242 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1245 InvalidateRect( descr
->self
, NULL
, TRUE
);
1249 /***********************************************************************
1250 * LISTBOX_SetHorizontalExtent
1252 static LRESULT
LISTBOX_SetHorizontalExtent( LB_DESCR
*descr
, INT extent
)
1254 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1256 if (extent
<= 0) extent
= 1;
1257 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1258 TRACE("[%p]: new horz extent = %d\n", descr
->self
, extent
);
1259 descr
->horz_extent
= extent
;
1260 if (descr
->horz_pos
> extent
- descr
->width
)
1261 LISTBOX_SetHorizontalPos( descr
, extent
- descr
->width
);
1263 LISTBOX_UpdateScroll( descr
);
1268 /***********************************************************************
1269 * LISTBOX_SetColumnWidth
1271 static LRESULT
LISTBOX_SetColumnWidth( LB_DESCR
*descr
, INT width
)
1273 if (width
== descr
->column_width
) return LB_OKAY
;
1274 TRACE("[%p]: new column width = %d\n", descr
->self
, width
);
1275 descr
->column_width
= width
;
1276 LISTBOX_UpdatePage( descr
);
1281 /***********************************************************************
1284 * Returns the item height.
1286 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1290 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1295 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1297 ERR("unable to get DC.\n" );
1300 if (font
) oldFont
= SelectObject( hdc
, font
);
1301 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1302 if (oldFont
) SelectObject( hdc
, oldFont
);
1303 ReleaseDC( descr
->self
, hdc
);
1305 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1306 if (!IS_OWNERDRAW(descr
))
1307 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1312 /***********************************************************************
1313 * LISTBOX_MakeItemVisible
1315 * Make sure that a given item is partially or fully visible.
1317 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1321 if (index
<= descr
->top_item
) top
= index
;
1322 else if (descr
->style
& LBS_MULTICOLUMN
)
1324 INT cols
= descr
->width
;
1325 if (!fully
) cols
+= descr
->column_width
- 1;
1326 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1328 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1329 top
= index
- descr
->page_size
* (cols
- 1);
1331 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1333 INT height
= fully
? descr
->items
[index
].height
: 1;
1334 for (top
= index
; top
> descr
->top_item
; top
--)
1335 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1339 if (index
< descr
->top_item
+ descr
->page_size
) return;
1340 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1341 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1342 top
= index
- descr
->page_size
+ 1;
1344 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1347 /***********************************************************************
1348 * LISTBOX_SetCaretIndex
1351 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1354 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1356 INT oldfocus
= descr
->focus_item
;
1358 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1359 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1360 if (index
== oldfocus
) return LB_OKAY
;
1361 descr
->focus_item
= index
;
1362 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1363 LISTBOX_RepaintItem( descr
, oldfocus
, ODA_FOCUS
);
1365 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1366 if (descr
->caret_on
&& (descr
->in_focus
))
1367 LISTBOX_RepaintItem( descr
, index
, ODA_FOCUS
);
1373 /***********************************************************************
1374 * LISTBOX_SelectItemRange
1376 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1378 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1383 /* A few sanity checks */
1385 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1386 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1388 if (!descr
->nb_items
) return LB_OKAY
;
1390 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1391 if (first
< 0) first
= 0;
1392 if (last
< first
) return LB_OKAY
;
1394 if (on
) /* Turn selection on */
1396 for (i
= first
; i
<= last
; i
++)
1398 if (descr
->items
[i
].selected
) continue;
1399 descr
->items
[i
].selected
= TRUE
;
1400 LISTBOX_InvalidateItemRect(descr
, i
);
1403 else /* Turn selection off */
1405 for (i
= first
; i
<= last
; i
++)
1407 if (!descr
->items
[i
].selected
) continue;
1408 descr
->items
[i
].selected
= FALSE
;
1409 LISTBOX_InvalidateItemRect(descr
, i
);
1415 /***********************************************************************
1416 * LISTBOX_SetSelection
1418 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1419 BOOL on
, BOOL send_notify
)
1421 TRACE( "index=%d notify=%s\n", index
, send_notify
? "YES" : "NO" );
1423 if (descr
->style
& LBS_NOSEL
)
1425 descr
->selected_item
= index
;
1428 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1429 if (descr
->style
& LBS_MULTIPLESEL
)
1431 if (index
== -1) /* Select all items */
1432 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1433 else /* Only one item */
1434 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1438 INT oldsel
= descr
->selected_item
;
1439 if (index
== oldsel
) return LB_OKAY
;
1440 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1441 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1442 descr
->selected_item
= index
;
1443 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1444 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1445 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1446 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1448 if( descr
->lphc
) /* set selection change flag for parent combo */
1449 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1455 /***********************************************************************
1458 * Change the caret position and extend the selection to the new caret.
1460 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1462 INT oldfocus
= descr
->focus_item
;
1464 if ((index
< 0) || (index
>= descr
->nb_items
))
1467 /* Important, repaint needs to be done in this order if
1468 you want to mimic Windows behavior:
1469 1. Remove the focus and paint the item
1470 2. Remove the selection and paint the item(s)
1471 3. Set the selection and repaint the item(s)
1472 4. Set the focus to 'index' and repaint the item */
1474 /* 1. remove the focus and repaint the item */
1475 descr
->focus_item
= -1;
1476 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1477 LISTBOX_RepaintItem( descr
, oldfocus
, ODA_FOCUS
);
1479 /* 2. then turn off the previous selection */
1480 /* 3. repaint the new selected item */
1481 if (descr
->style
& LBS_EXTENDEDSEL
)
1483 if (descr
->anchor_item
!= -1)
1485 INT first
= min( index
, descr
->anchor_item
);
1486 INT last
= max( index
, descr
->anchor_item
);
1488 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1489 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1490 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1493 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1495 /* Set selection to new caret item */
1496 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1499 /* 4. repaint the new item with the focus */
1500 descr
->focus_item
= index
;
1501 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1502 if (descr
->caret_on
&& (descr
->in_focus
))
1503 LISTBOX_RepaintItem( descr
, index
, ODA_FOCUS
);
1507 /***********************************************************************
1508 * LISTBOX_InsertItem
1510 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1511 LPWSTR str
, ULONG_PTR data
)
1515 INT oldfocus
= descr
->focus_item
;
1517 if (index
== -1) index
= descr
->nb_items
;
1518 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1519 if (!descr
->items
) max_items
= 0;
1520 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1521 if (descr
->nb_items
== max_items
)
1523 /* We need to grow the array */
1524 max_items
+= LB_ARRAY_GRANULARITY
;
1526 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1527 max_items
* sizeof(LB_ITEMDATA
) );
1529 item
= HeapAlloc( GetProcessHeap(), 0,
1530 max_items
* sizeof(LB_ITEMDATA
) );
1533 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1536 descr
->items
= item
;
1539 /* Insert the item structure */
1541 item
= &descr
->items
[index
];
1542 if (index
< descr
->nb_items
)
1543 RtlMoveMemory( item
+ 1, item
,
1544 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1548 item
->selected
= FALSE
;
1551 /* Get item height */
1553 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1555 MEASUREITEMSTRUCT mis
;
1556 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1558 mis
.CtlType
= ODT_LISTBOX
;
1561 mis
.itemData
= descr
->items
[index
].data
;
1562 mis
.itemHeight
= descr
->item_height
;
1563 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1564 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1565 TRACE("[%p]: measure item %d (%s) = %d\n",
1566 descr
->self
, index
, str
? debugstr_w(str
) : "", item
->height
);
1569 /* Repaint the items */
1571 LISTBOX_UpdateScroll( descr
);
1572 LISTBOX_InvalidateItems( descr
, index
);
1574 /* Move selection and focused item */
1575 /* If listbox was empty, set focus to the first item */
1576 if (descr
->nb_items
== 1)
1577 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1578 /* single select don't change selection index in win31 */
1579 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1581 descr
->selected_item
++;
1582 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1586 if (index
<= descr
->selected_item
)
1588 descr
->selected_item
++;
1589 descr
->focus_item
= oldfocus
; /* focus not changed */
1596 /***********************************************************************
1597 * LISTBOX_InsertString
1599 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
, LPCWSTR str
)
1601 LPWSTR new_str
= NULL
;
1605 if (HAS_STRINGS(descr
))
1607 static const WCHAR empty_stringW
[] = { 0 };
1608 if (!str
) str
= empty_stringW
;
1609 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1611 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1614 strcpyW(new_str
, str
);
1616 else data
= (ULONG_PTR
)str
;
1618 if (index
== -1) index
= descr
->nb_items
;
1619 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, data
)) != 0)
1621 HeapFree( GetProcessHeap(), 0, new_str
);
1625 TRACE("[%p]: added item %d %s\n",
1626 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1631 /***********************************************************************
1632 * LISTBOX_DeleteItem
1634 * Delete the content of an item. 'index' must be a valid index.
1636 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1638 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1639 * while Win95 sends it for all items with user data.
1640 * It's probably better to send it too often than not
1641 * often enough, so this is what we do here.
1643 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1645 DELETEITEMSTRUCT dis
;
1646 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1648 dis
.CtlType
= ODT_LISTBOX
;
1651 dis
.hwndItem
= descr
->self
;
1652 dis
.itemData
= descr
->items
[index
].data
;
1653 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1655 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1656 HeapFree( GetProcessHeap(), 0, descr
->items
[index
].str
);
1660 /***********************************************************************
1661 * LISTBOX_RemoveItem
1663 * Remove an item from the listbox and delete its content.
1665 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1670 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1672 /* We need to invalidate the original rect instead of the updated one. */
1673 LISTBOX_InvalidateItems( descr
, index
);
1675 LISTBOX_DeleteItem( descr
, index
);
1677 /* Remove the item */
1679 item
= &descr
->items
[index
];
1680 if (index
< descr
->nb_items
-1)
1681 RtlMoveMemory( item
, item
+ 1,
1682 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1684 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1686 /* Shrink the item array if possible */
1688 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1689 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1691 max_items
-= LB_ARRAY_GRANULARITY
;
1692 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1693 max_items
* sizeof(LB_ITEMDATA
) );
1694 if (item
) descr
->items
= item
;
1696 /* Repaint the items */
1698 LISTBOX_UpdateScroll( descr
);
1699 /* if we removed the scrollbar, reset the top of the list
1700 (correct for owner-drawn ???) */
1701 if (descr
->nb_items
== descr
->page_size
)
1702 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1704 /* Move selection and focused item */
1705 if (!IS_MULTISELECT(descr
))
1707 if (index
== descr
->selected_item
)
1708 descr
->selected_item
= -1;
1709 else if (index
< descr
->selected_item
)
1711 descr
->selected_item
--;
1712 if (ISWIN31
) /* win 31 do not change the selected item number */
1713 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1717 if (descr
->focus_item
>= descr
->nb_items
)
1719 descr
->focus_item
= descr
->nb_items
- 1;
1720 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1726 /***********************************************************************
1727 * LISTBOX_ResetContent
1729 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1733 for(i
= descr
->nb_items
- 1; i
>=0; i
--) LISTBOX_DeleteItem( descr
, i
);
1734 HeapFree( GetProcessHeap(), 0, descr
->items
);
1735 descr
->nb_items
= 0;
1736 descr
->top_item
= 0;
1737 descr
->selected_item
= -1;
1738 descr
->focus_item
= 0;
1739 descr
->anchor_item
= -1;
1740 descr
->items
= NULL
;
1744 /***********************************************************************
1747 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, INT count
)
1751 if (HAS_STRINGS(descr
))
1753 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1757 /* FIXME: this is far from optimal... */
1758 if (count
> descr
->nb_items
)
1760 while (count
> descr
->nb_items
)
1761 if ((ret
= LISTBOX_InsertString( descr
, -1, 0 )) < 0)
1764 else if (count
< descr
->nb_items
)
1766 while (count
< descr
->nb_items
)
1767 if ((ret
= LISTBOX_RemoveItem( descr
, (descr
->nb_items
- 1) )) < 0)
1774 /***********************************************************************
1777 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1778 LPCWSTR filespec
, BOOL long_names
)
1781 LRESULT ret
= LB_OKAY
;
1782 WIN32_FIND_DATAW entry
;
1785 /* don't scan directory if we just want drives exclusively */
1786 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1787 /* scan directory */
1788 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1790 int le
= GetLastError();
1791 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1798 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1800 static const WCHAR bracketW
[] = { ']',0 };
1801 static const WCHAR dotW
[] = { '.',0 };
1802 if (!(attrib
& DDL_DIRECTORY
) ||
1803 !strcmpW( entry
.cFileName
, dotW
)) continue;
1805 if (!long_names
&& entry
.cAlternateFileName
[0])
1806 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1808 strcpyW( buffer
+ 1, entry
.cFileName
);
1809 strcatW(buffer
, bracketW
);
1811 else /* not a directory */
1813 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1814 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1816 if ((attrib
& DDL_EXCLUSIVE
) &&
1817 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1820 if (!long_names
&& entry
.cAlternateFileName
[0])
1821 strcpyW( buffer
, entry
.cAlternateFileName
);
1823 strcpyW( buffer
, entry
.cFileName
);
1825 if (!long_names
) CharLowerW( buffer
);
1826 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1827 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1829 } while (FindNextFileW( handle
, &entry
));
1830 FindClose( handle
);
1835 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1837 WCHAR buffer
[] = {'[','-','a','-',']',0};
1838 WCHAR root
[] = {'A',':','\\',0};
1840 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1842 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1843 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1851 /***********************************************************************
1852 * LISTBOX_HandleVScroll
1854 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1858 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1862 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1865 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1868 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1869 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1872 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1873 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1875 case SB_THUMBPOSITION
:
1876 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1879 info
.cbSize
= sizeof(info
);
1880 info
.fMask
= SIF_TRACKPOS
;
1881 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1882 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1885 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1888 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1895 /***********************************************************************
1896 * LISTBOX_HandleHScroll
1898 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1903 if (descr
->style
& LBS_MULTICOLUMN
)
1908 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
1912 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
1916 page
= descr
->width
/ descr
->column_width
;
1917 if (page
< 1) page
= 1;
1918 LISTBOX_SetTopItem( descr
,
1919 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1922 page
= descr
->width
/ descr
->column_width
;
1923 if (page
< 1) page
= 1;
1924 LISTBOX_SetTopItem( descr
,
1925 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1927 case SB_THUMBPOSITION
:
1928 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
1931 info
.cbSize
= sizeof(info
);
1932 info
.fMask
= SIF_TRACKPOS
;
1933 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1934 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
1938 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1941 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1945 else if (descr
->horz_extent
)
1950 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
1953 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
1956 LISTBOX_SetHorizontalPos( descr
,
1957 descr
->horz_pos
- descr
->width
);
1960 LISTBOX_SetHorizontalPos( descr
,
1961 descr
->horz_pos
+ descr
->width
);
1963 case SB_THUMBPOSITION
:
1964 LISTBOX_SetHorizontalPos( descr
, pos
);
1967 info
.cbSize
= sizeof(info
);
1968 info
.fMask
= SIF_TRACKPOS
;
1969 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
1970 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
1973 LISTBOX_SetHorizontalPos( descr
, 0 );
1976 LISTBOX_SetHorizontalPos( descr
,
1977 descr
->horz_extent
- descr
->width
);
1984 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
1986 short gcWheelDelta
= 0;
1987 UINT pulScrollLines
= 3;
1989 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1991 gcWheelDelta
-= delta
;
1993 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
1995 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
1996 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
1997 LISTBOX_SetTopItem( descr
, descr
->top_item
+ cLineScroll
, TRUE
);
2002 /***********************************************************************
2003 * LISTBOX_HandleLButtonDown
2005 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
2007 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2008 TRACE("[%p]: lbuttondown %d,%d item %d\n", descr
->self
, x
, y
, index
);
2009 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2011 if (!descr
->in_focus
)
2013 if( !descr
->lphc
) SetFocus( descr
->self
);
2014 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2017 if (index
== -1) return 0;
2019 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2021 /* we should perhaps make sure that all items are deselected
2022 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2023 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2024 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2027 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2028 if (keys
& MK_CONTROL
)
2030 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2031 LISTBOX_SetSelection( descr
, index
,
2032 !descr
->items
[index
].selected
,
2033 (descr
->style
& LBS_NOTIFY
) != 0);
2037 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2039 if (descr
->style
& LBS_EXTENDEDSEL
)
2041 LISTBOX_SetSelection( descr
, index
,
2042 descr
->items
[index
].selected
,
2043 (descr
->style
& LBS_NOTIFY
) != 0 );
2047 LISTBOX_SetSelection( descr
, index
,
2048 !descr
->items
[index
].selected
,
2049 (descr
->style
& LBS_NOTIFY
) != 0 );
2055 descr
->anchor_item
= index
;
2056 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2057 LISTBOX_SetSelection( descr
, index
,
2058 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2061 descr
->captured
= TRUE
;
2062 SetCapture( descr
->self
);
2066 if (descr
->style
& LBS_NOTIFY
)
2067 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2068 MAKELPARAM( x
, y
) );
2069 if (GetWindowLongW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2076 if (DragDetect( descr
->self
, pt
))
2077 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2084 /*************************************************************************
2085 * LISTBOX_HandleLButtonDownCombo [Internal]
2087 * Process LButtonDown message for the ComboListBox
2090 * pWnd [I] The windows internal structure
2091 * pDescr [I] The ListBox internal structure
2092 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2093 * x [I] X Mouse Coordinate
2094 * y [I] Y Mouse Coordinate
2097 * 0 since we are processing the WM_LBUTTONDOWN Message
2100 * This function is only to be used when a ListBox is a ComboListBox
2103 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2105 RECT clientRect
, screenRect
;
2111 GetClientRect(descr
->self
, &clientRect
);
2113 if(PtInRect(&clientRect
, mousePos
))
2115 /* MousePos is in client, resume normal processing */
2116 if (msg
== WM_LBUTTONDOWN
)
2118 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2119 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2121 else if (descr
->style
& LBS_NOTIFY
)
2122 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2126 POINT screenMousePos
;
2127 HWND hWndOldCapture
;
2129 /* Check the Non-Client Area */
2130 screenMousePos
= mousePos
;
2131 hWndOldCapture
= GetCapture();
2133 GetWindowRect(descr
->self
, &screenRect
);
2134 ClientToScreen(descr
->self
, &screenMousePos
);
2136 if(!PtInRect(&screenRect
, screenMousePos
))
2138 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2139 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2140 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2144 /* Check to see the NC is a scrollbar */
2146 LONG style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2147 /* Check Vertical scroll bar */
2148 if (style
& WS_VSCROLL
)
2150 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2151 if (PtInRect( &clientRect
, mousePos
))
2152 nHitTestType
= HTVSCROLL
;
2154 /* Check horizontal scroll bar */
2155 if (style
& WS_HSCROLL
)
2157 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2158 if (PtInRect( &clientRect
, mousePos
))
2159 nHitTestType
= HTHSCROLL
;
2161 /* Windows sends this message when a scrollbar is clicked
2164 if(nHitTestType
!= 0)
2166 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2167 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2169 /* Resume the Capture after scrolling is complete
2171 if(hWndOldCapture
!= 0)
2172 SetCapture(hWndOldCapture
);
2178 /***********************************************************************
2179 * LISTBOX_HandleLButtonUp
2181 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2183 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2184 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2185 LISTBOX_Timer
= LB_TIMER_NONE
;
2186 if (descr
->captured
)
2188 descr
->captured
= FALSE
;
2189 if (GetCapture() == descr
->self
) ReleaseCapture();
2190 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2191 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2197 /***********************************************************************
2198 * LISTBOX_HandleTimer
2200 * Handle scrolling upon a timer event.
2201 * Return TRUE if scrolling should continue.
2203 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2208 if (descr
->top_item
) index
= descr
->top_item
- 1;
2212 if (descr
->top_item
) index
-= descr
->page_size
;
2215 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2216 if (index
== descr
->focus_item
) index
++;
2217 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2219 case LB_TIMER_RIGHT
:
2220 if (index
+ descr
->page_size
< descr
->nb_items
)
2221 index
+= descr
->page_size
;
2226 if (index
== descr
->focus_item
) return FALSE
;
2227 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2232 /***********************************************************************
2233 * LISTBOX_HandleSystemTimer
2235 * WM_SYSTIMER handler.
2237 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2239 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2241 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2242 LISTBOX_Timer
= LB_TIMER_NONE
;
2248 /***********************************************************************
2249 * LISTBOX_HandleMouseMove
2251 * WM_MOUSEMOVE handler.
2253 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2257 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2259 if (!descr
->captured
) return;
2261 if (descr
->style
& LBS_MULTICOLUMN
)
2264 else if (y
>= descr
->item_height
* descr
->page_size
)
2265 y
= descr
->item_height
* descr
->page_size
- 1;
2269 dir
= LB_TIMER_LEFT
;
2272 else if (x
>= descr
->width
)
2274 dir
= LB_TIMER_RIGHT
;
2275 x
= descr
->width
- 1;
2280 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2281 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2284 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2285 if (index
== -1) index
= descr
->focus_item
;
2286 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2288 /* Start/stop the system timer */
2290 if (dir
!= LB_TIMER_NONE
)
2291 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2292 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2293 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2294 LISTBOX_Timer
= dir
;
2298 /***********************************************************************
2299 * LISTBOX_HandleKeyDown
2301 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2304 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2305 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2306 bForceSelection
= FALSE
; /* only for single select list */
2308 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2310 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2311 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2312 (LPARAM
)descr
->self
);
2313 if (caret
== -2) return 0;
2315 if (caret
== -1) switch(key
)
2318 if (descr
->style
& LBS_MULTICOLUMN
)
2320 bForceSelection
= FALSE
;
2321 if (descr
->focus_item
>= descr
->page_size
)
2322 caret
= descr
->focus_item
- descr
->page_size
;
2327 caret
= descr
->focus_item
- 1;
2328 if (caret
< 0) caret
= 0;
2331 if (descr
->style
& LBS_MULTICOLUMN
)
2333 bForceSelection
= FALSE
;
2334 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2335 caret
= descr
->focus_item
+ descr
->page_size
;
2340 caret
= descr
->focus_item
+ 1;
2341 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2345 if (descr
->style
& LBS_MULTICOLUMN
)
2347 INT page
= descr
->width
/ descr
->column_width
;
2348 if (page
< 1) page
= 1;
2349 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2351 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2352 if (caret
< 0) caret
= 0;
2355 if (descr
->style
& LBS_MULTICOLUMN
)
2357 INT page
= descr
->width
/ descr
->column_width
;
2358 if (page
< 1) page
= 1;
2359 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2361 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2362 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2368 caret
= descr
->nb_items
- 1;
2371 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2372 else if (descr
->style
& LBS_MULTIPLESEL
)
2374 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2375 !descr
->items
[descr
->focus_item
].selected
,
2376 (descr
->style
& LBS_NOTIFY
) != 0 );
2380 bForceSelection
= FALSE
;
2382 if (bForceSelection
) /* focused item is used instead of key */
2383 caret
= descr
->focus_item
;
2386 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2387 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2388 !IS_MULTISELECT(descr
))
2389 descr
->anchor_item
= caret
;
2390 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2392 if (descr
->style
& LBS_MULTIPLESEL
)
2393 descr
->selected_item
= caret
;
2395 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2396 if (descr
->style
& LBS_NOTIFY
)
2400 /* make sure that combo parent doesn't hide us */
2401 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2403 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2410 /***********************************************************************
2411 * LISTBOX_HandleChar
2413 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2421 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2423 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2424 MAKEWPARAM(charW
, descr
->focus_item
),
2425 (LPARAM
)descr
->self
);
2426 if (caret
== -2) return 0;
2429 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2432 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2433 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2434 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2435 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2436 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2442 /***********************************************************************
2445 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2448 MEASUREITEMSTRUCT mis
;
2451 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2454 GetClientRect( hwnd
, &rect
);
2456 descr
->owner
= GetParent( descr
->self
);
2457 descr
->style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2458 descr
->width
= rect
.right
- rect
.left
;
2459 descr
->height
= rect
.bottom
- rect
.top
;
2460 descr
->items
= NULL
;
2461 descr
->nb_items
= 0;
2462 descr
->top_item
= 0;
2463 descr
->selected_item
= -1;
2464 descr
->focus_item
= 0;
2465 descr
->anchor_item
= -1;
2466 descr
->item_height
= 1;
2467 descr
->page_size
= 1;
2468 descr
->column_width
= 150;
2469 descr
->horz_extent
= (descr
->style
& WS_HSCROLL
) ? 1 : 0;
2470 descr
->horz_pos
= 0;
2473 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2474 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2475 descr
->in_focus
= FALSE
;
2476 descr
->captured
= FALSE
;
2478 descr
->locale
= GetUserDefaultLCID();
2481 if (is_old_app(descr
) && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2483 /* Win95 document "List Box Differences" from MSDN:
2484 If a list box in a version 3.x application has either the
2485 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2486 horizontal and vertical scroll bars.
2488 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2493 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2494 descr
->owner
= lphc
->self
;
2497 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2499 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2501 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2502 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2503 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2504 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2506 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2508 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2510 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2511 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2515 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2516 mis
.CtlType
= ODT_LISTBOX
;
2521 mis
.itemHeight
= descr
->item_height
;
2522 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2523 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2527 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2532 /***********************************************************************
2535 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2537 LISTBOX_ResetContent( descr
);
2538 SetWindowLongPtrW( descr
->self
, 0, 0 );
2539 HeapFree( GetProcessHeap(), 0, descr
);
2544 /***********************************************************************
2545 * ListBoxWndProc_common
2547 static LRESULT WINAPI
ListBoxWndProc_common( HWND hwnd
, UINT msg
,
2548 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2550 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2551 LPHEADCOMBO lphc
= 0;
2556 if (!IsWindow(hwnd
)) return 0;
2558 if (msg
== WM_CREATE
)
2560 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2561 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= (LPHEADCOMBO
)lpcs
->lpCreateParams
;
2562 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2563 TRACE("creating wnd=%p descr=%lx\n", hwnd
, GetWindowLongPtrW( hwnd
, 0 ) );
2566 /* Ignore all other messages before we get a WM_CREATE */
2567 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2568 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2570 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2572 TRACE("[%p]: msg %s wp %08x lp %08lx\n",
2573 descr
->self
, SPY_GetMsgName(msg
, descr
->self
), wParam
, lParam
);
2577 case LB_RESETCONTENT16
:
2578 case LB_RESETCONTENT
:
2579 LISTBOX_ResetContent( descr
);
2580 LISTBOX_UpdateScroll( descr
);
2581 InvalidateRect( descr
->self
, NULL
, TRUE
);
2584 case LB_ADDSTRING16
:
2585 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2591 if(unicode
|| !HAS_STRINGS(descr
))
2592 textW
= (LPWSTR
)lParam
;
2595 LPSTR textA
= (LPSTR
)lParam
;
2596 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2597 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2598 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2600 wParam
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2601 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2602 if (!unicode
&& HAS_STRINGS(descr
))
2603 HeapFree(GetProcessHeap(), 0, textW
);
2607 case LB_INSERTSTRING16
:
2608 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2609 wParam
= (INT
)(INT16
)wParam
;
2611 case LB_INSERTSTRING
:
2615 if(unicode
|| !HAS_STRINGS(descr
))
2616 textW
= (LPWSTR
)lParam
;
2619 LPSTR textA
= (LPSTR
)lParam
;
2620 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2621 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2622 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2624 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2625 if(!unicode
&& HAS_STRINGS(descr
))
2626 HeapFree(GetProcessHeap(), 0, textW
);
2631 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2637 if(unicode
|| !HAS_STRINGS(descr
))
2638 textW
= (LPWSTR
)lParam
;
2641 LPSTR textA
= (LPSTR
)lParam
;
2642 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2643 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2644 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2646 wParam
= LISTBOX_FindFileStrPos( descr
, textW
);
2647 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2648 if(!unicode
&& HAS_STRINGS(descr
))
2649 HeapFree(GetProcessHeap(), 0, textW
);
2653 case LB_DELETESTRING16
:
2654 case LB_DELETESTRING
:
2655 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2656 return descr
->nb_items
;
2659 SetLastError(ERROR_INVALID_INDEX
);
2663 case LB_GETITEMDATA16
:
2664 case LB_GETITEMDATA
:
2665 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2667 SetLastError(ERROR_INVALID_INDEX
);
2670 return descr
->items
[wParam
].data
;
2672 case LB_SETITEMDATA16
:
2673 case LB_SETITEMDATA
:
2674 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2676 SetLastError(ERROR_INVALID_INDEX
);
2679 descr
->items
[wParam
].data
= lParam
;
2684 return descr
->nb_items
;
2687 lParam
= (LPARAM
)MapSL(lParam
);
2690 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, unicode
);
2692 case LB_GETTEXTLEN16
:
2695 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2697 SetLastError(ERROR_INVALID_INDEX
);
2700 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2701 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2702 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2703 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2705 case LB_GETCURSEL16
:
2707 if (descr
->nb_items
== 0)
2709 if (!IS_MULTISELECT(descr
))
2710 return descr
->selected_item
;
2711 if (descr
->selected_item
!= -1)
2712 return descr
->selected_item
;
2713 return descr
->focus_item
;
2714 /* otherwise, if the user tries to move the selection with the */
2715 /* arrow keys, we will give the application something to choke on */
2716 case LB_GETTOPINDEX16
:
2717 case LB_GETTOPINDEX
:
2718 return descr
->top_item
;
2720 case LB_GETITEMHEIGHT16
:
2721 case LB_GETITEMHEIGHT
:
2722 return LISTBOX_GetItemHeight( descr
, wParam
);
2724 case LB_SETITEMHEIGHT16
:
2725 lParam
= LOWORD(lParam
);
2727 case LB_SETITEMHEIGHT
:
2728 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2730 case LB_ITEMFROMPOINT
:
2735 pt
.x
= LOWORD(lParam
);
2736 pt
.y
= HIWORD(lParam
);
2739 rect
.right
= descr
->width
;
2740 rect
.bottom
= descr
->height
;
2742 return MAKELONG( LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
),
2743 !PtInRect( &rect
, pt
) );
2746 case LB_SETCARETINDEX16
:
2747 case LB_SETCARETINDEX
:
2748 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2749 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2756 case LB_GETCARETINDEX16
:
2757 case LB_GETCARETINDEX
:
2758 return descr
->focus_item
;
2760 case LB_SETTOPINDEX16
:
2761 case LB_SETTOPINDEX
:
2762 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2764 case LB_SETCOLUMNWIDTH16
:
2765 case LB_SETCOLUMNWIDTH
:
2766 return LISTBOX_SetColumnWidth( descr
, wParam
);
2768 case LB_GETITEMRECT16
:
2771 RECT16
*r16
= MapSL(lParam
);
2772 ret
= LISTBOX_GetItemRect( descr
, (INT16
)wParam
, &rect
);
2773 r16
->left
= rect
.left
;
2774 r16
->top
= rect
.top
;
2775 r16
->right
= rect
.right
;
2776 r16
->bottom
= rect
.bottom
;
2780 case LB_GETITEMRECT
:
2781 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2783 case LB_FINDSTRING16
:
2784 wParam
= (INT
)(INT16
)wParam
;
2785 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2791 if(unicode
|| !HAS_STRINGS(descr
))
2792 textW
= (LPWSTR
)lParam
;
2795 LPSTR textA
= (LPSTR
)lParam
;
2796 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2797 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2798 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2800 ret
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2801 if(!unicode
&& HAS_STRINGS(descr
))
2802 HeapFree(GetProcessHeap(), 0, textW
);
2806 case LB_FINDSTRINGEXACT16
:
2807 wParam
= (INT
)(INT16
)wParam
;
2808 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2810 case LB_FINDSTRINGEXACT
:
2814 if(unicode
|| !HAS_STRINGS(descr
))
2815 textW
= (LPWSTR
)lParam
;
2818 LPSTR textA
= (LPSTR
)lParam
;
2819 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2820 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2821 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2823 ret
= LISTBOX_FindString( descr
, wParam
, textW
, TRUE
);
2824 if(!unicode
&& HAS_STRINGS(descr
))
2825 HeapFree(GetProcessHeap(), 0, textW
);
2829 case LB_SELECTSTRING16
:
2830 wParam
= (INT
)(INT16
)wParam
;
2831 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2833 case LB_SELECTSTRING
:
2838 if(HAS_STRINGS(descr
))
2839 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2840 debugstr_a((LPSTR
)lParam
));
2841 if(unicode
|| !HAS_STRINGS(descr
))
2842 textW
= (LPWSTR
)lParam
;
2845 LPSTR textA
= (LPSTR
)lParam
;
2846 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2847 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2848 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2850 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2851 if(!unicode
&& HAS_STRINGS(descr
))
2852 HeapFree(GetProcessHeap(), 0, textW
);
2853 if (index
!= LB_ERR
)
2855 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2856 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2862 wParam
= (INT
)(INT16
)wParam
;
2865 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2867 return descr
->items
[wParam
].selected
;
2870 lParam
= (INT
)(INT16
)lParam
;
2873 return LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2875 case LB_SETCURSEL16
:
2876 wParam
= (INT
)(INT16
)wParam
;
2879 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2880 LISTBOX_SetCaretIndex( descr
, wParam
, TRUE
);
2881 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2882 if (lphc
&& ret
!= LB_ERR
) ret
= descr
->selected_item
;
2885 case LB_GETSELCOUNT16
:
2886 case LB_GETSELCOUNT
:
2887 return LISTBOX_GetSelCount( descr
);
2889 case LB_GETSELITEMS16
:
2890 return LISTBOX_GetSelItems16( descr
, wParam
, (LPINT16
)MapSL(lParam
) );
2892 case LB_GETSELITEMS
:
2893 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2895 case LB_SELITEMRANGE16
:
2896 case LB_SELITEMRANGE
:
2897 if (LOWORD(lParam
) <= HIWORD(lParam
))
2898 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2899 HIWORD(lParam
), wParam
);
2901 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2902 LOWORD(lParam
), wParam
);
2904 case LB_SELITEMRANGEEX16
:
2905 case LB_SELITEMRANGEEX
:
2906 if ((INT
)lParam
>= (INT
)wParam
)
2907 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2909 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
2911 case LB_GETHORIZONTALEXTENT16
:
2912 case LB_GETHORIZONTALEXTENT
:
2913 return descr
->horz_extent
;
2915 case LB_SETHORIZONTALEXTENT16
:
2916 case LB_SETHORIZONTALEXTENT
:
2917 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
2919 case LB_GETANCHORINDEX16
:
2920 case LB_GETANCHORINDEX
:
2921 return descr
->anchor_item
;
2923 case LB_SETANCHORINDEX16
:
2924 wParam
= (INT
)(INT16
)wParam
;
2926 case LB_SETANCHORINDEX
:
2927 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2929 SetLastError(ERROR_INVALID_INDEX
);
2932 descr
->anchor_item
= (INT
)wParam
;
2936 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2937 * be set automatically (this is different in Win32) */
2938 if (wParam
& DDL_DRIVES
) wParam
|= DDL_EXCLUSIVE
;
2939 lParam
= (LPARAM
)MapSL(lParam
);
2946 textW
= (LPWSTR
)lParam
;
2949 LPSTR textA
= (LPSTR
)lParam
;
2950 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2951 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2952 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2954 ret
= LISTBOX_Directory( descr
, wParam
, textW
, msg
== LB_DIR
);
2956 HeapFree(GetProcessHeap(), 0, textW
);
2961 return descr
->locale
;
2966 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
2968 ret
= descr
->locale
;
2969 descr
->locale
= (LCID
)wParam
;
2973 case LB_INITSTORAGE
:
2974 return LISTBOX_InitStorage( descr
, wParam
);
2977 return LISTBOX_SetCount( descr
, (INT
)wParam
);
2979 case LB_SETTABSTOPS16
:
2980 return LISTBOX_SetTabStops( descr
, (INT
)(INT16
)wParam
, MapSL(lParam
), TRUE
);
2982 case LB_SETTABSTOPS
:
2983 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
, FALSE
);
2987 if (descr
->caret_on
)
2989 descr
->caret_on
= TRUE
;
2990 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2991 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2996 if (!descr
->caret_on
)
2998 descr
->caret_on
= FALSE
;
2999 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3000 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3003 case LB_GETLISTBOXINFO
:
3004 FIXME("LB_GETLISTBOXINFO: stub!\n");
3008 return LISTBOX_Destroy( descr
);
3011 InvalidateRect( descr
->self
, NULL
, TRUE
);
3015 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
3019 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
3021 case WM_PRINTCLIENT
:
3025 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
3026 ret
= LISTBOX_Paint( descr
, hdc
);
3027 if( !wParam
) EndPaint( descr
->self
, &ps
);
3031 LISTBOX_UpdateSize( descr
);
3034 return (LRESULT
)descr
->font
;
3036 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
3037 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
3040 descr
->in_focus
= TRUE
;
3041 descr
->caret_on
= TRUE
;
3042 if (descr
->focus_item
!= -1)
3043 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3044 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
3047 descr
->in_focus
= FALSE
;
3048 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
3049 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3050 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
3053 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3055 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3057 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
3058 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
3059 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
3060 case WM_LBUTTONDOWN
:
3062 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3063 (INT16
)LOWORD(lParam
),
3064 (INT16
)HIWORD(lParam
) );
3065 return LISTBOX_HandleLButtonDown( descr
, wParam
,
3066 (INT16
)LOWORD(lParam
),
3067 (INT16
)HIWORD(lParam
) );
3068 case WM_LBUTTONDBLCLK
:
3070 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3071 (INT16
)LOWORD(lParam
),
3072 (INT16
)HIWORD(lParam
) );
3073 if (descr
->style
& LBS_NOTIFY
)
3074 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
3077 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
3079 BOOL captured
= descr
->captured
;
3083 mousePos
.x
= (INT16
)LOWORD(lParam
);
3084 mousePos
.y
= (INT16
)HIWORD(lParam
);
3087 * If we are in a dropdown combobox, we simulate that
3088 * the mouse is captured to show the tracking of the item.
3090 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
3091 descr
->captured
= TRUE
;
3093 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
3095 descr
->captured
= captured
;
3097 else if (GetCapture() == descr
->self
)
3099 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
3100 (INT16
)HIWORD(lParam
) );
3110 * If the mouse button "up" is not in the listbox,
3111 * we make sure there is no selection by re-selecting the
3112 * item that was selected when the listbox was made visible.
3114 mousePos
.x
= (INT16
)LOWORD(lParam
);
3115 mousePos
.y
= (INT16
)HIWORD(lParam
);
3117 GetClientRect(descr
->self
, &clientRect
);
3120 * When the user clicks outside the combobox and the focus
3121 * is lost, the owning combobox will send a fake buttonup with
3122 * 0xFFFFFFF as the mouse location, we must also revert the
3123 * selection to the original selection.
3125 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
3126 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
3128 return LISTBOX_HandleLButtonUp( descr
);
3130 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3132 /* for some reason Windows makes it possible to
3133 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3135 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3136 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3137 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3139 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3143 return LISTBOX_HandleKeyDown( descr
, wParam
);
3148 charW
= (WCHAR
)wParam
;
3151 CHAR charA
= (CHAR
)wParam
;
3152 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
3154 return LISTBOX_HandleChar( descr
, charW
);
3157 return LISTBOX_HandleSystemTimer( descr
);
3159 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3162 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3163 wParam
, (LPARAM
)descr
->self
);
3164 TRACE("hbrush = %p\n", hbrush
);
3166 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3169 GetClientRect(descr
->self
, &rect
);
3170 FillRect((HDC
)wParam
, &rect
, hbrush
);
3175 if( lphc
) return 0;
3176 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3177 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3180 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3189 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3190 WARN("[%p]: unknown msg %04x wp %08x lp %08lx\n",
3191 hwnd
, msg
, wParam
, lParam
);
3194 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3195 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3198 /***********************************************************************
3201 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3203 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3206 /***********************************************************************
3209 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3211 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);