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
->style
& LBS_SORT
)) return -1; /* Add it at the end */
796 max
= descr
->nb_items
;
799 index
= (min
+ max
) / 2;
800 if (HAS_STRINGS(descr
))
801 res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, descr
->items
[index
].str
);
804 COMPAREITEMSTRUCT cis
;
805 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
807 cis
.CtlType
= ODT_LISTBOX
;
809 cis
.hwndItem
= descr
->self
;
810 /* note that some application (MetaStock) expects the second item
811 * to be in the listbox */
813 cis
.itemData1
= (ULONG_PTR
)str
;
815 cis
.itemData2
= descr
->items
[index
].data
;
816 cis
.dwLocaleId
= descr
->locale
;
817 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
819 if (!res
) return index
;
820 if (res
< 0) max
= index
;
821 else min
= index
+ 1;
823 return exact
? -1 : max
;
827 /***********************************************************************
828 * LISTBOX_FindFileStrPos
830 * Find the nearest string located before a given string in directory
831 * sort order (i.e. first files, then directories, then drives).
833 static INT
LISTBOX_FindFileStrPos( LB_DESCR
*descr
, LPCWSTR str
)
837 if (!HAS_STRINGS(descr
))
838 return LISTBOX_FindStringPos( descr
, str
, FALSE
);
840 max
= descr
->nb_items
;
843 INT index
= (min
+ max
) / 2;
844 LPCWSTR p
= descr
->items
[index
].str
;
845 if (*p
== '[') /* drive or directory */
847 if (*str
!= '[') res
= -1;
848 else if (p
[1] == '-') /* drive */
850 if (str
[1] == '-') res
= str
[2] - p
[2];
855 if (str
[1] == '-') res
= 1;
856 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
861 if (*str
== '[') res
= 1;
862 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
864 if (!res
) return index
;
865 if (res
< 0) max
= index
;
866 else min
= index
+ 1;
872 /***********************************************************************
875 * Find the item beginning with a given string.
877 static INT
LISTBOX_FindString( LB_DESCR
*descr
, INT start
, LPCWSTR str
, BOOL exact
)
882 if (start
>= descr
->nb_items
) start
= -1;
883 item
= descr
->items
+ start
+ 1;
884 if (HAS_STRINGS(descr
))
886 if (!str
|| ! str
[0] ) return LB_ERR
;
889 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
890 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
891 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
892 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
896 /* Special case for drives and directories: ignore prefix */
897 #define CHECK_DRIVE(item) \
898 if ((item)->str[0] == '[') \
900 if (!strncmpiW( str, (item)->str+1, len )) return i; \
901 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
905 INT len
= strlenW(str
);
906 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
908 if (!strncmpiW( str
, item
->str
, len
)) return i
;
911 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
913 if (!strncmpiW( str
, item
->str
, len
)) return i
;
921 if (exact
&& (descr
->style
& LBS_SORT
))
922 /* If sorted, use a WM_COMPAREITEM binary search */
923 return LISTBOX_FindStringPos( descr
, str
, TRUE
);
925 /* Otherwise use a linear search */
926 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
927 if (item
->data
== (ULONG_PTR
)str
) return i
;
928 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
929 if (item
->data
== (ULONG_PTR
)str
) return i
;
935 /***********************************************************************
936 * LISTBOX_GetSelCount
938 static LRESULT
LISTBOX_GetSelCount( const LB_DESCR
*descr
)
941 const LB_ITEMDATA
*item
= descr
->items
;
943 if (!(descr
->style
& LBS_MULTIPLESEL
) ||
944 (descr
->style
& LBS_NOSEL
))
946 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
947 if (item
->selected
) count
++;
952 /***********************************************************************
953 * LISTBOX_GetSelItems
955 static LRESULT
LISTBOX_GetSelItems( const LB_DESCR
*descr
, INT max
, LPINT array
)
958 const LB_ITEMDATA
*item
= descr
->items
;
960 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
961 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
962 if (item
->selected
) array
[count
++] = i
;
967 /***********************************************************************
970 static LRESULT
LISTBOX_Paint( LB_DESCR
*descr
, HDC hdc
)
972 INT i
, col_pos
= descr
->page_size
- 1;
974 RECT focusRect
= {-1, -1, -1, -1};
976 HBRUSH hbrush
, oldBrush
= 0;
978 if (descr
->style
& LBS_NOREDRAW
) return 0;
980 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
981 if (descr
->style
& LBS_MULTICOLUMN
)
982 rect
.right
= rect
.left
+ descr
->column_width
;
983 else if (descr
->horz_pos
)
985 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
986 rect
.right
+= descr
->horz_pos
;
989 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
990 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
991 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
992 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
993 if (!IsWindowEnabled(descr
->self
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
995 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
998 /* Special case for empty listbox: paint focus rect */
999 rect
.bottom
= rect
.top
+ descr
->item_height
;
1000 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1001 &rect
, NULL
, 0, NULL
);
1002 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1003 rect
.top
= rect
.bottom
;
1006 /* Paint all the item, regarding the selection
1007 Focus state will be painted after */
1009 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1011 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1012 rect
.bottom
= rect
.top
+ descr
->item_height
;
1014 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1016 /* keep the focus rect, to paint the focus item after */
1017 if (i
== descr
->focus_item
)
1020 LISTBOX_PaintItem( descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1021 rect
.top
= rect
.bottom
;
1023 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1025 if (!IS_OWNERDRAW(descr
))
1027 /* Clear the bottom of the column */
1028 if (rect
.top
< descr
->height
)
1030 rect
.bottom
= descr
->height
;
1031 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1032 &rect
, NULL
, 0, NULL
);
1036 /* Go to the next column */
1037 rect
.left
+= descr
->column_width
;
1038 rect
.right
+= descr
->column_width
;
1040 col_pos
= descr
->page_size
- 1;
1045 if (rect
.top
>= descr
->height
) break;
1049 /* Paint the focus item now */
1050 if (focusRect
.top
!= focusRect
.bottom
&&
1051 descr
->caret_on
&& descr
->in_focus
)
1052 LISTBOX_PaintItem( descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1054 if (!IS_OWNERDRAW(descr
))
1056 /* Clear the remainder of the client area */
1057 if (rect
.top
< descr
->height
)
1059 rect
.bottom
= descr
->height
;
1060 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1061 &rect
, NULL
, 0, NULL
);
1063 if (rect
.right
< descr
->width
)
1065 rect
.left
= rect
.right
;
1066 rect
.right
= descr
->width
;
1068 rect
.bottom
= descr
->height
;
1069 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1070 &rect
, NULL
, 0, NULL
);
1073 if (oldFont
) SelectObject( hdc
, oldFont
);
1074 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1078 static void LISTBOX_NCPaint( LB_DESCR
*descr
, HRGN region
)
1080 DWORD exstyle
= GetWindowLongW( descr
->self
, GWL_EXSTYLE
);
1081 HTHEME theme
= GetWindowTheme( descr
->self
);
1082 HRGN cliprgn
= region
;
1087 if (!theme
|| !(exstyle
& WS_EX_CLIENTEDGE
))
1090 cxEdge
= GetSystemMetrics(SM_CXEDGE
),
1091 cyEdge
= GetSystemMetrics(SM_CYEDGE
);
1093 GetWindowRect(descr
->self
, &r
);
1095 /* New clipping region passed to default proc to exclude border */
1096 cliprgn
= CreateRectRgn(r
.left
+ cxEdge
, r
.top
+ cyEdge
,
1097 r
.right
- cxEdge
, r
.bottom
- cyEdge
);
1098 if (region
!= (HRGN
)1)
1099 CombineRgn(cliprgn
, cliprgn
, region
, RGN_AND
);
1100 OffsetRect(&r
, -r
.left
, -r
.top
);
1102 #ifdef __REACTOS__ /* r73789 */
1103 hdc
= GetWindowDC(descr
->self
);
1104 /* Exclude client part */
1105 ExcludeClipRect(hdc
,
1111 hdc
= GetDCEx(descr
->self
, region
, DCX_WINDOW
|DCX_INTERSECTRGN
);
1112 OffsetRect(&r
, -r
.left
, -r
.top
);
1115 if (IsThemeBackgroundPartiallyTransparent (theme
, 0, 0))
1116 DrawThemeParentBackground(descr
->self
, hdc
, &r
);
1117 DrawThemeBackground (theme
, hdc
, 0, 0, &r
, 0);
1118 ReleaseDC(descr
->self
, hdc
);
1121 /***********************************************************************
1122 * LISTBOX_InvalidateItems
1124 * Invalidate all items from a given item. If the specified item is not
1125 * visible, nothing happens.
1127 static void LISTBOX_InvalidateItems( LB_DESCR
*descr
, INT index
)
1131 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1133 if (descr
->style
& LBS_NOREDRAW
)
1135 descr
->style
|= LBS_DISPLAYCHANGED
;
1138 rect
.bottom
= descr
->height
;
1139 InvalidateRect( descr
->self
, &rect
, TRUE
);
1140 if (descr
->style
& LBS_MULTICOLUMN
)
1142 /* Repaint the other columns */
1143 rect
.left
= rect
.right
;
1144 rect
.right
= descr
->width
;
1146 InvalidateRect( descr
->self
, &rect
, TRUE
);
1151 static void LISTBOX_InvalidateItemRect( LB_DESCR
*descr
, INT index
)
1155 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1156 InvalidateRect( descr
->self
, &rect
, TRUE
);
1159 /***********************************************************************
1160 * LISTBOX_GetItemHeight
1162 static LRESULT
LISTBOX_GetItemHeight( const LB_DESCR
*descr
, INT index
)
1164 if (descr
->style
& LBS_OWNERDRAWVARIABLE
&& descr
->nb_items
> 0)
1166 if ((index
< 0) || (index
>= descr
->nb_items
))
1168 SetLastError(ERROR_INVALID_INDEX
);
1171 return descr
->items
[index
].height
;
1173 else return descr
->item_height
;
1177 /***********************************************************************
1178 * LISTBOX_SetItemHeight
1180 static LRESULT
LISTBOX_SetItemHeight( LB_DESCR
*descr
, INT index
, INT height
, BOOL repaint
)
1182 if (height
> MAXBYTE
)
1185 if (!height
) height
= 1;
1187 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1189 if ((index
< 0) || (index
>= descr
->nb_items
))
1191 SetLastError(ERROR_INVALID_INDEX
);
1194 TRACE("[%p]: item %d height = %d\n", descr
->self
, index
, height
);
1195 descr
->items
[index
].height
= height
;
1196 LISTBOX_UpdateScroll( descr
);
1198 LISTBOX_InvalidateItems( descr
, index
);
1200 else if (height
!= descr
->item_height
)
1202 TRACE("[%p]: new height = %d\n", descr
->self
, height
);
1203 descr
->item_height
= height
;
1204 LISTBOX_UpdatePage( descr
);
1205 LISTBOX_UpdateScroll( descr
);
1207 InvalidateRect( descr
->self
, 0, TRUE
);
1213 /***********************************************************************
1214 * LISTBOX_SetHorizontalPos
1216 static void LISTBOX_SetHorizontalPos( LB_DESCR
*descr
, INT pos
)
1220 if (pos
> descr
->horz_extent
- descr
->width
)
1221 pos
= descr
->horz_extent
- descr
->width
;
1222 if (pos
< 0) pos
= 0;
1223 if (!(diff
= descr
->horz_pos
- pos
)) return;
1224 TRACE("[%p]: new horz pos = %d\n", descr
->self
, pos
);
1225 descr
->horz_pos
= pos
;
1226 LISTBOX_UpdateScroll( descr
);
1227 if (abs(diff
) < descr
->width
)
1230 /* Invalidate the focused item so it will be repainted correctly */
1231 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1232 InvalidateRect( descr
->self
, &rect
, TRUE
);
1233 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
1234 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1237 InvalidateRect( descr
->self
, NULL
, TRUE
);
1241 /***********************************************************************
1242 * LISTBOX_SetHorizontalExtent
1244 static LRESULT
LISTBOX_SetHorizontalExtent( LB_DESCR
*descr
, INT extent
)
1246 if (descr
->style
& LBS_MULTICOLUMN
)
1248 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1249 TRACE("[%p]: new horz extent = %d\n", descr
->self
, extent
);
1250 descr
->horz_extent
= extent
;
1251 if (descr
->style
& WS_HSCROLL
) {
1253 info
.cbSize
= sizeof(info
);
1255 info
.nMax
= descr
->horz_extent
? descr
->horz_extent
- 1 : 0;
1256 info
.fMask
= SIF_RANGE
;
1257 if (descr
->style
& LBS_DISABLENOSCROLL
)
1258 info
.fMask
|= SIF_DISABLENOSCROLL
;
1259 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
1261 if (descr
->horz_pos
> extent
- descr
->width
)
1262 LISTBOX_SetHorizontalPos( descr
, extent
- descr
->width
);
1267 /***********************************************************************
1268 * LISTBOX_SetColumnWidth
1270 static LRESULT
LISTBOX_SetColumnWidth( LB_DESCR
*descr
, INT width
)
1272 if (width
== descr
->column_width
) return LB_OKAY
;
1273 TRACE("[%p]: new column width = %d\n", descr
->self
, width
);
1274 descr
->column_width
= width
;
1275 LISTBOX_UpdatePage( descr
);
1280 /***********************************************************************
1283 * Returns the item height.
1285 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1289 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1294 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1296 ERR("unable to get DC.\n" );
1299 if (font
) oldFont
= SelectObject( hdc
, font
);
1300 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1301 if (oldFont
) SelectObject( hdc
, oldFont
);
1302 ReleaseDC( descr
->self
, hdc
);
1304 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1305 if (!IS_OWNERDRAW(descr
))
1306 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1311 /***********************************************************************
1312 * LISTBOX_MakeItemVisible
1314 * Make sure that a given item is partially or fully visible.
1316 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1320 TRACE("current top item %d, index %d, fully %d\n", descr
->top_item
, index
, fully
);
1322 if (index
<= descr
->top_item
) top
= index
;
1323 else if (descr
->style
& LBS_MULTICOLUMN
)
1325 INT cols
= descr
->width
;
1326 if (!fully
) cols
+= descr
->column_width
- 1;
1327 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1329 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1330 top
= index
- descr
->page_size
* (cols
- 1);
1332 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1334 INT height
= fully
? descr
->items
[index
].height
: 1;
1335 for (top
= index
; top
> descr
->top_item
; top
--)
1336 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1340 if (index
< descr
->top_item
+ descr
->page_size
) return;
1341 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1342 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1343 top
= index
- descr
->page_size
+ 1;
1345 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1348 /***********************************************************************
1349 * LISTBOX_SetCaretIndex
1352 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1355 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1357 INT oldfocus
= descr
->focus_item
;
1359 TRACE("old focus %d, index %d\n", oldfocus
, index
);
1361 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1362 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1363 if (index
== oldfocus
) return LB_OKAY
;
1365 LISTBOX_DrawFocusRect( descr
, FALSE
);
1366 descr
->focus_item
= index
;
1368 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1369 LISTBOX_DrawFocusRect( descr
, TRUE
);
1375 /***********************************************************************
1376 * LISTBOX_SelectItemRange
1378 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1380 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1385 /* A few sanity checks */
1387 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1388 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1390 if (!descr
->nb_items
) return LB_OKAY
;
1392 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1393 if (first
< 0) first
= 0;
1394 if (last
< first
) return LB_OKAY
;
1396 if (on
) /* Turn selection on */
1398 for (i
= first
; i
<= last
; i
++)
1400 if (descr
->items
[i
].selected
) continue;
1401 descr
->items
[i
].selected
= TRUE
;
1402 LISTBOX_InvalidateItemRect(descr
, i
);
1405 else /* Turn selection off */
1407 for (i
= first
; i
<= last
; i
++)
1409 if (!descr
->items
[i
].selected
) continue;
1410 descr
->items
[i
].selected
= FALSE
;
1411 LISTBOX_InvalidateItemRect(descr
, i
);
1417 /***********************************************************************
1418 * LISTBOX_SetSelection
1420 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1421 BOOL on
, BOOL send_notify
)
1423 TRACE( "cur_sel=%d index=%d notify=%s\n",
1424 descr
->selected_item
, index
, send_notify
? "YES" : "NO" );
1426 if (descr
->style
& LBS_NOSEL
)
1428 descr
->selected_item
= index
;
1431 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1432 if (descr
->style
& LBS_MULTIPLESEL
)
1434 if (index
== -1) /* Select all items */
1435 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1436 else /* Only one item */
1437 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1441 INT oldsel
= descr
->selected_item
;
1442 if (index
== oldsel
) return LB_OKAY
;
1443 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1444 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1445 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1446 descr
->selected_item
= index
;
1447 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1448 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1449 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1451 if( descr
->lphc
) /* set selection change flag for parent combo */
1452 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1458 /***********************************************************************
1461 * Change the caret position and extend the selection to the new caret.
1463 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1465 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1467 if ((index
< 0) || (index
>= descr
->nb_items
))
1470 /* Important, repaint needs to be done in this order if
1471 you want to mimic Windows behavior:
1472 1. Remove the focus and paint the item
1473 2. Remove the selection and paint the item(s)
1474 3. Set the selection and repaint the item(s)
1475 4. Set the focus to 'index' and repaint the item */
1477 /* 1. remove the focus and repaint the item */
1478 LISTBOX_DrawFocusRect( descr
, FALSE
);
1480 /* 2. then turn off the previous selection */
1481 /* 3. repaint the new selected item */
1482 if (descr
->style
& LBS_EXTENDEDSEL
)
1484 if (descr
->anchor_item
!= -1)
1486 INT first
= min( index
, descr
->anchor_item
);
1487 INT last
= max( index
, descr
->anchor_item
);
1489 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1490 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1491 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1494 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1496 /* Set selection to new caret item */
1497 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1500 /* 4. repaint the new item with the focus */
1501 descr
->focus_item
= index
;
1502 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1503 LISTBOX_DrawFocusRect( descr
, TRUE
);
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
) );
1546 item
->data
= HAS_STRINGS(descr
) ? 0 : data
;
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
= 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
;
1604 if (HAS_STRINGS(descr
))
1606 static const WCHAR empty_stringW
[] = { 0 };
1607 if (!str
) str
= empty_stringW
;
1608 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1610 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1613 strcpyW(new_str
, str
);
1616 if (index
== -1) index
= descr
->nb_items
;
1617 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, (ULONG_PTR
)str
)) != 0)
1619 HeapFree( GetProcessHeap(), 0, new_str
);
1623 TRACE("[%p]: added item %d %s\n",
1624 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1629 /***********************************************************************
1630 * LISTBOX_DeleteItem
1632 * Delete the content of an item. 'index' must be a valid index.
1634 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1636 /* save the item data before it gets freed by LB_RESETCONTENT */
1637 ULONG_PTR item_data
= descr
->items
[index
].data
;
1638 LPWSTR item_str
= descr
->items
[index
].str
;
1640 if (!descr
->nb_items
)
1641 SendMessageW( descr
->self
, LB_RESETCONTENT
, 0, 0 );
1643 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1644 * while Win95 sends it for all items with user data.
1645 * It's probably better to send it too often than not
1646 * often enough, so this is what we do here.
1648 if (IS_OWNERDRAW(descr
) || item_data
)
1650 DELETEITEMSTRUCT dis
;
1651 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1653 dis
.CtlType
= ODT_LISTBOX
;
1656 dis
.hwndItem
= descr
->self
;
1657 dis
.itemData
= item_data
;
1658 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1660 if (HAS_STRINGS(descr
))
1661 HeapFree( GetProcessHeap(), 0, item_str
);
1665 /***********************************************************************
1666 * LISTBOX_RemoveItem
1668 * Remove an item from the listbox and delete its content.
1670 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1675 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1677 /* We need to invalidate the original rect instead of the updated one. */
1678 LISTBOX_InvalidateItems( descr
, index
);
1681 LISTBOX_DeleteItem( descr
, index
);
1683 if (!descr
->nb_items
) return LB_OKAY
;
1685 /* Remove the item */
1687 item
= &descr
->items
[index
];
1688 if (index
< descr
->nb_items
)
1689 RtlMoveMemory( item
, item
+ 1,
1690 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1691 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1693 /* Shrink the item array if possible */
1695 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1696 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1698 max_items
-= LB_ARRAY_GRANULARITY
;
1699 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1700 max_items
* sizeof(LB_ITEMDATA
) );
1701 if (item
) descr
->items
= item
;
1703 /* Repaint the items */
1705 LISTBOX_UpdateScroll( descr
);
1706 /* if we removed the scrollbar, reset the top of the list
1707 (correct for owner-drawn ???) */
1708 if (descr
->nb_items
== descr
->page_size
)
1709 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1711 /* Move selection and focused item */
1712 if (!IS_MULTISELECT(descr
))
1714 if (index
== descr
->selected_item
)
1715 descr
->selected_item
= -1;
1716 else if (index
< descr
->selected_item
)
1718 descr
->selected_item
--;
1719 if (ISWIN31
) /* win 31 do not change the selected item number */
1720 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1724 if (descr
->focus_item
>= descr
->nb_items
)
1726 descr
->focus_item
= descr
->nb_items
- 1;
1727 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1733 /***********************************************************************
1734 * LISTBOX_ResetContent
1736 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1740 for(i
= descr
->nb_items
- 1; i
>=0; i
--) LISTBOX_DeleteItem( descr
, i
);
1741 HeapFree( GetProcessHeap(), 0, descr
->items
);
1742 descr
->nb_items
= 0;
1743 descr
->top_item
= 0;
1744 descr
->selected_item
= -1;
1745 descr
->focus_item
= 0;
1746 descr
->anchor_item
= -1;
1747 descr
->items
= NULL
;
1751 /***********************************************************************
1754 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, INT count
)
1758 if (HAS_STRINGS(descr
))
1760 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1764 /* FIXME: this is far from optimal... */
1765 if (count
> descr
->nb_items
)
1767 while (count
> descr
->nb_items
)
1768 if ((ret
= LISTBOX_InsertString( descr
, -1, 0 )) < 0)
1771 else if (count
< descr
->nb_items
)
1773 while (count
< descr
->nb_items
)
1774 if ((ret
= LISTBOX_RemoveItem( descr
, (descr
->nb_items
- 1) )) < 0)
1778 InvalidateRect( descr
->self
, NULL
, TRUE
);
1783 /***********************************************************************
1786 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1787 LPCWSTR filespec
, BOOL long_names
)
1790 LRESULT ret
= LB_OKAY
;
1791 WIN32_FIND_DATAW entry
;
1793 LRESULT maxinsert
= LB_ERR
;
1795 /* don't scan directory if we just want drives exclusively */
1796 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1797 /* scan directory */
1798 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1800 int le
= GetLastError();
1801 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1808 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1810 static const WCHAR bracketW
[] = { ']',0 };
1811 static const WCHAR dotW
[] = { '.',0 };
1812 if (!(attrib
& DDL_DIRECTORY
) ||
1813 !strcmpW( entry
.cFileName
, dotW
)) continue;
1815 if (!long_names
&& entry
.cAlternateFileName
[0])
1816 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1818 strcpyW( buffer
+ 1, entry
.cFileName
);
1819 strcatW(buffer
, bracketW
);
1821 else /* not a directory */
1823 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1824 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1826 if ((attrib
& DDL_EXCLUSIVE
) &&
1827 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1830 if (!long_names
&& entry
.cAlternateFileName
[0])
1831 strcpyW( buffer
, entry
.cAlternateFileName
);
1833 strcpyW( buffer
, entry
.cFileName
);
1835 if (!long_names
) CharLowerW( buffer
);
1836 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1837 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1839 if (ret
<= maxinsert
) maxinsert
++; else maxinsert
= ret
;
1840 } while (FindNextFileW( handle
, &entry
));
1841 FindClose( handle
);
1849 if (attrib
& DDL_DRIVES
)
1851 WCHAR buffer
[] = {'[','-','a','-',']',0};
1852 WCHAR root
[] = {'A',':','\\',0};
1854 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1856 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1857 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1866 /***********************************************************************
1867 * LISTBOX_HandleVScroll
1869 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1873 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1877 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1880 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1883 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1884 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1887 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1888 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1890 case SB_THUMBPOSITION
:
1891 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1894 info
.cbSize
= sizeof(info
);
1895 info
.fMask
= SIF_TRACKPOS
;
1896 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1897 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1900 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1903 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1910 /***********************************************************************
1911 * LISTBOX_HandleHScroll
1913 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1918 if (descr
->style
& LBS_MULTICOLUMN
)
1923 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
1927 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
1931 page
= descr
->width
/ descr
->column_width
;
1932 if (page
< 1) page
= 1;
1933 LISTBOX_SetTopItem( descr
,
1934 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1937 page
= descr
->width
/ descr
->column_width
;
1938 if (page
< 1) page
= 1;
1939 LISTBOX_SetTopItem( descr
,
1940 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1942 case SB_THUMBPOSITION
:
1943 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
1946 info
.cbSize
= sizeof(info
);
1947 info
.fMask
= SIF_TRACKPOS
;
1948 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1949 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
1953 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1956 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1960 else if (descr
->horz_extent
)
1965 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
1968 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
1971 LISTBOX_SetHorizontalPos( descr
,
1972 descr
->horz_pos
- descr
->width
);
1975 LISTBOX_SetHorizontalPos( descr
,
1976 descr
->horz_pos
+ descr
->width
);
1978 case SB_THUMBPOSITION
:
1979 LISTBOX_SetHorizontalPos( descr
, pos
);
1982 info
.cbSize
= sizeof(info
);
1983 info
.fMask
= SIF_TRACKPOS
;
1984 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
1985 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
1988 LISTBOX_SetHorizontalPos( descr
, 0 );
1991 LISTBOX_SetHorizontalPos( descr
,
1992 descr
->horz_extent
- descr
->width
);
1999 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
2001 UINT pulScrollLines
= 3;
2003 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
2005 /* if scrolling changes direction, ignore left overs */
2006 if ((delta
< 0 && descr
->wheel_remain
< 0) ||
2007 (delta
> 0 && descr
->wheel_remain
> 0))
2008 descr
->wheel_remain
+= delta
;
2010 descr
->wheel_remain
= delta
;
2012 if (descr
->wheel_remain
&& pulScrollLines
)
2015 pulScrollLines
= min((UINT
) descr
->page_size
, pulScrollLines
);
2016 cLineScroll
= pulScrollLines
* (float)descr
->wheel_remain
/ WHEEL_DELTA
;
2017 descr
->wheel_remain
-= WHEEL_DELTA
* cLineScroll
/ (int)pulScrollLines
;
2018 LISTBOX_SetTopItem( descr
, descr
->top_item
- cLineScroll
, TRUE
);
2023 /***********************************************************************
2024 * LISTBOX_HandleLButtonDown
2026 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
2028 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2030 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2031 descr
->self
, x
, y
, index
, descr
->focus_item
);
2033 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2035 if (!descr
->in_focus
)
2037 if( !descr
->lphc
) SetFocus( descr
->self
);
2038 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2041 if (index
== -1) return 0;
2045 if (descr
->style
& LBS_NOTIFY
)
2046 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2047 MAKELPARAM( x
, y
) );
2050 descr
->captured
= TRUE
;
2051 SetCapture( descr
->self
);
2053 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2055 /* we should perhaps make sure that all items are deselected
2056 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2057 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2058 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2061 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2062 if (keys
& MK_CONTROL
)
2064 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2065 LISTBOX_SetSelection( descr
, index
,
2066 !descr
->items
[index
].selected
,
2067 (descr
->style
& LBS_NOTIFY
) != 0);
2071 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2073 if (descr
->style
& LBS_EXTENDEDSEL
)
2075 LISTBOX_SetSelection( descr
, index
,
2076 descr
->items
[index
].selected
,
2077 (descr
->style
& LBS_NOTIFY
) != 0 );
2081 LISTBOX_SetSelection( descr
, index
,
2082 !descr
->items
[index
].selected
,
2083 (descr
->style
& LBS_NOTIFY
) != 0 );
2089 descr
->anchor_item
= index
;
2090 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2091 LISTBOX_SetSelection( descr
, index
,
2092 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2097 if (GetWindowLongW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2104 if (DragDetect( descr
->self
, pt
))
2105 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2112 /*************************************************************************
2113 * LISTBOX_HandleLButtonDownCombo [Internal]
2115 * Process LButtonDown message for the ComboListBox
2118 * pWnd [I] The windows internal structure
2119 * pDescr [I] The ListBox internal structure
2120 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2121 * x [I] X Mouse Coordinate
2122 * y [I] Y Mouse Coordinate
2125 * 0 since we are processing the WM_LBUTTONDOWN Message
2128 * This function is only to be used when a ListBox is a ComboListBox
2131 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2133 RECT clientRect
, screenRect
;
2139 GetClientRect(descr
->self
, &clientRect
);
2141 if(PtInRect(&clientRect
, mousePos
))
2143 /* MousePos is in client, resume normal processing */
2144 if (msg
== WM_LBUTTONDOWN
)
2146 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2147 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2149 else if (descr
->style
& LBS_NOTIFY
)
2150 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2154 POINT screenMousePos
;
2155 HWND hWndOldCapture
;
2157 /* Check the Non-Client Area */
2158 screenMousePos
= mousePos
;
2159 hWndOldCapture
= GetCapture();
2161 GetWindowRect(descr
->self
, &screenRect
);
2162 ClientToScreen(descr
->self
, &screenMousePos
);
2164 if(!PtInRect(&screenRect
, screenMousePos
))
2166 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2167 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2168 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2172 /* Check to see the NC is a scrollbar */
2174 LONG style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2175 /* Check Vertical scroll bar */
2176 if (style
& WS_VSCROLL
)
2178 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2179 if (PtInRect( &clientRect
, mousePos
))
2180 nHitTestType
= HTVSCROLL
;
2182 /* Check horizontal scroll bar */
2183 if (style
& WS_HSCROLL
)
2185 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2186 if (PtInRect( &clientRect
, mousePos
))
2187 nHitTestType
= HTHSCROLL
;
2189 /* Windows sends this message when a scrollbar is clicked
2192 if(nHitTestType
!= 0)
2194 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2195 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2197 /* Resume the Capture after scrolling is complete
2199 if(hWndOldCapture
!= 0)
2200 SetCapture(hWndOldCapture
);
2206 /***********************************************************************
2207 * LISTBOX_HandleLButtonUp
2209 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2211 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2212 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2213 LISTBOX_Timer
= LB_TIMER_NONE
;
2214 if (descr
->captured
)
2216 descr
->captured
= FALSE
;
2217 if (GetCapture() == descr
->self
) ReleaseCapture();
2218 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2219 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2225 /***********************************************************************
2226 * LISTBOX_HandleTimer
2228 * Handle scrolling upon a timer event.
2229 * Return TRUE if scrolling should continue.
2231 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2236 if (descr
->top_item
) index
= descr
->top_item
- 1;
2240 if (descr
->top_item
) index
-= descr
->page_size
;
2243 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2244 if (index
== descr
->focus_item
) index
++;
2245 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2247 case LB_TIMER_RIGHT
:
2248 if (index
+ descr
->page_size
< descr
->nb_items
)
2249 index
+= descr
->page_size
;
2254 if (index
== descr
->focus_item
) return FALSE
;
2255 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2260 /***********************************************************************
2261 * LISTBOX_HandleSystemTimer
2263 * WM_SYSTIMER handler.
2265 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2267 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2269 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2270 LISTBOX_Timer
= LB_TIMER_NONE
;
2276 /***********************************************************************
2277 * LISTBOX_HandleMouseMove
2279 * WM_MOUSEMOVE handler.
2281 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2285 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2287 if (!descr
->captured
) return;
2289 if (descr
->style
& LBS_MULTICOLUMN
)
2292 else if (y
>= descr
->item_height
* descr
->page_size
)
2293 y
= descr
->item_height
* descr
->page_size
- 1;
2297 dir
= LB_TIMER_LEFT
;
2300 else if (x
>= descr
->width
)
2302 dir
= LB_TIMER_RIGHT
;
2303 x
= descr
->width
- 1;
2308 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2309 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2312 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2313 if (index
== -1) index
= descr
->focus_item
;
2314 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2316 /* Start/stop the system timer */
2318 if (dir
!= LB_TIMER_NONE
)
2319 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2320 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2321 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2322 LISTBOX_Timer
= dir
;
2326 /***********************************************************************
2327 * LISTBOX_HandleKeyDown
2329 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2332 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2333 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2334 bForceSelection
= FALSE
; /* only for single select list */
2336 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2338 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2339 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2340 (LPARAM
)descr
->self
);
2341 if (caret
== -2) return 0;
2343 if (caret
== -1) switch(key
)
2346 if (descr
->style
& LBS_MULTICOLUMN
)
2348 bForceSelection
= FALSE
;
2349 if (descr
->focus_item
>= descr
->page_size
)
2350 caret
= descr
->focus_item
- descr
->page_size
;
2355 caret
= descr
->focus_item
- 1;
2356 if (caret
< 0) caret
= 0;
2359 if (descr
->style
& LBS_MULTICOLUMN
)
2361 bForceSelection
= FALSE
;
2362 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2363 caret
= descr
->focus_item
+ descr
->page_size
;
2368 caret
= descr
->focus_item
+ 1;
2369 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2373 if (descr
->style
& LBS_MULTICOLUMN
)
2375 INT page
= descr
->width
/ descr
->column_width
;
2376 if (page
< 1) page
= 1;
2377 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2379 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2380 if (caret
< 0) caret
= 0;
2383 if (descr
->style
& LBS_MULTICOLUMN
)
2385 INT page
= descr
->width
/ descr
->column_width
;
2386 if (page
< 1) page
= 1;
2387 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2389 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2390 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2396 caret
= descr
->nb_items
- 1;
2399 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2400 else if (descr
->style
& LBS_MULTIPLESEL
)
2402 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2403 !descr
->items
[descr
->focus_item
].selected
,
2404 (descr
->style
& LBS_NOTIFY
) != 0 );
2408 bForceSelection
= FALSE
;
2410 if (bForceSelection
) /* focused item is used instead of key */
2411 caret
= descr
->focus_item
;
2414 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2415 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2416 !IS_MULTISELECT(descr
))
2417 descr
->anchor_item
= caret
;
2418 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2420 if (descr
->style
& LBS_MULTIPLESEL
)
2421 descr
->selected_item
= caret
;
2423 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2424 if (descr
->style
& LBS_NOTIFY
)
2426 if (descr
->lphc
&& IsWindowVisible( descr
->self
))
2428 /* make sure that combo parent doesn't hide us */
2429 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2431 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2438 /***********************************************************************
2439 * LISTBOX_HandleChar
2441 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2449 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2451 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2452 MAKEWPARAM(charW
, descr
->focus_item
),
2453 (LPARAM
)descr
->self
);
2454 if (caret
== -2) return 0;
2457 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2460 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2461 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2462 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2463 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2464 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2470 /***********************************************************************
2473 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2476 MEASUREITEMSTRUCT mis
;
2479 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2482 GetClientRect( hwnd
, &rect
);
2484 descr
->owner
= GetParent( descr
->self
);
2485 descr
->style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2486 descr
->width
= rect
.right
- rect
.left
;
2487 descr
->height
= rect
.bottom
- rect
.top
;
2488 descr
->items
= NULL
;
2489 descr
->nb_items
= 0;
2490 descr
->top_item
= 0;
2491 descr
->selected_item
= -1;
2492 descr
->focus_item
= 0;
2493 descr
->anchor_item
= -1;
2494 descr
->item_height
= 1;
2495 descr
->page_size
= 1;
2496 descr
->column_width
= 150;
2497 descr
->horz_extent
= 0;
2498 descr
->horz_pos
= 0;
2501 descr
->wheel_remain
= 0;
2502 descr
->caret_on
= !lphc
;
2503 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2504 descr
->in_focus
= FALSE
;
2505 descr
->captured
= FALSE
;
2507 descr
->locale
= GetUserDefaultLCID();
2512 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2513 descr
->owner
= lphc
->self
;
2516 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2518 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2520 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2521 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2522 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2523 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2525 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2527 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2529 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2530 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2534 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2535 mis
.CtlType
= ODT_LISTBOX
;
2540 mis
.itemHeight
= descr
->item_height
;
2541 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2542 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2546 OpenThemeData( descr
->self
, WC_LISTBOXW
);
2548 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2553 /***********************************************************************
2556 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2558 HTHEME theme
= GetWindowTheme( descr
->self
);
2559 CloseThemeData( theme
);
2560 LISTBOX_ResetContent( descr
);
2561 SetWindowLongPtrW( descr
->self
, 0, 0 );
2562 HeapFree( GetProcessHeap(), 0, descr
);
2567 /***********************************************************************
2568 * ListBoxWndProc_common
2570 static LRESULT CALLBACK
LISTBOX_WindowProc( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
2572 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2573 HEADCOMBO
*lphc
= NULL
;
2579 if (!IsWindow(hwnd
)) return 0;
2581 if (msg
== WM_CREATE
)
2583 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2584 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= lpcs
->lpCreateParams
;
2585 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2586 TRACE("creating hwnd %p descr %p\n", hwnd
, (void *)GetWindowLongPtrW( hwnd
, 0 ) );
2589 /* Ignore all other messages before we get a WM_CREATE */
2590 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
2592 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2594 TRACE("[%p]: msg %#x wp %08lx lp %08lx\n", descr
->self
, msg
, wParam
, lParam
);
2598 case LB_RESETCONTENT
:
2599 LISTBOX_ResetContent( descr
);
2600 LISTBOX_UpdateScroll( descr
);
2601 InvalidateRect( descr
->self
, NULL
, TRUE
);
2606 const WCHAR
*textW
= (const WCHAR
*)lParam
;
2607 INT index
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2608 return LISTBOX_InsertString( descr
, index
, textW
);
2611 case LB_INSERTSTRING
:
2612 return LISTBOX_InsertString( descr
, wParam
, (const WCHAR
*)lParam
);
2616 const WCHAR
*textW
= (const WCHAR
*)lParam
;
2617 INT index
= LISTBOX_FindFileStrPos( descr
, textW
);
2618 return LISTBOX_InsertString( descr
, index
, textW
);
2621 case LB_DELETESTRING
:
2622 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2623 return descr
->nb_items
;
2626 SetLastError(ERROR_INVALID_INDEX
);
2630 case LB_GETITEMDATA
:
2631 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2633 SetLastError(ERROR_INVALID_INDEX
);
2636 return descr
->items
[wParam
].data
;
2638 case LB_SETITEMDATA
:
2639 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2641 SetLastError(ERROR_INVALID_INDEX
);
2644 descr
->items
[wParam
].data
= lParam
;
2645 /* undocumented: returns TRUE, not LB_OKAY (0) */
2649 return descr
->nb_items
;
2652 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, TRUE
);
2655 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2657 SetLastError(ERROR_INVALID_INDEX
);
2660 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2661 return strlenW( descr
->items
[wParam
].str
);
2664 if (descr
->nb_items
== 0)
2666 if (!IS_MULTISELECT(descr
))
2667 return descr
->selected_item
;
2668 if (descr
->selected_item
!= -1)
2669 return descr
->selected_item
;
2670 return descr
->focus_item
;
2671 /* otherwise, if the user tries to move the selection with the */
2672 /* arrow keys, we will give the application something to choke on */
2673 case LB_GETTOPINDEX
:
2674 return descr
->top_item
;
2676 case LB_GETITEMHEIGHT
:
2677 return LISTBOX_GetItemHeight( descr
, wParam
);
2679 case LB_SETITEMHEIGHT
:
2680 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2682 case LB_ITEMFROMPOINT
:
2689 /* The hiword of the return value is not a client area
2690 hittest as suggested by MSDN, but rather a hittest on
2691 the returned listbox item. */
2693 if(descr
->nb_items
== 0)
2694 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2696 pt
.x
= (short)LOWORD(lParam
);
2697 pt
.y
= (short)HIWORD(lParam
);
2699 SetRect(&rect
, 0, 0, descr
->width
, descr
->height
);
2701 if(!PtInRect(&rect
, pt
))
2703 pt
.x
= min(pt
.x
, rect
.right
- 1);
2704 pt
.x
= max(pt
.x
, 0);
2705 pt
.y
= min(pt
.y
, rect
.bottom
- 1);
2706 pt
.y
= max(pt
.y
, 0);
2710 index
= LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
);
2714 index
= descr
->nb_items
- 1;
2717 return MAKELONG(index
, hit
? 0 : 1);
2720 case LB_SETCARETINDEX
:
2721 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2722 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2729 case LB_GETCARETINDEX
:
2730 return descr
->focus_item
;
2732 case LB_SETTOPINDEX
:
2733 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2735 case LB_SETCOLUMNWIDTH
:
2736 return LISTBOX_SetColumnWidth( descr
, wParam
);
2738 case LB_GETITEMRECT
:
2739 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2742 return LISTBOX_FindString( descr
, wParam
, (const WCHAR
*)lParam
, FALSE
);
2744 case LB_FINDSTRINGEXACT
:
2745 return LISTBOX_FindString( descr
, wParam
, (const WCHAR
*)lParam
, TRUE
);
2747 case LB_SELECTSTRING
:
2749 const WCHAR
*textW
= (const WCHAR
*)lParam
;
2752 if (HAS_STRINGS(descr
))
2753 TRACE("LB_SELECTSTRING: %s\n", debugstr_w(textW
));
2755 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2756 if (index
!= LB_ERR
)
2758 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2759 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2765 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2767 return descr
->items
[wParam
].selected
;
2770 return LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2773 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2774 LISTBOX_SetCaretIndex( descr
, wParam
, TRUE
);
2775 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2776 if (ret
!= LB_ERR
) ret
= descr
->selected_item
;
2779 case LB_GETSELCOUNT
:
2780 return LISTBOX_GetSelCount( descr
);
2782 case LB_GETSELITEMS
:
2783 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2785 case LB_SELITEMRANGE
:
2786 if (LOWORD(lParam
) <= HIWORD(lParam
))
2787 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2788 HIWORD(lParam
), wParam
);
2790 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2791 LOWORD(lParam
), wParam
);
2793 case LB_SELITEMRANGEEX
:
2794 if ((INT
)lParam
>= (INT
)wParam
)
2795 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2797 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
2799 case LB_GETHORIZONTALEXTENT
:
2800 return descr
->horz_extent
;
2802 case LB_SETHORIZONTALEXTENT
:
2803 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
2805 case LB_GETANCHORINDEX
:
2806 return descr
->anchor_item
;
2808 case LB_SETANCHORINDEX
:
2809 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2811 SetLastError(ERROR_INVALID_INDEX
);
2814 descr
->anchor_item
= (INT
)wParam
;
2818 return LISTBOX_Directory( descr
, wParam
, (const WCHAR
*)lParam
, msg
== LB_DIR
);
2821 return descr
->locale
;
2826 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
2828 ret
= descr
->locale
;
2829 descr
->locale
= (LCID
)wParam
;
2833 case LB_INITSTORAGE
:
2834 return LISTBOX_InitStorage( descr
, wParam
);
2837 return LISTBOX_SetCount( descr
, (INT
)wParam
);
2839 case LB_SETTABSTOPS
:
2840 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
);
2843 if (descr
->caret_on
)
2845 descr
->caret_on
= TRUE
;
2846 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2847 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2851 if (!descr
->caret_on
)
2853 descr
->caret_on
= FALSE
;
2854 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2855 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2858 case LB_GETLISTBOXINFO
:
2859 return descr
->page_size
;
2862 return LISTBOX_Destroy( descr
);
2865 InvalidateRect( descr
->self
, NULL
, TRUE
);
2869 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
2873 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2875 case WM_PRINTCLIENT
:
2879 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
2880 ret
= LISTBOX_Paint( descr
, hdc
);
2881 if( !wParam
) EndPaint( descr
->self
, &ps
);
2886 LISTBOX_NCPaint( descr
, (HRGN
)wParam
);
2890 LISTBOX_UpdateSize( descr
);
2893 return (LRESULT
)descr
->font
;
2895 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
2896 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
2899 descr
->in_focus
= TRUE
;
2900 descr
->caret_on
= TRUE
;
2901 if (descr
->focus_item
!= -1)
2902 LISTBOX_DrawFocusRect( descr
, TRUE
);
2903 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
2906 LISTBOX_HandleLButtonUp( descr
); /* Release capture if we have it */
2907 descr
->in_focus
= FALSE
;
2908 descr
->wheel_remain
= 0;
2909 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2910 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2911 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
2914 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
2916 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
2918 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2919 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
2920 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
2921 case WM_LBUTTONDOWN
:
2923 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
2924 (INT16
)LOWORD(lParam
),
2925 (INT16
)HIWORD(lParam
) );
2926 return LISTBOX_HandleLButtonDown( descr
, wParam
,
2927 (INT16
)LOWORD(lParam
),
2928 (INT16
)HIWORD(lParam
) );
2929 case WM_LBUTTONDBLCLK
:
2931 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
2932 (INT16
)LOWORD(lParam
),
2933 (INT16
)HIWORD(lParam
) );
2934 if (descr
->style
& LBS_NOTIFY
)
2935 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2938 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
2940 BOOL captured
= descr
->captured
;
2944 mousePos
.x
= (INT16
)LOWORD(lParam
);
2945 mousePos
.y
= (INT16
)HIWORD(lParam
);
2948 * If we are in a dropdown combobox, we simulate that
2949 * the mouse is captured to show the tracking of the item.
2951 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
2952 descr
->captured
= TRUE
;
2954 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
2956 descr
->captured
= captured
;
2958 else if (GetCapture() == descr
->self
)
2960 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
2961 (INT16
)HIWORD(lParam
) );
2971 * If the mouse button "up" is not in the listbox,
2972 * we make sure there is no selection by re-selecting the
2973 * item that was selected when the listbox was made visible.
2975 mousePos
.x
= (INT16
)LOWORD(lParam
);
2976 mousePos
.y
= (INT16
)HIWORD(lParam
);
2978 GetClientRect(descr
->self
, &clientRect
);
2981 * When the user clicks outside the combobox and the focus
2982 * is lost, the owning combobox will send a fake buttonup with
2983 * 0xFFFFFFF as the mouse location, we must also revert the
2984 * selection to the original selection.
2986 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
2987 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
2989 return LISTBOX_HandleLButtonUp( descr
);
2991 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
2993 /* for some reason Windows makes it possible to
2994 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2996 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
2997 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
2998 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3000 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3004 return LISTBOX_HandleKeyDown( descr
, wParam
);
3006 return LISTBOX_HandleChar( descr
, wParam
);
3009 return LISTBOX_HandleSystemTimer( descr
);
3011 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3014 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3015 wParam
, (LPARAM
)descr
->self
);
3016 TRACE("hbrush = %p\n", hbrush
);
3018 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3021 GetClientRect(descr
->self
, &rect
);
3022 FillRect((HDC
)wParam
, &rect
, hbrush
);
3027 if( lphc
) return 0;
3028 return SendMessageW( descr
->owner
, msg
, wParam
, lParam
);
3031 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3039 case WM_THEMECHANGED
:
3040 theme
= GetWindowTheme( hwnd
);
3041 CloseThemeData( theme
);
3042 OpenThemeData( hwnd
, WC_LISTBOXW
);
3046 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3047 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3048 hwnd
, msg
, wParam
, lParam
);
3051 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
3054 void LISTBOX_Register(void)
3058 memset(&wndClass
, 0, sizeof(wndClass
));
3059 wndClass
.style
= CS_PARENTDC
| CS_DBLCLKS
| CS_GLOBALCLASS
;
3060 wndClass
.lpfnWndProc
= LISTBOX_WindowProc
;
3061 wndClass
.cbClsExtra
= 0;
3062 wndClass
.cbWndExtra
= sizeof(LB_DESCR
*);
3063 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
3064 wndClass
.hbrBackground
= NULL
;
3065 wndClass
.lpszClassName
= WC_LISTBOXW
;
3066 RegisterClassW(&wndClass
);
3069 void COMBOLBOX_Register(void)
3071 static const WCHAR combolboxW
[] = {'C','o','m','b','o','L','B','o','x',0};
3074 memset(&wndClass
, 0, sizeof(wndClass
));
3075 wndClass
.style
= CS_SAVEBITS
| CS_DBLCLKS
| CS_DROPSHADOW
| CS_GLOBALCLASS
;
3076 wndClass
.lpfnWndProc
= LISTBOX_WindowProc
;
3077 wndClass
.cbClsExtra
= 0;
3078 wndClass
.cbWndExtra
= sizeof(LB_DESCR
*);
3079 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
3080 wndClass
.hbrBackground
= NULL
;
3081 wndClass
.lpszClassName
= combolboxW
;
3082 RegisterClassW(&wndClass
);