4 * Copyright 1996 Alexandre Julliard
5 * Copyright 2005 Frank Richter
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
36 #include "wine/unicode.h"
37 #include "wine/exception.h"
38 #include "wine/debug.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(listbox2
);
44 /* Items array granularity */
45 #define LB_ARRAY_GRANULARITY 16
47 /* Scrolling timeout in ms */
48 #define LB_SCROLL_TIMEOUT 50
50 /* Listbox system timer id */
53 /* flag listbox changed while setredraw false - internal style */
54 #define LBS_DISPLAYCHANGED 0x80000000
59 LPWSTR str
; /* Item text */
60 BOOL selected
; /* Is item selected? */
61 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
62 ULONG_PTR data
; /* User data */
65 /* Listbox structure */
68 HWND self
; /* Our own window handle */
69 HWND owner
; /* Owner window to send notifications to */
70 UINT style
; /* Window style */
71 INT width
; /* Window width */
72 INT height
; /* Window height */
73 LB_ITEMDATA
*items
; /* Array of items */
74 INT nb_items
; /* Number of items */
75 INT top_item
; /* Top visible item */
76 INT selected_item
; /* Selected item */
77 INT focus_item
; /* Item that has the focus */
78 INT anchor_item
; /* Anchor item for extended selection */
79 INT item_height
; /* Default item height */
80 INT page_size
; /* Items per listbox page */
81 INT column_width
; /* Column width for multi-column listboxes */
82 INT horz_extent
; /* Horizontal extent */
83 INT horz_pos
; /* Horizontal position */
84 INT nb_tabs
; /* Number of tabs in array */
85 INT
*tabs
; /* Array of tabs */
86 INT avg_char_width
; /* Average width of characters */
87 INT wheel_remain
; /* Left over scroll amount */
88 BOOL caret_on
; /* Is caret on? */
89 BOOL captured
; /* Is mouse captured? */
91 HFONT font
; /* Current font */
92 LCID locale
; /* Current locale for string comparisons */
93 HEADCOMBO
*lphc
; /* ComboLBox */
97 #define IS_OWNERDRAW(descr) \
98 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
100 #define HAS_STRINGS(descr) \
101 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
104 #define IS_MULTISELECT(descr) \
105 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
106 !((descr)->style & LBS_NOSEL))
108 #define SEND_NOTIFICATION(descr,code) \
109 (SendMessageW( (descr)->owner, WM_COMMAND, \
110 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
112 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
114 /* Current timer status */
124 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
126 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
);
128 /***********************************************************************
129 * LISTBOX_GetCurrentPageSize
131 * Return the current page size
133 static INT
LISTBOX_GetCurrentPageSize( const LB_DESCR
*descr
)
136 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
137 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
139 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
141 if (i
== descr
->top_item
) return 1;
142 else return i
- descr
->top_item
;
146 /***********************************************************************
147 * LISTBOX_GetMaxTopIndex
149 * Return the maximum possible index for the top of the listbox.
151 static INT
LISTBOX_GetMaxTopIndex( const LB_DESCR
*descr
)
155 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
157 page
= descr
->height
;
158 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
159 if ((page
-= descr
->items
[max
].height
) < 0) break;
160 if (max
< descr
->nb_items
- 1) max
++;
162 else if (descr
->style
& LBS_MULTICOLUMN
)
164 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
165 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
166 max
= (max
- page
) * descr
->page_size
;
170 max
= descr
->nb_items
- descr
->page_size
;
172 if (max
< 0) max
= 0;
177 /***********************************************************************
178 * LISTBOX_UpdateScroll
180 * Update the scrollbars. Should be called whenever the content
181 * of the listbox changes.
183 static void LISTBOX_UpdateScroll( LB_DESCR
*descr
)
187 /* Check the listbox scroll bar flags individually before we call
188 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
189 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
190 scroll bar when we do not need one.
191 if (!(descr->style & WS_VSCROLL)) return;
194 /* It is important that we check descr->style, and not wnd->dwStyle,
195 for WS_VSCROLL, as the former is exactly the one passed in
196 argument to CreateWindow.
197 In Windows (and from now on in Wine :) a listbox created
198 with such a style (no WS_SCROLL) does not update
199 the scrollbar with listbox-related data, thus letting
200 the programmer use it for his/her own purposes. */
202 if (descr
->style
& LBS_NOREDRAW
) return;
203 info
.cbSize
= sizeof(info
);
205 if (descr
->style
& LBS_MULTICOLUMN
)
208 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
209 info
.nPos
= descr
->top_item
/ descr
->page_size
;
210 info
.nPage
= descr
->width
/ descr
->column_width
;
211 if (info
.nPage
< 1) info
.nPage
= 1;
212 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
213 if (descr
->style
& LBS_DISABLENOSCROLL
)
214 info
.fMask
|= SIF_DISABLENOSCROLL
;
215 if (descr
->style
& WS_HSCROLL
)
216 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
218 info
.fMask
= SIF_RANGE
;
219 if (descr
->style
& WS_VSCROLL
)
220 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
225 info
.nMax
= descr
->nb_items
- 1;
226 info
.nPos
= descr
->top_item
;
227 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
228 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
229 if (descr
->style
& LBS_DISABLENOSCROLL
)
230 info
.fMask
|= SIF_DISABLENOSCROLL
;
231 if (descr
->style
& WS_VSCROLL
)
232 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
234 if ((descr
->style
& WS_HSCROLL
) && descr
->horz_extent
)
236 info
.nPos
= descr
->horz_pos
;
237 info
.nPage
= descr
->width
;
238 info
.fMask
= SIF_POS
| SIF_PAGE
;
239 if (descr
->style
& LBS_DISABLENOSCROLL
)
240 info
.fMask
|= SIF_DISABLENOSCROLL
;
241 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
245 if (descr
->style
& LBS_DISABLENOSCROLL
)
249 info
.fMask
= SIF_RANGE
| SIF_DISABLENOSCROLL
;
250 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
254 ShowScrollBar( descr
->self
, SB_HORZ
, FALSE
);
261 /***********************************************************************
264 * Set the top item of the listbox, scrolling up or down if necessary.
266 static LRESULT
LISTBOX_SetTopItem( LB_DESCR
*descr
, INT index
, BOOL scroll
)
268 INT max
= LISTBOX_GetMaxTopIndex( descr
);
270 TRACE("setting top item %d, scroll %d\n", index
, scroll
);
272 if (index
> max
) index
= max
;
273 if (index
< 0) index
= 0;
274 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
275 if (descr
->top_item
== index
) return LB_OKAY
;
279 if (descr
->style
& LBS_MULTICOLUMN
)
280 diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
281 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
285 if (index
> descr
->top_item
)
287 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
288 diff
-= descr
->items
[i
].height
;
292 for (i
= index
; i
< descr
->top_item
; i
++)
293 diff
+= descr
->items
[i
].height
;
297 diff
= (descr
->top_item
- index
) * descr
->item_height
;
299 ScrollWindowEx( descr
->self
, 0, diff
, NULL
, NULL
, 0, NULL
,
300 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
303 InvalidateRect( descr
->self
, NULL
, TRUE
);
304 descr
->top_item
= index
;
305 LISTBOX_UpdateScroll( descr
);
310 /***********************************************************************
313 * Update the page size. Should be called when the size of
314 * the client area or the item height changes.
316 static void LISTBOX_UpdatePage( LB_DESCR
*descr
)
320 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
322 if (page_size
== descr
->page_size
) return;
323 descr
->page_size
= page_size
;
324 if (descr
->style
& LBS_MULTICOLUMN
)
325 InvalidateRect( descr
->self
, NULL
, TRUE
);
326 LISTBOX_SetTopItem( descr
, descr
->top_item
, FALSE
);
330 /***********************************************************************
333 * Update the size of the listbox. Should be called when the size of
334 * the client area changes.
336 static void LISTBOX_UpdateSize( LB_DESCR
*descr
)
340 GetClientRect( descr
->self
, &rect
);
341 descr
->width
= rect
.right
- rect
.left
;
342 descr
->height
= rect
.bottom
- rect
.top
;
343 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
348 GetWindowRect( descr
->self
, &rect
);
349 if(descr
->item_height
!= 0)
350 remaining
= descr
->height
% descr
->item_height
;
353 if ((descr
->height
> descr
->item_height
) && remaining
)
355 TRACE("[%p]: changing height %d -> %d\n",
356 descr
->self
, descr
->height
, descr
->height
- remaining
);
357 SetWindowPos( descr
->self
, 0, 0, 0, rect
.right
- rect
.left
,
358 rect
.bottom
- rect
.top
- remaining
,
359 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
363 TRACE("[%p]: new size = %d,%d\n", descr
->self
, descr
->width
, descr
->height
);
364 LISTBOX_UpdatePage( descr
);
365 LISTBOX_UpdateScroll( descr
);
367 /* Invalidate the focused item so it will be repainted correctly */
368 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
370 InvalidateRect( descr
->self
, &rect
, FALSE
);
375 /***********************************************************************
376 * LISTBOX_GetItemRect
378 * Get the rectangle enclosing an item, in listbox client coordinates.
379 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
381 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
)
383 /* Index <= 0 is legal even on empty listboxes */
384 if (index
&& (index
>= descr
->nb_items
))
387 SetLastError(ERROR_INVALID_INDEX
);
390 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
391 if (descr
->style
& LBS_MULTICOLUMN
)
393 INT col
= (index
/ descr
->page_size
) -
394 (descr
->top_item
/ descr
->page_size
);
395 rect
->left
+= col
* descr
->column_width
;
396 rect
->right
= rect
->left
+ descr
->column_width
;
397 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
398 rect
->bottom
= rect
->top
+ descr
->item_height
;
400 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
403 rect
->right
+= descr
->horz_pos
;
404 if ((index
>= 0) && (index
< descr
->nb_items
))
406 if (index
< descr
->top_item
)
408 for (i
= descr
->top_item
-1; i
>= index
; i
--)
409 rect
->top
-= descr
->items
[i
].height
;
413 for (i
= descr
->top_item
; i
< index
; i
++)
414 rect
->top
+= descr
->items
[i
].height
;
416 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
422 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
423 rect
->bottom
= rect
->top
+ descr
->item_height
;
424 rect
->right
+= descr
->horz_pos
;
427 TRACE("item %d, rect %s\n", index
, wine_dbgstr_rect(rect
));
429 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
430 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
434 /***********************************************************************
435 * LISTBOX_GetItemFromPoint
437 * Return the item nearest from point (x,y) (in client coordinates).
439 static INT
LISTBOX_GetItemFromPoint( const LB_DESCR
*descr
, INT x
, INT y
)
441 INT index
= descr
->top_item
;
443 if (!descr
->nb_items
) return -1; /* No items */
444 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
449 while (index
< descr
->nb_items
)
451 if ((pos
+= descr
->items
[index
].height
) > y
) break;
460 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
464 else if (descr
->style
& LBS_MULTICOLUMN
)
466 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
467 if (y
>= 0) index
+= y
/ descr
->item_height
;
468 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
469 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
473 index
+= (y
/ descr
->item_height
);
475 if (index
< 0) return 0;
476 if (index
>= descr
->nb_items
) return -1;
481 /***********************************************************************
486 static void LISTBOX_PaintItem( LB_DESCR
*descr
, HDC hdc
, const RECT
*rect
,
487 INT index
, UINT action
, BOOL ignoreFocus
)
489 LB_ITEMDATA
*item
= NULL
;
490 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
492 if (IS_OWNERDRAW(descr
))
500 if (action
== ODA_FOCUS
)
501 DrawFocusRect( hdc
, rect
);
503 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
507 /* some programs mess with the clipping region when
508 drawing the item, *and* restore the previous region
509 after they are done, so a region has better to exist
510 else everything ends clipped */
511 GetClientRect(descr
->self
, &r
);
512 hrgn
= set_control_clipping( hdc
, &r
);
514 dis
.CtlType
= ODT_LISTBOX
;
515 dis
.CtlID
= GetWindowLongPtrW( descr
->self
, GWLP_ID
);
516 dis
.hwndItem
= descr
->self
;
517 dis
.itemAction
= action
;
521 if (item
->selected
) dis
.itemState
|= ODS_SELECTED
;
522 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
524 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
525 if (!IsWindowEnabled(descr
->self
)) dis
.itemState
|= ODS_DISABLED
;
526 dis
.itemData
= item
->data
;
528 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
529 descr
->self
, index
, debugstr_w(item
->str
), action
,
530 dis
.itemState
, wine_dbgstr_rect(rect
) );
531 SendMessageW(descr
->owner
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
532 SelectClipRgn( hdc
, hrgn
);
533 if (hrgn
) DeleteObject( hrgn
);
537 COLORREF oldText
= 0, oldBk
= 0;
539 if (action
== ODA_FOCUS
)
541 DrawFocusRect( hdc
, rect
);
544 if (item
&& item
->selected
)
546 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
547 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
550 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
551 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
552 wine_dbgstr_rect(rect
) );
554 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
555 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
556 else if (!(descr
->style
& LBS_USETABSTOPS
))
557 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
558 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
559 strlenW(item
->str
), NULL
);
562 /* Output empty string to paint background in the full width. */
563 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
564 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
565 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
566 item
->str
, strlenW(item
->str
),
567 descr
->nb_tabs
, descr
->tabs
, 0);
569 if (item
&& item
->selected
)
571 SetBkColor( hdc
, oldBk
);
572 SetTextColor( hdc
, oldText
);
574 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
576 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
581 /***********************************************************************
584 * Change the redraw flag.
586 static void LISTBOX_SetRedraw( LB_DESCR
*descr
, BOOL on
)
590 if (!(descr
->style
& LBS_NOREDRAW
)) return;
591 descr
->style
&= ~LBS_NOREDRAW
;
592 if (descr
->style
& LBS_DISPLAYCHANGED
)
593 { /* page was changed while setredraw false, refresh automatically */
594 InvalidateRect(descr
->self
, NULL
, TRUE
);
595 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
596 { /* reset top of page if less than number of items/page */
597 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
598 if (descr
->top_item
< 0) descr
->top_item
= 0;
600 descr
->style
&= ~LBS_DISPLAYCHANGED
;
602 LISTBOX_UpdateScroll( descr
);
604 else descr
->style
|= LBS_NOREDRAW
;
608 /***********************************************************************
609 * LISTBOX_RepaintItem
611 * Repaint a single item synchronously.
613 static void LISTBOX_RepaintItem( LB_DESCR
*descr
, INT index
, UINT action
)
618 HBRUSH hbrush
, oldBrush
= 0;
620 /* Do not repaint the item if the item is not visible */
621 if (!IsWindowVisible(descr
->self
)) return;
622 if (descr
->style
& LBS_NOREDRAW
)
624 descr
->style
|= LBS_DISPLAYCHANGED
;
627 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
628 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
629 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
630 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
631 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
632 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
633 if (!IsWindowEnabled(descr
->self
))
634 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
635 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
636 LISTBOX_PaintItem( descr
, hdc
, &rect
, index
, action
, TRUE
);
637 if (oldFont
) SelectObject( hdc
, oldFont
);
638 if (oldBrush
) SelectObject( hdc
, oldBrush
);
639 ReleaseDC( descr
->self
, hdc
);
643 /***********************************************************************
644 * LISTBOX_DrawFocusRect
646 static void LISTBOX_DrawFocusRect( LB_DESCR
*descr
, BOOL on
)
652 /* Do not repaint the item if the item is not visible */
653 if (!IsWindowVisible(descr
->self
)) return;
655 if (descr
->focus_item
== -1) return;
656 if (!descr
->caret_on
|| !descr
->in_focus
) return;
658 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) != 1) return;
659 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
660 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
661 if (!IsWindowEnabled(descr
->self
))
662 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
663 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
664 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, !on
);
665 if (oldFont
) SelectObject( hdc
, oldFont
);
666 ReleaseDC( descr
->self
, hdc
);
670 /***********************************************************************
671 * LISTBOX_InitStorage
673 static LRESULT
LISTBOX_InitStorage( LB_DESCR
*descr
, INT nb_items
)
677 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
678 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
680 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
681 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
682 nb_items
* sizeof(LB_ITEMDATA
));
685 item
= HeapAlloc( GetProcessHeap(), 0,
686 nb_items
* sizeof(LB_ITEMDATA
));
691 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
699 /***********************************************************************
700 * LISTBOX_SetTabStops
702 static BOOL
LISTBOX_SetTabStops( LB_DESCR
*descr
, INT count
, LPINT tabs
)
706 if (!(descr
->style
& LBS_USETABSTOPS
))
708 SetLastError(ERROR_LB_WITHOUT_TABSTOPS
);
712 HeapFree( GetProcessHeap(), 0, descr
->tabs
);
713 if (!(descr
->nb_tabs
= count
))
718 if (!(descr
->tabs
= HeapAlloc( GetProcessHeap(), 0,
719 descr
->nb_tabs
* sizeof(INT
) )))
721 memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
723 /* convert into "dialog units"*/
724 for (i
= 0; i
< descr
->nb_tabs
; i
++)
725 descr
->tabs
[i
] = MulDiv(descr
->tabs
[i
], descr
->avg_char_width
, 4);
731 /***********************************************************************
734 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPWSTR buffer
, BOOL unicode
)
738 if ((index
< 0) || (index
>= descr
->nb_items
))
740 SetLastError(ERROR_INVALID_INDEX
);
744 if (HAS_STRINGS(descr
))
747 return strlenW(descr
->items
[index
].str
);
749 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
751 __TRY
/* hide a Delphi bug that passes a read-only buffer */
753 strcpyW( buffer
, descr
->items
[index
].str
);
754 len
= strlenW(buffer
);
758 WARN( "got an invalid buffer (Delphi bug?)\n" );
759 SetLastError( ERROR_INVALID_PARAMETER
);
766 *((DWORD
*)buffer
) = *(DWORD
*)&descr
->items
[index
].data
;
772 static inline INT
LISTBOX_lstrcmpiW( LCID lcid
, LPCWSTR str1
, LPCWSTR str2
)
774 INT ret
= CompareStringW( lcid
, NORM_IGNORECASE
, str1
, -1, str2
, -1 );
775 if (ret
== CSTR_LESS_THAN
)
777 if (ret
== CSTR_EQUAL
)
779 if (ret
== CSTR_GREATER_THAN
)
784 /***********************************************************************
785 * LISTBOX_FindStringPos
787 * Find the nearest string located before a given string in sort order.
788 * If 'exact' is TRUE, return an error if we don't get an exact match.
790 static INT
LISTBOX_FindStringPos( LB_DESCR
*descr
, LPCWSTR str
, BOOL exact
)
792 INT index
, min
, max
, res
;
794 if (!descr
->nb_items
|| !(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
797 max
= descr
->nb_items
- 1;
800 index
= (min
+ max
) / 2;
801 if (HAS_STRINGS(descr
))
802 res
= LISTBOX_lstrcmpiW( descr
->locale
, descr
->items
[index
].str
, str
);
805 COMPAREITEMSTRUCT cis
;
806 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
808 cis
.CtlType
= ODT_LISTBOX
;
810 cis
.hwndItem
= descr
->self
;
811 /* note that some application (MetaStock) expects the second item
812 * to be in the listbox */
814 cis
.itemData1
= descr
->items
[index
].data
;
816 cis
.itemData2
= (ULONG_PTR
)str
;
817 cis
.dwLocaleId
= descr
->locale
;
818 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
820 if (!res
) return index
;
821 if (res
> 0) max
= index
- 1;
822 else min
= index
+ 1;
824 return exact
? -1 : min
;
828 /***********************************************************************
829 * LISTBOX_FindFileStrPos
831 * Find the nearest string located before a given string in directory
832 * sort order (i.e. first files, then directories, then drives).
834 static INT
LISTBOX_FindFileStrPos( LB_DESCR
*descr
, LPCWSTR str
)
838 if (!HAS_STRINGS(descr
))
839 return LISTBOX_FindStringPos( descr
, str
, FALSE
);
841 max
= descr
->nb_items
;
844 INT index
= (min
+ max
) / 2;
845 LPCWSTR p
= descr
->items
[index
].str
;
846 if (*p
== '[') /* drive or directory */
848 if (*str
!= '[') res
= -1;
849 else if (p
[1] == '-') /* drive */
851 if (str
[1] == '-') res
= str
[2] - p
[2];
856 if (str
[1] == '-') res
= 1;
857 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
862 if (*str
== '[') res
= 1;
863 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
865 if (!res
) return index
;
866 if (res
< 0) max
= index
;
867 else min
= index
+ 1;
873 /***********************************************************************
876 * Find the item beginning with a given string.
878 static INT
LISTBOX_FindString( LB_DESCR
*descr
, INT start
, LPCWSTR str
, BOOL exact
)
883 if (start
>= descr
->nb_items
) start
= -1;
884 item
= descr
->items
+ start
+ 1;
885 if (HAS_STRINGS(descr
))
887 if (!str
|| ! str
[0] ) return LB_ERR
;
890 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
891 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
892 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
893 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
897 /* Special case for drives and directories: ignore prefix */
898 #define CHECK_DRIVE(item) \
899 if ((item)->str[0] == '[') \
901 if (!strncmpiW( str, (item)->str+1, len )) return i; \
902 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
906 INT len
= strlenW(str
);
907 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
909 if (!strncmpiW( str
, item
->str
, len
)) return i
;
912 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
914 if (!strncmpiW( str
, item
->str
, len
)) return i
;
922 if (exact
&& (descr
->style
& LBS_SORT
))
923 /* If sorted, use a WM_COMPAREITEM binary search */
924 return LISTBOX_FindStringPos( descr
, str
, TRUE
);
926 /* Otherwise use a linear search */
927 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
928 if (item
->data
== (ULONG_PTR
)str
) return i
;
929 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
930 if (item
->data
== (ULONG_PTR
)str
) return i
;
936 /***********************************************************************
937 * LISTBOX_GetSelCount
939 static LRESULT
LISTBOX_GetSelCount( const LB_DESCR
*descr
)
942 const LB_ITEMDATA
*item
= descr
->items
;
944 if (!(descr
->style
& LBS_MULTIPLESEL
) ||
945 (descr
->style
& LBS_NOSEL
))
947 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
948 if (item
->selected
) count
++;
953 /***********************************************************************
954 * LISTBOX_GetSelItems
956 static LRESULT
LISTBOX_GetSelItems( const LB_DESCR
*descr
, INT max
, LPINT array
)
959 const LB_ITEMDATA
*item
= descr
->items
;
961 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
962 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
963 if (item
->selected
) array
[count
++] = i
;
968 /***********************************************************************
971 static LRESULT
LISTBOX_Paint( LB_DESCR
*descr
, HDC hdc
)
973 INT i
, col_pos
= descr
->page_size
- 1;
975 RECT focusRect
= {-1, -1, -1, -1};
977 HBRUSH hbrush
, oldBrush
= 0;
979 if (descr
->style
& LBS_NOREDRAW
) return 0;
981 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
982 if (descr
->style
& LBS_MULTICOLUMN
)
983 rect
.right
= rect
.left
+ descr
->column_width
;
984 else if (descr
->horz_pos
)
986 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
987 rect
.right
+= descr
->horz_pos
;
990 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
991 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
992 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
993 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
994 if (!IsWindowEnabled(descr
->self
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
996 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
999 /* Special case for empty listbox: paint focus rect */
1000 rect
.bottom
= rect
.top
+ descr
->item_height
;
1001 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1002 &rect
, NULL
, 0, NULL
);
1003 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1004 rect
.top
= rect
.bottom
;
1007 /* Paint all the item, regarding the selection
1008 Focus state will be painted after */
1010 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1012 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1013 rect
.bottom
= rect
.top
+ descr
->item_height
;
1015 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1017 /* keep the focus rect, to paint the focus item after */
1018 if (i
== descr
->focus_item
)
1021 LISTBOX_PaintItem( descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1022 rect
.top
= rect
.bottom
;
1024 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1026 if (!IS_OWNERDRAW(descr
))
1028 /* Clear the bottom of the column */
1029 if (rect
.top
< descr
->height
)
1031 rect
.bottom
= descr
->height
;
1032 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1033 &rect
, NULL
, 0, NULL
);
1037 /* Go to the next column */
1038 rect
.left
+= descr
->column_width
;
1039 rect
.right
+= descr
->column_width
;
1041 col_pos
= descr
->page_size
- 1;
1046 if (rect
.top
>= descr
->height
) break;
1050 /* Paint the focus item now */
1051 if (focusRect
.top
!= focusRect
.bottom
&&
1052 descr
->caret_on
&& descr
->in_focus
)
1053 LISTBOX_PaintItem( descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1055 if (!IS_OWNERDRAW(descr
))
1057 /* Clear the remainder of the client area */
1058 if (rect
.top
< descr
->height
)
1060 rect
.bottom
= descr
->height
;
1061 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1062 &rect
, NULL
, 0, NULL
);
1064 if (rect
.right
< descr
->width
)
1066 rect
.left
= rect
.right
;
1067 rect
.right
= descr
->width
;
1069 rect
.bottom
= descr
->height
;
1070 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1071 &rect
, NULL
, 0, NULL
);
1074 if (oldFont
) SelectObject( hdc
, oldFont
);
1075 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1079 static void LISTBOX_NCPaint( LB_DESCR
*descr
, HRGN region
)
1081 DWORD exstyle
= GetWindowLongW( descr
->self
, GWL_EXSTYLE
);
1082 HTHEME theme
= GetWindowTheme( descr
->self
);
1083 HRGN cliprgn
= region
;
1088 if (!theme
|| !(exstyle
& WS_EX_CLIENTEDGE
))
1091 cxEdge
= GetSystemMetrics(SM_CXEDGE
),
1092 cyEdge
= GetSystemMetrics(SM_CYEDGE
);
1094 GetWindowRect(descr
->self
, &r
);
1096 /* New clipping region passed to default proc to exclude border */
1097 cliprgn
= CreateRectRgn(r
.left
+ cxEdge
, r
.top
+ cyEdge
,
1098 r
.right
- cxEdge
, r
.bottom
- cyEdge
);
1099 if (region
!= (HRGN
)1)
1100 CombineRgn(cliprgn
, cliprgn
, region
, RGN_AND
);
1101 OffsetRect(&r
, -r
.left
, -r
.top
);
1103 hdc
= GetDCEx(descr
->self
, region
, DCX_WINDOW
|DCX_INTERSECTRGN
);
1104 OffsetRect(&r
, -r
.left
, -r
.top
);
1106 if (IsThemeBackgroundPartiallyTransparent (theme
, 0, 0))
1107 DrawThemeParentBackground(descr
->self
, hdc
, &r
);
1108 DrawThemeBackground (theme
, hdc
, 0, 0, &r
, 0);
1109 ReleaseDC(descr
->self
, hdc
);
1112 /***********************************************************************
1113 * LISTBOX_InvalidateItems
1115 * Invalidate all items from a given item. If the specified item is not
1116 * visible, nothing happens.
1118 static void LISTBOX_InvalidateItems( LB_DESCR
*descr
, INT index
)
1122 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1124 if (descr
->style
& LBS_NOREDRAW
)
1126 descr
->style
|= LBS_DISPLAYCHANGED
;
1129 rect
.bottom
= descr
->height
;
1130 InvalidateRect( descr
->self
, &rect
, TRUE
);
1131 if (descr
->style
& LBS_MULTICOLUMN
)
1133 /* Repaint the other columns */
1134 rect
.left
= rect
.right
;
1135 rect
.right
= descr
->width
;
1137 InvalidateRect( descr
->self
, &rect
, TRUE
);
1142 static void LISTBOX_InvalidateItemRect( LB_DESCR
*descr
, INT index
)
1146 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1147 InvalidateRect( descr
->self
, &rect
, TRUE
);
1150 /***********************************************************************
1151 * LISTBOX_GetItemHeight
1153 static LRESULT
LISTBOX_GetItemHeight( const LB_DESCR
*descr
, INT index
)
1155 if (descr
->style
& LBS_OWNERDRAWVARIABLE
&& descr
->nb_items
> 0)
1157 if ((index
< 0) || (index
>= descr
->nb_items
))
1159 SetLastError(ERROR_INVALID_INDEX
);
1162 return descr
->items
[index
].height
;
1164 else return descr
->item_height
;
1168 /***********************************************************************
1169 * LISTBOX_SetItemHeight
1171 static LRESULT
LISTBOX_SetItemHeight( LB_DESCR
*descr
, INT index
, INT height
, BOOL repaint
)
1173 if (height
> MAXBYTE
)
1176 if (!height
) height
= 1;
1178 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1180 if ((index
< 0) || (index
>= descr
->nb_items
))
1182 SetLastError(ERROR_INVALID_INDEX
);
1185 TRACE("[%p]: item %d height = %d\n", descr
->self
, index
, height
);
1186 descr
->items
[index
].height
= height
;
1187 LISTBOX_UpdateScroll( descr
);
1189 LISTBOX_InvalidateItems( descr
, index
);
1191 else if (height
!= descr
->item_height
)
1193 TRACE("[%p]: new height = %d\n", descr
->self
, height
);
1194 descr
->item_height
= height
;
1195 LISTBOX_UpdatePage( descr
);
1196 LISTBOX_UpdateScroll( descr
);
1198 InvalidateRect( descr
->self
, 0, TRUE
);
1204 /***********************************************************************
1205 * LISTBOX_SetHorizontalPos
1207 static void LISTBOX_SetHorizontalPos( LB_DESCR
*descr
, INT pos
)
1211 if (pos
> descr
->horz_extent
- descr
->width
)
1212 pos
= descr
->horz_extent
- descr
->width
;
1213 if (pos
< 0) pos
= 0;
1214 if (!(diff
= descr
->horz_pos
- pos
)) return;
1215 TRACE("[%p]: new horz pos = %d\n", descr
->self
, pos
);
1216 descr
->horz_pos
= pos
;
1217 LISTBOX_UpdateScroll( descr
);
1218 if (abs(diff
) < descr
->width
)
1221 /* Invalidate the focused item so it will be repainted correctly */
1222 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1223 InvalidateRect( descr
->self
, &rect
, TRUE
);
1224 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
1225 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1228 InvalidateRect( descr
->self
, NULL
, TRUE
);
1232 /***********************************************************************
1233 * LISTBOX_SetHorizontalExtent
1235 static LRESULT
LISTBOX_SetHorizontalExtent( LB_DESCR
*descr
, INT extent
)
1237 if (descr
->style
& LBS_MULTICOLUMN
)
1239 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1240 TRACE("[%p]: new horz extent = %d\n", descr
->self
, extent
);
1241 descr
->horz_extent
= extent
;
1242 if (descr
->style
& WS_HSCROLL
) {
1244 info
.cbSize
= sizeof(info
);
1246 info
.nMax
= descr
->horz_extent
? descr
->horz_extent
- 1 : 0;
1247 info
.fMask
= SIF_RANGE
;
1248 if (descr
->style
& LBS_DISABLENOSCROLL
)
1249 info
.fMask
|= SIF_DISABLENOSCROLL
;
1250 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
1252 if (descr
->horz_pos
> extent
- descr
->width
)
1253 LISTBOX_SetHorizontalPos( descr
, extent
- descr
->width
);
1258 /***********************************************************************
1259 * LISTBOX_SetColumnWidth
1261 static LRESULT
LISTBOX_SetColumnWidth( LB_DESCR
*descr
, INT width
)
1263 if (width
== descr
->column_width
) return LB_OKAY
;
1264 TRACE("[%p]: new column width = %d\n", descr
->self
, width
);
1265 descr
->column_width
= width
;
1266 LISTBOX_UpdatePage( descr
);
1271 /***********************************************************************
1274 * Returns the item height.
1276 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1280 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1285 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1287 ERR("unable to get DC.\n" );
1290 if (font
) oldFont
= SelectObject( hdc
, font
);
1291 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1292 if (oldFont
) SelectObject( hdc
, oldFont
);
1293 ReleaseDC( descr
->self
, hdc
);
1295 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1296 if (!IS_OWNERDRAW(descr
))
1297 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1302 /***********************************************************************
1303 * LISTBOX_MakeItemVisible
1305 * Make sure that a given item is partially or fully visible.
1307 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1311 TRACE("current top item %d, index %d, fully %d\n", descr
->top_item
, index
, fully
);
1313 if (index
<= descr
->top_item
) top
= index
;
1314 else if (descr
->style
& LBS_MULTICOLUMN
)
1316 INT cols
= descr
->width
;
1317 if (!fully
) cols
+= descr
->column_width
- 1;
1318 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1320 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1321 top
= index
- descr
->page_size
* (cols
- 1);
1323 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1325 INT height
= fully
? descr
->items
[index
].height
: 1;
1326 for (top
= index
; top
> descr
->top_item
; top
--)
1327 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1331 if (index
< descr
->top_item
+ descr
->page_size
) return;
1332 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1333 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1334 top
= index
- descr
->page_size
+ 1;
1336 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1339 /***********************************************************************
1340 * LISTBOX_SetCaretIndex
1343 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1346 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1348 INT oldfocus
= descr
->focus_item
;
1350 TRACE("old focus %d, index %d\n", oldfocus
, index
);
1352 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1353 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1354 if (index
== oldfocus
) return LB_OKAY
;
1356 LISTBOX_DrawFocusRect( descr
, FALSE
);
1357 descr
->focus_item
= index
;
1359 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1360 LISTBOX_DrawFocusRect( descr
, TRUE
);
1366 /***********************************************************************
1367 * LISTBOX_SelectItemRange
1369 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1371 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1376 /* A few sanity checks */
1378 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1379 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1381 if (!descr
->nb_items
) return LB_OKAY
;
1383 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1384 if (first
< 0) first
= 0;
1385 if (last
< first
) return LB_OKAY
;
1387 if (on
) /* Turn selection on */
1389 for (i
= first
; i
<= last
; i
++)
1391 if (descr
->items
[i
].selected
) continue;
1392 descr
->items
[i
].selected
= TRUE
;
1393 LISTBOX_InvalidateItemRect(descr
, i
);
1396 else /* Turn selection off */
1398 for (i
= first
; i
<= last
; i
++)
1400 if (!descr
->items
[i
].selected
) continue;
1401 descr
->items
[i
].selected
= FALSE
;
1402 LISTBOX_InvalidateItemRect(descr
, i
);
1408 /***********************************************************************
1409 * LISTBOX_SetSelection
1411 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1412 BOOL on
, BOOL send_notify
)
1414 TRACE( "cur_sel=%d index=%d notify=%s\n",
1415 descr
->selected_item
, index
, send_notify
? "YES" : "NO" );
1417 if (descr
->style
& LBS_NOSEL
)
1419 descr
->selected_item
= index
;
1422 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1423 if (descr
->style
& LBS_MULTIPLESEL
)
1425 if (index
== -1) /* Select all items */
1426 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1427 else /* Only one item */
1428 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1432 INT oldsel
= descr
->selected_item
;
1433 if (index
== oldsel
) return LB_OKAY
;
1434 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1435 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1436 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1437 descr
->selected_item
= index
;
1438 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1439 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1440 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1442 if( descr
->lphc
) /* set selection change flag for parent combo */
1443 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1449 /***********************************************************************
1452 * Change the caret position and extend the selection to the new caret.
1454 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1456 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1458 if ((index
< 0) || (index
>= descr
->nb_items
))
1461 /* Important, repaint needs to be done in this order if
1462 you want to mimic Windows behavior:
1463 1. Remove the focus and paint the item
1464 2. Remove the selection and paint the item(s)
1465 3. Set the selection and repaint the item(s)
1466 4. Set the focus to 'index' and repaint the item */
1468 /* 1. remove the focus and repaint the item */
1469 LISTBOX_DrawFocusRect( descr
, FALSE
);
1471 /* 2. then turn off the previous selection */
1472 /* 3. repaint the new selected item */
1473 if (descr
->style
& LBS_EXTENDEDSEL
)
1475 if (descr
->anchor_item
!= -1)
1477 INT first
= min( index
, descr
->anchor_item
);
1478 INT last
= max( index
, descr
->anchor_item
);
1480 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1481 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1482 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1485 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1487 /* Set selection to new caret item */
1488 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1491 /* 4. repaint the new item with the focus */
1492 descr
->focus_item
= index
;
1493 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1494 LISTBOX_DrawFocusRect( descr
, TRUE
);
1498 /***********************************************************************
1499 * LISTBOX_InsertItem
1501 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1502 LPWSTR str
, ULONG_PTR data
)
1506 INT oldfocus
= descr
->focus_item
;
1508 if (index
== -1) index
= descr
->nb_items
;
1509 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1510 if (!descr
->items
) max_items
= 0;
1511 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1512 if (descr
->nb_items
== max_items
)
1514 /* We need to grow the array */
1515 max_items
+= LB_ARRAY_GRANULARITY
;
1517 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1518 max_items
* sizeof(LB_ITEMDATA
) );
1520 item
= HeapAlloc( GetProcessHeap(), 0,
1521 max_items
* sizeof(LB_ITEMDATA
) );
1524 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1527 descr
->items
= item
;
1530 /* Insert the item structure */
1532 item
= &descr
->items
[index
];
1533 if (index
< descr
->nb_items
)
1534 RtlMoveMemory( item
+ 1, item
,
1535 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1537 item
->data
= HAS_STRINGS(descr
) ? 0 : data
;
1539 item
->selected
= FALSE
;
1542 /* Get item height */
1544 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1546 MEASUREITEMSTRUCT mis
;
1547 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1549 mis
.CtlType
= ODT_LISTBOX
;
1552 mis
.itemData
= data
;
1553 mis
.itemHeight
= descr
->item_height
;
1554 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1555 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1556 TRACE("[%p]: measure item %d (%s) = %d\n",
1557 descr
->self
, index
, str
? debugstr_w(str
) : "", item
->height
);
1560 /* Repaint the items */
1562 LISTBOX_UpdateScroll( descr
);
1563 LISTBOX_InvalidateItems( descr
, index
);
1565 /* Move selection and focused item */
1566 /* If listbox was empty, set focus to the first item */
1567 if (descr
->nb_items
== 1)
1568 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1569 /* single select don't change selection index in win31 */
1570 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1572 descr
->selected_item
++;
1573 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1577 if (index
<= descr
->selected_item
)
1579 descr
->selected_item
++;
1580 descr
->focus_item
= oldfocus
; /* focus not changed */
1587 /***********************************************************************
1588 * LISTBOX_InsertString
1590 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
, LPCWSTR str
)
1592 LPWSTR new_str
= NULL
;
1595 if (HAS_STRINGS(descr
))
1597 static const WCHAR empty_stringW
[] = { 0 };
1598 if (!str
) str
= empty_stringW
;
1599 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1601 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1604 strcpyW(new_str
, str
);
1607 if (index
== -1) index
= descr
->nb_items
;
1608 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, (ULONG_PTR
)str
)) != 0)
1610 HeapFree( GetProcessHeap(), 0, new_str
);
1614 TRACE("[%p]: added item %d %s\n",
1615 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1620 /***********************************************************************
1621 * LISTBOX_DeleteItem
1623 * Delete the content of an item. 'index' must be a valid index.
1625 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1627 /* save the item data before it gets freed by LB_RESETCONTENT */
1628 ULONG_PTR item_data
= descr
->items
[index
].data
;
1629 LPWSTR item_str
= descr
->items
[index
].str
;
1631 if (!descr
->nb_items
)
1632 SendMessageW( descr
->self
, LB_RESETCONTENT
, 0, 0 );
1634 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1635 * while Win95 sends it for all items with user data.
1636 * It's probably better to send it too often than not
1637 * often enough, so this is what we do here.
1639 if (IS_OWNERDRAW(descr
) || item_data
)
1641 DELETEITEMSTRUCT dis
;
1642 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1644 dis
.CtlType
= ODT_LISTBOX
;
1647 dis
.hwndItem
= descr
->self
;
1648 dis
.itemData
= item_data
;
1649 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1651 if (HAS_STRINGS(descr
))
1652 HeapFree( GetProcessHeap(), 0, item_str
);
1656 /***********************************************************************
1657 * LISTBOX_RemoveItem
1659 * Remove an item from the listbox and delete its content.
1661 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1666 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1668 /* We need to invalidate the original rect instead of the updated one. */
1669 LISTBOX_InvalidateItems( descr
, index
);
1672 LISTBOX_DeleteItem( descr
, index
);
1674 if (!descr
->nb_items
) return LB_OKAY
;
1676 /* Remove the item */
1678 item
= &descr
->items
[index
];
1679 if (index
< descr
->nb_items
)
1680 RtlMoveMemory( item
, item
+ 1,
1681 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1682 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1684 /* Shrink the item array if possible */
1686 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1687 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1689 max_items
-= LB_ARRAY_GRANULARITY
;
1690 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1691 max_items
* sizeof(LB_ITEMDATA
) );
1692 if (item
) descr
->items
= item
;
1694 /* Repaint the items */
1696 LISTBOX_UpdateScroll( descr
);
1697 /* if we removed the scrollbar, reset the top of the list
1698 (correct for owner-drawn ???) */
1699 if (descr
->nb_items
== descr
->page_size
)
1700 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1702 /* Move selection and focused item */
1703 if (!IS_MULTISELECT(descr
))
1705 if (index
== descr
->selected_item
)
1706 descr
->selected_item
= -1;
1707 else if (index
< descr
->selected_item
)
1709 descr
->selected_item
--;
1710 if (ISWIN31
) /* win 31 do not change the selected item number */
1711 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1715 if (descr
->focus_item
>= descr
->nb_items
)
1717 descr
->focus_item
= descr
->nb_items
- 1;
1718 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1724 /***********************************************************************
1725 * LISTBOX_ResetContent
1727 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1731 for(i
= descr
->nb_items
- 1; i
>=0; i
--) LISTBOX_DeleteItem( descr
, i
);
1732 HeapFree( GetProcessHeap(), 0, descr
->items
);
1733 descr
->nb_items
= 0;
1734 descr
->top_item
= 0;
1735 descr
->selected_item
= -1;
1736 descr
->focus_item
= 0;
1737 descr
->anchor_item
= -1;
1738 descr
->items
= NULL
;
1742 /***********************************************************************
1745 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, INT count
)
1749 if (HAS_STRINGS(descr
))
1751 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1755 /* FIXME: this is far from optimal... */
1756 if (count
> descr
->nb_items
)
1758 while (count
> descr
->nb_items
)
1759 if ((ret
= LISTBOX_InsertString( descr
, -1, 0 )) < 0)
1762 else if (count
< descr
->nb_items
)
1764 while (count
< descr
->nb_items
)
1765 if ((ret
= LISTBOX_RemoveItem( descr
, (descr
->nb_items
- 1) )) < 0)
1769 InvalidateRect( descr
->self
, NULL
, TRUE
);
1774 /***********************************************************************
1777 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1778 LPCWSTR filespec
, BOOL long_names
)
1781 LRESULT ret
= LB_OKAY
;
1782 WIN32_FIND_DATAW entry
;
1784 LRESULT maxinsert
= LB_ERR
;
1786 /* don't scan directory if we just want drives exclusively */
1787 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1788 /* scan directory */
1789 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1791 int le
= GetLastError();
1792 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1799 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1801 static const WCHAR bracketW
[] = { ']',0 };
1802 static const WCHAR dotW
[] = { '.',0 };
1803 if (!(attrib
& DDL_DIRECTORY
) ||
1804 !strcmpW( entry
.cFileName
, dotW
)) continue;
1806 if (!long_names
&& entry
.cAlternateFileName
[0])
1807 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1809 strcpyW( buffer
+ 1, entry
.cFileName
);
1810 strcatW(buffer
, bracketW
);
1812 else /* not a directory */
1814 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1815 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1817 if ((attrib
& DDL_EXCLUSIVE
) &&
1818 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1821 if (!long_names
&& entry
.cAlternateFileName
[0])
1822 strcpyW( buffer
, entry
.cAlternateFileName
);
1824 strcpyW( buffer
, entry
.cFileName
);
1826 if (!long_names
) CharLowerW( buffer
);
1827 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1828 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1830 if (ret
<= maxinsert
) maxinsert
++; else maxinsert
= ret
;
1831 } while (FindNextFileW( handle
, &entry
));
1832 FindClose( handle
);
1840 if (attrib
& DDL_DRIVES
)
1842 WCHAR buffer
[] = {'[','-','a','-',']',0};
1843 WCHAR root
[] = {'A',':','\\',0};
1845 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1847 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1848 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1857 /***********************************************************************
1858 * LISTBOX_HandleVScroll
1860 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1864 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1868 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1871 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1874 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1875 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1878 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1879 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1881 case SB_THUMBPOSITION
:
1882 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1885 info
.cbSize
= sizeof(info
);
1886 info
.fMask
= SIF_TRACKPOS
;
1887 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1888 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1891 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1894 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1901 /***********************************************************************
1902 * LISTBOX_HandleHScroll
1904 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1909 if (descr
->style
& LBS_MULTICOLUMN
)
1914 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
1918 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
1922 page
= descr
->width
/ descr
->column_width
;
1923 if (page
< 1) page
= 1;
1924 LISTBOX_SetTopItem( descr
,
1925 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1928 page
= descr
->width
/ descr
->column_width
;
1929 if (page
< 1) page
= 1;
1930 LISTBOX_SetTopItem( descr
,
1931 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1933 case SB_THUMBPOSITION
:
1934 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
1937 info
.cbSize
= sizeof(info
);
1938 info
.fMask
= SIF_TRACKPOS
;
1939 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1940 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
1944 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1947 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1951 else if (descr
->horz_extent
)
1956 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
1959 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
1962 LISTBOX_SetHorizontalPos( descr
,
1963 descr
->horz_pos
- descr
->width
);
1966 LISTBOX_SetHorizontalPos( descr
,
1967 descr
->horz_pos
+ descr
->width
);
1969 case SB_THUMBPOSITION
:
1970 LISTBOX_SetHorizontalPos( descr
, pos
);
1973 info
.cbSize
= sizeof(info
);
1974 info
.fMask
= SIF_TRACKPOS
;
1975 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
1976 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
1979 LISTBOX_SetHorizontalPos( descr
, 0 );
1982 LISTBOX_SetHorizontalPos( descr
,
1983 descr
->horz_extent
- descr
->width
);
1990 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
1992 UINT pulScrollLines
= 3;
1994 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1996 /* if scrolling changes direction, ignore left overs */
1997 if ((delta
< 0 && descr
->wheel_remain
< 0) ||
1998 (delta
> 0 && descr
->wheel_remain
> 0))
1999 descr
->wheel_remain
+= delta
;
2001 descr
->wheel_remain
= delta
;
2003 if (descr
->wheel_remain
&& pulScrollLines
)
2006 pulScrollLines
= min((UINT
) descr
->page_size
, pulScrollLines
);
2007 cLineScroll
= pulScrollLines
* (float)descr
->wheel_remain
/ WHEEL_DELTA
;
2008 descr
->wheel_remain
-= WHEEL_DELTA
* cLineScroll
/ (int)pulScrollLines
;
2009 LISTBOX_SetTopItem( descr
, descr
->top_item
- cLineScroll
, TRUE
);
2014 /***********************************************************************
2015 * LISTBOX_HandleLButtonDown
2017 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
2019 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2021 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2022 descr
->self
, x
, y
, index
, descr
->focus_item
);
2024 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2026 if (!descr
->in_focus
)
2028 if( !descr
->lphc
) SetFocus( descr
->self
);
2029 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2032 if (index
== -1) return 0;
2036 if (descr
->style
& LBS_NOTIFY
)
2037 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2038 MAKELPARAM( x
, y
) );
2041 descr
->captured
= TRUE
;
2042 SetCapture( descr
->self
);
2044 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2046 /* we should perhaps make sure that all items are deselected
2047 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2048 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2049 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2052 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2053 if (keys
& MK_CONTROL
)
2055 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2056 LISTBOX_SetSelection( descr
, index
,
2057 !descr
->items
[index
].selected
,
2058 (descr
->style
& LBS_NOTIFY
) != 0);
2062 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2064 if (descr
->style
& LBS_EXTENDEDSEL
)
2066 LISTBOX_SetSelection( descr
, index
,
2067 descr
->items
[index
].selected
,
2068 (descr
->style
& LBS_NOTIFY
) != 0 );
2072 LISTBOX_SetSelection( descr
, index
,
2073 !descr
->items
[index
].selected
,
2074 (descr
->style
& LBS_NOTIFY
) != 0 );
2080 descr
->anchor_item
= index
;
2081 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2082 LISTBOX_SetSelection( descr
, index
,
2083 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2088 if (GetWindowLongW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2095 if (DragDetect( descr
->self
, pt
))
2096 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2103 /*************************************************************************
2104 * LISTBOX_HandleLButtonDownCombo [Internal]
2106 * Process LButtonDown message for the ComboListBox
2109 * pWnd [I] The windows internal structure
2110 * pDescr [I] The ListBox internal structure
2111 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2112 * x [I] X Mouse Coordinate
2113 * y [I] Y Mouse Coordinate
2116 * 0 since we are processing the WM_LBUTTONDOWN Message
2119 * This function is only to be used when a ListBox is a ComboListBox
2122 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2124 RECT clientRect
, screenRect
;
2130 GetClientRect(descr
->self
, &clientRect
);
2132 if(PtInRect(&clientRect
, mousePos
))
2134 /* MousePos is in client, resume normal processing */
2135 if (msg
== WM_LBUTTONDOWN
)
2137 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2138 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2140 else if (descr
->style
& LBS_NOTIFY
)
2141 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2145 POINT screenMousePos
;
2146 HWND hWndOldCapture
;
2148 /* Check the Non-Client Area */
2149 screenMousePos
= mousePos
;
2150 hWndOldCapture
= GetCapture();
2152 GetWindowRect(descr
->self
, &screenRect
);
2153 ClientToScreen(descr
->self
, &screenMousePos
);
2155 if(!PtInRect(&screenRect
, screenMousePos
))
2157 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2158 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2159 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2163 /* Check to see the NC is a scrollbar */
2165 LONG style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2166 /* Check Vertical scroll bar */
2167 if (style
& WS_VSCROLL
)
2169 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2170 if (PtInRect( &clientRect
, mousePos
))
2171 nHitTestType
= HTVSCROLL
;
2173 /* Check horizontal scroll bar */
2174 if (style
& WS_HSCROLL
)
2176 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2177 if (PtInRect( &clientRect
, mousePos
))
2178 nHitTestType
= HTHSCROLL
;
2180 /* Windows sends this message when a scrollbar is clicked
2183 if(nHitTestType
!= 0)
2185 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2186 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2188 /* Resume the Capture after scrolling is complete
2190 if(hWndOldCapture
!= 0)
2191 SetCapture(hWndOldCapture
);
2197 /***********************************************************************
2198 * LISTBOX_HandleLButtonUp
2200 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2202 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2203 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2204 LISTBOX_Timer
= LB_TIMER_NONE
;
2205 if (descr
->captured
)
2207 descr
->captured
= FALSE
;
2208 if (GetCapture() == descr
->self
) ReleaseCapture();
2209 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2210 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2216 /***********************************************************************
2217 * LISTBOX_HandleTimer
2219 * Handle scrolling upon a timer event.
2220 * Return TRUE if scrolling should continue.
2222 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2227 if (descr
->top_item
) index
= descr
->top_item
- 1;
2231 if (descr
->top_item
) index
-= descr
->page_size
;
2234 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2235 if (index
== descr
->focus_item
) index
++;
2236 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2238 case LB_TIMER_RIGHT
:
2239 if (index
+ descr
->page_size
< descr
->nb_items
)
2240 index
+= descr
->page_size
;
2245 if (index
== descr
->focus_item
) return FALSE
;
2246 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2251 /***********************************************************************
2252 * LISTBOX_HandleSystemTimer
2254 * WM_SYSTIMER handler.
2256 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2258 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2260 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2261 LISTBOX_Timer
= LB_TIMER_NONE
;
2267 /***********************************************************************
2268 * LISTBOX_HandleMouseMove
2270 * WM_MOUSEMOVE handler.
2272 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2276 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2278 if (!descr
->captured
) return;
2280 if (descr
->style
& LBS_MULTICOLUMN
)
2283 else if (y
>= descr
->item_height
* descr
->page_size
)
2284 y
= descr
->item_height
* descr
->page_size
- 1;
2288 dir
= LB_TIMER_LEFT
;
2291 else if (x
>= descr
->width
)
2293 dir
= LB_TIMER_RIGHT
;
2294 x
= descr
->width
- 1;
2299 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2300 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2303 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2304 if (index
== -1) index
= descr
->focus_item
;
2305 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2307 /* Start/stop the system timer */
2309 if (dir
!= LB_TIMER_NONE
)
2310 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2311 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2312 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2313 LISTBOX_Timer
= dir
;
2317 /***********************************************************************
2318 * LISTBOX_HandleKeyDown
2320 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2323 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2324 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2325 bForceSelection
= FALSE
; /* only for single select list */
2327 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2329 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2330 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2331 (LPARAM
)descr
->self
);
2332 if (caret
== -2) return 0;
2334 if (caret
== -1) switch(key
)
2337 if (descr
->style
& LBS_MULTICOLUMN
)
2339 bForceSelection
= FALSE
;
2340 if (descr
->focus_item
>= descr
->page_size
)
2341 caret
= descr
->focus_item
- descr
->page_size
;
2346 caret
= descr
->focus_item
- 1;
2347 if (caret
< 0) caret
= 0;
2350 if (descr
->style
& LBS_MULTICOLUMN
)
2352 bForceSelection
= FALSE
;
2353 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2354 caret
= descr
->focus_item
+ descr
->page_size
;
2359 caret
= descr
->focus_item
+ 1;
2360 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2364 if (descr
->style
& LBS_MULTICOLUMN
)
2366 INT page
= descr
->width
/ descr
->column_width
;
2367 if (page
< 1) page
= 1;
2368 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2370 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2371 if (caret
< 0) caret
= 0;
2374 if (descr
->style
& LBS_MULTICOLUMN
)
2376 INT page
= descr
->width
/ descr
->column_width
;
2377 if (page
< 1) page
= 1;
2378 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2380 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2381 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2387 caret
= descr
->nb_items
- 1;
2390 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2391 else if (descr
->style
& LBS_MULTIPLESEL
)
2393 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2394 !descr
->items
[descr
->focus_item
].selected
,
2395 (descr
->style
& LBS_NOTIFY
) != 0 );
2399 bForceSelection
= FALSE
;
2401 if (bForceSelection
) /* focused item is used instead of key */
2402 caret
= descr
->focus_item
;
2405 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2406 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2407 !IS_MULTISELECT(descr
))
2408 descr
->anchor_item
= caret
;
2409 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2411 if (descr
->style
& LBS_MULTIPLESEL
)
2412 descr
->selected_item
= caret
;
2414 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2415 if (descr
->style
& LBS_NOTIFY
)
2417 if (descr
->lphc
&& IsWindowVisible( descr
->self
))
2419 /* make sure that combo parent doesn't hide us */
2420 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2422 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2429 /***********************************************************************
2430 * LISTBOX_HandleChar
2432 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2440 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2442 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2443 MAKEWPARAM(charW
, descr
->focus_item
),
2444 (LPARAM
)descr
->self
);
2445 if (caret
== -2) return 0;
2448 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2451 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2452 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2453 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2454 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2455 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2461 /***********************************************************************
2464 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2467 MEASUREITEMSTRUCT mis
;
2470 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2473 GetClientRect( hwnd
, &rect
);
2475 descr
->owner
= GetParent( descr
->self
);
2476 descr
->style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2477 descr
->width
= rect
.right
- rect
.left
;
2478 descr
->height
= rect
.bottom
- rect
.top
;
2479 descr
->items
= NULL
;
2480 descr
->nb_items
= 0;
2481 descr
->top_item
= 0;
2482 descr
->selected_item
= -1;
2483 descr
->focus_item
= 0;
2484 descr
->anchor_item
= -1;
2485 descr
->item_height
= 1;
2486 descr
->page_size
= 1;
2487 descr
->column_width
= 150;
2488 descr
->horz_extent
= 0;
2489 descr
->horz_pos
= 0;
2492 descr
->wheel_remain
= 0;
2493 descr
->caret_on
= !lphc
;
2494 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2495 descr
->in_focus
= FALSE
;
2496 descr
->captured
= FALSE
;
2498 descr
->locale
= GetUserDefaultLCID();
2503 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2504 descr
->owner
= lphc
->self
;
2507 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2509 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2511 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2512 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2513 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2514 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2516 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2518 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2520 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2521 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2525 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2526 mis
.CtlType
= ODT_LISTBOX
;
2531 mis
.itemHeight
= descr
->item_height
;
2532 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2533 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2537 OpenThemeData( descr
->self
, WC_LISTBOXW
);
2539 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2544 /***********************************************************************
2547 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2549 HTHEME theme
= GetWindowTheme( descr
->self
);
2550 CloseThemeData( theme
);
2551 LISTBOX_ResetContent( descr
);
2552 SetWindowLongPtrW( descr
->self
, 0, 0 );
2553 HeapFree( GetProcessHeap(), 0, descr
);
2558 /***********************************************************************
2559 * ListBoxWndProc_common
2561 static LRESULT CALLBACK
LISTBOX_WindowProc( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
2563 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2564 HEADCOMBO
*lphc
= NULL
;
2570 if (!IsWindow(hwnd
)) return 0;
2572 if (msg
== WM_CREATE
)
2574 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2575 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= lpcs
->lpCreateParams
;
2576 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2577 TRACE("creating hwnd %p descr %p\n", hwnd
, (void *)GetWindowLongPtrW( hwnd
, 0 ) );
2580 /* Ignore all other messages before we get a WM_CREATE */
2581 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
2583 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2585 TRACE("[%p]: msg %#x wp %08lx lp %08lx\n", descr
->self
, msg
, wParam
, lParam
);
2589 case LB_RESETCONTENT
:
2590 LISTBOX_ResetContent( descr
);
2591 LISTBOX_UpdateScroll( descr
);
2592 InvalidateRect( descr
->self
, NULL
, TRUE
);
2597 const WCHAR
*textW
= (const WCHAR
*)lParam
;
2598 INT index
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2599 return LISTBOX_InsertString( descr
, index
, textW
);
2602 case LB_INSERTSTRING
:
2603 return LISTBOX_InsertString( descr
, wParam
, (const WCHAR
*)lParam
);
2607 const WCHAR
*textW
= (const WCHAR
*)lParam
;
2608 INT index
= LISTBOX_FindFileStrPos( descr
, textW
);
2609 return LISTBOX_InsertString( descr
, index
, textW
);
2612 case LB_DELETESTRING
:
2613 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2614 return descr
->nb_items
;
2617 SetLastError(ERROR_INVALID_INDEX
);
2621 case LB_GETITEMDATA
:
2622 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2624 SetLastError(ERROR_INVALID_INDEX
);
2627 return descr
->items
[wParam
].data
;
2629 case LB_SETITEMDATA
:
2630 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2632 SetLastError(ERROR_INVALID_INDEX
);
2635 descr
->items
[wParam
].data
= lParam
;
2636 /* undocumented: returns TRUE, not LB_OKAY (0) */
2640 return descr
->nb_items
;
2643 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, TRUE
);
2646 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2648 SetLastError(ERROR_INVALID_INDEX
);
2651 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2652 return strlenW( descr
->items
[wParam
].str
);
2655 if (descr
->nb_items
== 0)
2657 if (!IS_MULTISELECT(descr
))
2658 return descr
->selected_item
;
2659 if (descr
->selected_item
!= -1)
2660 return descr
->selected_item
;
2661 return descr
->focus_item
;
2662 /* otherwise, if the user tries to move the selection with the */
2663 /* arrow keys, we will give the application something to choke on */
2664 case LB_GETTOPINDEX
:
2665 return descr
->top_item
;
2667 case LB_GETITEMHEIGHT
:
2668 return LISTBOX_GetItemHeight( descr
, wParam
);
2670 case LB_SETITEMHEIGHT
:
2671 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2673 case LB_ITEMFROMPOINT
:
2680 /* The hiword of the return value is not a client area
2681 hittest as suggested by MSDN, but rather a hittest on
2682 the returned listbox item. */
2684 if(descr
->nb_items
== 0)
2685 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2687 pt
.x
= (short)LOWORD(lParam
);
2688 pt
.y
= (short)HIWORD(lParam
);
2690 SetRect(&rect
, 0, 0, descr
->width
, descr
->height
);
2692 if(!PtInRect(&rect
, pt
))
2694 pt
.x
= min(pt
.x
, rect
.right
- 1);
2695 pt
.x
= max(pt
.x
, 0);
2696 pt
.y
= min(pt
.y
, rect
.bottom
- 1);
2697 pt
.y
= max(pt
.y
, 0);
2701 index
= LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
);
2705 index
= descr
->nb_items
- 1;
2708 return MAKELONG(index
, hit
? 0 : 1);
2711 case LB_SETCARETINDEX
:
2712 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2713 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2720 case LB_GETCARETINDEX
:
2721 return descr
->focus_item
;
2723 case LB_SETTOPINDEX
:
2724 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2726 case LB_SETCOLUMNWIDTH
:
2727 return LISTBOX_SetColumnWidth( descr
, wParam
);
2729 case LB_GETITEMRECT
:
2730 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2733 return LISTBOX_FindString( descr
, wParam
, (const WCHAR
*)lParam
, FALSE
);
2735 case LB_FINDSTRINGEXACT
:
2736 return LISTBOX_FindString( descr
, wParam
, (const WCHAR
*)lParam
, TRUE
);
2738 case LB_SELECTSTRING
:
2740 const WCHAR
*textW
= (const WCHAR
*)lParam
;
2743 if (HAS_STRINGS(descr
))
2744 TRACE("LB_SELECTSTRING: %s\n", debugstr_w(textW
));
2746 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2747 if (index
!= LB_ERR
)
2749 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2750 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2756 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2758 return descr
->items
[wParam
].selected
;
2761 ret
= LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2762 if (ret
!= LB_ERR
&& wParam
)
2763 descr
->anchor_item
= lParam
;
2767 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2768 LISTBOX_SetCaretIndex( descr
, wParam
, TRUE
);
2769 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2770 if (ret
!= LB_ERR
) ret
= descr
->selected_item
;
2773 case LB_GETSELCOUNT
:
2774 return LISTBOX_GetSelCount( descr
);
2776 case LB_GETSELITEMS
:
2777 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2779 case LB_SELITEMRANGE
:
2780 if (LOWORD(lParam
) <= HIWORD(lParam
))
2781 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2782 HIWORD(lParam
), wParam
);
2784 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2785 LOWORD(lParam
), wParam
);
2787 case LB_SELITEMRANGEEX
:
2788 if ((INT
)lParam
>= (INT
)wParam
)
2789 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2791 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
2793 case LB_GETHORIZONTALEXTENT
:
2794 return descr
->horz_extent
;
2796 case LB_SETHORIZONTALEXTENT
:
2797 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
2799 case LB_GETANCHORINDEX
:
2800 return descr
->anchor_item
;
2802 case LB_SETANCHORINDEX
:
2803 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2805 SetLastError(ERROR_INVALID_INDEX
);
2808 descr
->anchor_item
= (INT
)wParam
;
2812 return LISTBOX_Directory( descr
, wParam
, (const WCHAR
*)lParam
, msg
== LB_DIR
);
2815 return descr
->locale
;
2820 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
2822 ret
= descr
->locale
;
2823 descr
->locale
= (LCID
)wParam
;
2827 case LB_INITSTORAGE
:
2828 return LISTBOX_InitStorage( descr
, wParam
);
2831 return LISTBOX_SetCount( descr
, (INT
)wParam
);
2833 case LB_SETTABSTOPS
:
2834 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
);
2837 if (descr
->caret_on
)
2839 descr
->caret_on
= TRUE
;
2840 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2841 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2845 if (!descr
->caret_on
)
2847 descr
->caret_on
= FALSE
;
2848 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2849 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2852 case LB_GETLISTBOXINFO
:
2853 return descr
->page_size
;
2856 return LISTBOX_Destroy( descr
);
2859 InvalidateRect( descr
->self
, NULL
, TRUE
);
2863 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
2867 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2869 case WM_PRINTCLIENT
:
2873 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
2874 ret
= LISTBOX_Paint( descr
, hdc
);
2875 if( !wParam
) EndPaint( descr
->self
, &ps
);
2880 LISTBOX_NCPaint( descr
, (HRGN
)wParam
);
2884 LISTBOX_UpdateSize( descr
);
2887 return (LRESULT
)descr
->font
;
2889 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
2890 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
2893 descr
->in_focus
= TRUE
;
2894 descr
->caret_on
= TRUE
;
2895 if (descr
->focus_item
!= -1)
2896 LISTBOX_DrawFocusRect( descr
, TRUE
);
2897 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
2900 LISTBOX_HandleLButtonUp( descr
); /* Release capture if we have it */
2901 descr
->in_focus
= FALSE
;
2902 descr
->wheel_remain
= 0;
2903 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2904 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2905 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
2908 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
2910 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
2912 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2913 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
2914 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
2915 case WM_LBUTTONDOWN
:
2917 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
2918 (INT16
)LOWORD(lParam
),
2919 (INT16
)HIWORD(lParam
) );
2920 return LISTBOX_HandleLButtonDown( descr
, wParam
,
2921 (INT16
)LOWORD(lParam
),
2922 (INT16
)HIWORD(lParam
) );
2923 case WM_LBUTTONDBLCLK
:
2925 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
2926 (INT16
)LOWORD(lParam
),
2927 (INT16
)HIWORD(lParam
) );
2928 if (descr
->style
& LBS_NOTIFY
)
2929 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2932 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
2934 BOOL captured
= descr
->captured
;
2938 mousePos
.x
= (INT16
)LOWORD(lParam
);
2939 mousePos
.y
= (INT16
)HIWORD(lParam
);
2942 * If we are in a dropdown combobox, we simulate that
2943 * the mouse is captured to show the tracking of the item.
2945 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
2946 descr
->captured
= TRUE
;
2948 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
2950 descr
->captured
= captured
;
2952 else if (GetCapture() == descr
->self
)
2954 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
2955 (INT16
)HIWORD(lParam
) );
2965 * If the mouse button "up" is not in the listbox,
2966 * we make sure there is no selection by re-selecting the
2967 * item that was selected when the listbox was made visible.
2969 mousePos
.x
= (INT16
)LOWORD(lParam
);
2970 mousePos
.y
= (INT16
)HIWORD(lParam
);
2972 GetClientRect(descr
->self
, &clientRect
);
2975 * When the user clicks outside the combobox and the focus
2976 * is lost, the owning combobox will send a fake buttonup with
2977 * 0xFFFFFFF as the mouse location, we must also revert the
2978 * selection to the original selection.
2980 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
2981 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
2983 return LISTBOX_HandleLButtonUp( descr
);
2985 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
2987 /* for some reason Windows makes it possible to
2988 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2990 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
2991 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
2992 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
2994 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
2998 return LISTBOX_HandleKeyDown( descr
, wParam
);
3000 return LISTBOX_HandleChar( descr
, wParam
);
3003 return LISTBOX_HandleSystemTimer( descr
);
3005 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3008 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3009 wParam
, (LPARAM
)descr
->self
);
3010 TRACE("hbrush = %p\n", hbrush
);
3012 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3015 GetClientRect(descr
->self
, &rect
);
3016 FillRect((HDC
)wParam
, &rect
, hbrush
);
3021 if( lphc
) return 0;
3022 return SendMessageW( descr
->owner
, msg
, wParam
, lParam
);
3025 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3033 case WM_THEMECHANGED
:
3034 theme
= GetWindowTheme( hwnd
);
3035 CloseThemeData( theme
);
3036 OpenThemeData( hwnd
, WC_LISTBOXW
);
3040 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3041 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3042 hwnd
, msg
, wParam
, lParam
);
3045 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
3048 void LISTBOX_Register(void)
3052 memset(&wndClass
, 0, sizeof(wndClass
));
3053 wndClass
.style
= CS_PARENTDC
| CS_DBLCLKS
| CS_GLOBALCLASS
;
3054 wndClass
.lpfnWndProc
= LISTBOX_WindowProc
;
3055 wndClass
.cbClsExtra
= 0;
3056 wndClass
.cbWndExtra
= sizeof(LB_DESCR
*);
3057 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
3058 wndClass
.hbrBackground
= NULL
;
3059 wndClass
.lpszClassName
= WC_LISTBOXW
;
3060 RegisterClassW(&wndClass
);
3063 void COMBOLBOX_Register(void)
3065 static const WCHAR combolboxW
[] = {'C','o','m','b','o','L','B','o','x',0};
3068 memset(&wndClass
, 0, sizeof(wndClass
));
3069 wndClass
.style
= CS_SAVEBITS
| CS_DBLCLKS
| CS_DROPSHADOW
| CS_GLOBALCLASS
;
3070 wndClass
.lpfnWndProc
= LISTBOX_WindowProc
;
3071 wndClass
.cbClsExtra
= 0;
3072 wndClass
.cbWndExtra
= sizeof(LB_DESCR
*);
3073 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
3074 wndClass
.hbrBackground
= NULL
;
3075 wndClass
.lpszClassName
= combolboxW
;
3076 RegisterClassW(&wndClass
);