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 hdc
= GetDCEx(descr
->self
, region
, DCX_WINDOW
|DCX_INTERSECTRGN
);
1103 OffsetRect(&r
, -r
.left
, -r
.top
);
1105 if (IsThemeBackgroundPartiallyTransparent (theme
, 0, 0))
1106 DrawThemeParentBackground(descr
->self
, hdc
, &r
);
1107 DrawThemeBackground (theme
, hdc
, 0, 0, &r
, 0);
1108 ReleaseDC(descr
->self
, hdc
);
1111 /***********************************************************************
1112 * LISTBOX_InvalidateItems
1114 * Invalidate all items from a given item. If the specified item is not
1115 * visible, nothing happens.
1117 static void LISTBOX_InvalidateItems( LB_DESCR
*descr
, INT index
)
1121 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1123 if (descr
->style
& LBS_NOREDRAW
)
1125 descr
->style
|= LBS_DISPLAYCHANGED
;
1128 rect
.bottom
= descr
->height
;
1129 InvalidateRect( descr
->self
, &rect
, TRUE
);
1130 if (descr
->style
& LBS_MULTICOLUMN
)
1132 /* Repaint the other columns */
1133 rect
.left
= rect
.right
;
1134 rect
.right
= descr
->width
;
1136 InvalidateRect( descr
->self
, &rect
, TRUE
);
1141 static void LISTBOX_InvalidateItemRect( LB_DESCR
*descr
, INT index
)
1145 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1146 InvalidateRect( descr
->self
, &rect
, TRUE
);
1149 /***********************************************************************
1150 * LISTBOX_GetItemHeight
1152 static LRESULT
LISTBOX_GetItemHeight( const LB_DESCR
*descr
, INT index
)
1154 if (descr
->style
& LBS_OWNERDRAWVARIABLE
&& descr
->nb_items
> 0)
1156 if ((index
< 0) || (index
>= descr
->nb_items
))
1158 SetLastError(ERROR_INVALID_INDEX
);
1161 return descr
->items
[index
].height
;
1163 else return descr
->item_height
;
1167 /***********************************************************************
1168 * LISTBOX_SetItemHeight
1170 static LRESULT
LISTBOX_SetItemHeight( LB_DESCR
*descr
, INT index
, INT height
, BOOL repaint
)
1172 if (height
> MAXBYTE
)
1175 if (!height
) height
= 1;
1177 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1179 if ((index
< 0) || (index
>= descr
->nb_items
))
1181 SetLastError(ERROR_INVALID_INDEX
);
1184 TRACE("[%p]: item %d height = %d\n", descr
->self
, index
, height
);
1185 descr
->items
[index
].height
= height
;
1186 LISTBOX_UpdateScroll( descr
);
1188 LISTBOX_InvalidateItems( descr
, index
);
1190 else if (height
!= descr
->item_height
)
1192 TRACE("[%p]: new height = %d\n", descr
->self
, height
);
1193 descr
->item_height
= height
;
1194 LISTBOX_UpdatePage( descr
);
1195 LISTBOX_UpdateScroll( descr
);
1197 InvalidateRect( descr
->self
, 0, TRUE
);
1203 /***********************************************************************
1204 * LISTBOX_SetHorizontalPos
1206 static void LISTBOX_SetHorizontalPos( LB_DESCR
*descr
, INT pos
)
1210 if (pos
> descr
->horz_extent
- descr
->width
)
1211 pos
= descr
->horz_extent
- descr
->width
;
1212 if (pos
< 0) pos
= 0;
1213 if (!(diff
= descr
->horz_pos
- pos
)) return;
1214 TRACE("[%p]: new horz pos = %d\n", descr
->self
, pos
);
1215 descr
->horz_pos
= pos
;
1216 LISTBOX_UpdateScroll( descr
);
1217 if (abs(diff
) < descr
->width
)
1220 /* Invalidate the focused item so it will be repainted correctly */
1221 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1222 InvalidateRect( descr
->self
, &rect
, TRUE
);
1223 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
1224 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1227 InvalidateRect( descr
->self
, NULL
, TRUE
);
1231 /***********************************************************************
1232 * LISTBOX_SetHorizontalExtent
1234 static LRESULT
LISTBOX_SetHorizontalExtent( LB_DESCR
*descr
, INT extent
)
1236 if (descr
->style
& LBS_MULTICOLUMN
)
1238 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1239 TRACE("[%p]: new horz extent = %d\n", descr
->self
, extent
);
1240 descr
->horz_extent
= extent
;
1241 if (descr
->style
& WS_HSCROLL
) {
1243 info
.cbSize
= sizeof(info
);
1245 info
.nMax
= descr
->horz_extent
? descr
->horz_extent
- 1 : 0;
1246 info
.fMask
= SIF_RANGE
;
1247 if (descr
->style
& LBS_DISABLENOSCROLL
)
1248 info
.fMask
|= SIF_DISABLENOSCROLL
;
1249 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
1251 if (descr
->horz_pos
> extent
- descr
->width
)
1252 LISTBOX_SetHorizontalPos( descr
, extent
- descr
->width
);
1257 /***********************************************************************
1258 * LISTBOX_SetColumnWidth
1260 static LRESULT
LISTBOX_SetColumnWidth( LB_DESCR
*descr
, INT width
)
1262 if (width
== descr
->column_width
) return LB_OKAY
;
1263 TRACE("[%p]: new column width = %d\n", descr
->self
, width
);
1264 descr
->column_width
= width
;
1265 LISTBOX_UpdatePage( descr
);
1270 /***********************************************************************
1273 * Returns the item height.
1275 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1279 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1284 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1286 ERR("unable to get DC.\n" );
1289 if (font
) oldFont
= SelectObject( hdc
, font
);
1290 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1291 if (oldFont
) SelectObject( hdc
, oldFont
);
1292 ReleaseDC( descr
->self
, hdc
);
1294 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1295 if (!IS_OWNERDRAW(descr
))
1296 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1301 /***********************************************************************
1302 * LISTBOX_MakeItemVisible
1304 * Make sure that a given item is partially or fully visible.
1306 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1310 TRACE("current top item %d, index %d, fully %d\n", descr
->top_item
, index
, fully
);
1312 if (index
<= descr
->top_item
) top
= index
;
1313 else if (descr
->style
& LBS_MULTICOLUMN
)
1315 INT cols
= descr
->width
;
1316 if (!fully
) cols
+= descr
->column_width
- 1;
1317 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1319 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1320 top
= index
- descr
->page_size
* (cols
- 1);
1322 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1324 INT height
= fully
? descr
->items
[index
].height
: 1;
1325 for (top
= index
; top
> descr
->top_item
; top
--)
1326 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1330 if (index
< descr
->top_item
+ descr
->page_size
) return;
1331 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1332 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1333 top
= index
- descr
->page_size
+ 1;
1335 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1338 /***********************************************************************
1339 * LISTBOX_SetCaretIndex
1342 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1345 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1347 INT oldfocus
= descr
->focus_item
;
1349 TRACE("old focus %d, index %d\n", oldfocus
, index
);
1351 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1352 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1353 if (index
== oldfocus
) return LB_OKAY
;
1355 LISTBOX_DrawFocusRect( descr
, FALSE
);
1356 descr
->focus_item
= index
;
1358 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1359 LISTBOX_DrawFocusRect( descr
, TRUE
);
1365 /***********************************************************************
1366 * LISTBOX_SelectItemRange
1368 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1370 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1375 /* A few sanity checks */
1377 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1378 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1380 if (!descr
->nb_items
) return LB_OKAY
;
1382 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1383 if (first
< 0) first
= 0;
1384 if (last
< first
) return LB_OKAY
;
1386 if (on
) /* Turn selection on */
1388 for (i
= first
; i
<= last
; i
++)
1390 if (descr
->items
[i
].selected
) continue;
1391 descr
->items
[i
].selected
= TRUE
;
1392 LISTBOX_InvalidateItemRect(descr
, i
);
1395 else /* Turn selection off */
1397 for (i
= first
; i
<= last
; i
++)
1399 if (!descr
->items
[i
].selected
) continue;
1400 descr
->items
[i
].selected
= FALSE
;
1401 LISTBOX_InvalidateItemRect(descr
, i
);
1407 /***********************************************************************
1408 * LISTBOX_SetSelection
1410 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1411 BOOL on
, BOOL send_notify
)
1413 TRACE( "cur_sel=%d index=%d notify=%s\n",
1414 descr
->selected_item
, index
, send_notify
? "YES" : "NO" );
1416 if (descr
->style
& LBS_NOSEL
)
1418 descr
->selected_item
= index
;
1421 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1422 if (descr
->style
& LBS_MULTIPLESEL
)
1424 if (index
== -1) /* Select all items */
1425 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1426 else /* Only one item */
1427 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1431 INT oldsel
= descr
->selected_item
;
1432 if (index
== oldsel
) return LB_OKAY
;
1433 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1434 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1435 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1436 descr
->selected_item
= index
;
1437 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1438 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1439 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1441 if( descr
->lphc
) /* set selection change flag for parent combo */
1442 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1448 /***********************************************************************
1451 * Change the caret position and extend the selection to the new caret.
1453 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1455 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1457 if ((index
< 0) || (index
>= descr
->nb_items
))
1460 /* Important, repaint needs to be done in this order if
1461 you want to mimic Windows behavior:
1462 1. Remove the focus and paint the item
1463 2. Remove the selection and paint the item(s)
1464 3. Set the selection and repaint the item(s)
1465 4. Set the focus to 'index' and repaint the item */
1467 /* 1. remove the focus and repaint the item */
1468 LISTBOX_DrawFocusRect( descr
, FALSE
);
1470 /* 2. then turn off the previous selection */
1471 /* 3. repaint the new selected item */
1472 if (descr
->style
& LBS_EXTENDEDSEL
)
1474 if (descr
->anchor_item
!= -1)
1476 INT first
= min( index
, descr
->anchor_item
);
1477 INT last
= max( index
, descr
->anchor_item
);
1479 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1480 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1481 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1484 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1486 /* Set selection to new caret item */
1487 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1490 /* 4. repaint the new item with the focus */
1491 descr
->focus_item
= index
;
1492 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1493 LISTBOX_DrawFocusRect( descr
, TRUE
);
1497 /***********************************************************************
1498 * LISTBOX_InsertItem
1500 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1501 LPWSTR str
, ULONG_PTR data
)
1505 INT oldfocus
= descr
->focus_item
;
1507 if (index
== -1) index
= descr
->nb_items
;
1508 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1509 if (!descr
->items
) max_items
= 0;
1510 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1511 if (descr
->nb_items
== max_items
)
1513 /* We need to grow the array */
1514 max_items
+= LB_ARRAY_GRANULARITY
;
1516 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1517 max_items
* sizeof(LB_ITEMDATA
) );
1519 item
= HeapAlloc( GetProcessHeap(), 0,
1520 max_items
* sizeof(LB_ITEMDATA
) );
1523 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1526 descr
->items
= item
;
1529 /* Insert the item structure */
1531 item
= &descr
->items
[index
];
1532 if (index
< descr
->nb_items
)
1533 RtlMoveMemory( item
+ 1, item
,
1534 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1536 item
->data
= HAS_STRINGS(descr
) ? 0 : data
;
1538 item
->selected
= FALSE
;
1541 /* Get item height */
1543 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1545 MEASUREITEMSTRUCT mis
;
1546 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1548 mis
.CtlType
= ODT_LISTBOX
;
1551 mis
.itemData
= data
;
1552 mis
.itemHeight
= descr
->item_height
;
1553 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1554 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1555 TRACE("[%p]: measure item %d (%s) = %d\n",
1556 descr
->self
, index
, str
? debugstr_w(str
) : "", item
->height
);
1559 /* Repaint the items */
1561 LISTBOX_UpdateScroll( descr
);
1562 LISTBOX_InvalidateItems( descr
, index
);
1564 /* Move selection and focused item */
1565 /* If listbox was empty, set focus to the first item */
1566 if (descr
->nb_items
== 1)
1567 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1568 /* single select don't change selection index in win31 */
1569 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1571 descr
->selected_item
++;
1572 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1576 if (index
<= descr
->selected_item
)
1578 descr
->selected_item
++;
1579 descr
->focus_item
= oldfocus
; /* focus not changed */
1586 /***********************************************************************
1587 * LISTBOX_InsertString
1589 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
, LPCWSTR str
)
1591 LPWSTR new_str
= NULL
;
1594 if (HAS_STRINGS(descr
))
1596 static const WCHAR empty_stringW
[] = { 0 };
1597 if (!str
) str
= empty_stringW
;
1598 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1600 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1603 strcpyW(new_str
, str
);
1606 if (index
== -1) index
= descr
->nb_items
;
1607 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, (ULONG_PTR
)str
)) != 0)
1609 HeapFree( GetProcessHeap(), 0, new_str
);
1613 TRACE("[%p]: added item %d %s\n",
1614 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1619 /***********************************************************************
1620 * LISTBOX_DeleteItem
1622 * Delete the content of an item. 'index' must be a valid index.
1624 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1626 /* save the item data before it gets freed by LB_RESETCONTENT */
1627 ULONG_PTR item_data
= descr
->items
[index
].data
;
1628 LPWSTR item_str
= descr
->items
[index
].str
;
1630 if (!descr
->nb_items
)
1631 SendMessageW( descr
->self
, LB_RESETCONTENT
, 0, 0 );
1633 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1634 * while Win95 sends it for all items with user data.
1635 * It's probably better to send it too often than not
1636 * often enough, so this is what we do here.
1638 if (IS_OWNERDRAW(descr
) || item_data
)
1640 DELETEITEMSTRUCT dis
;
1641 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1643 dis
.CtlType
= ODT_LISTBOX
;
1646 dis
.hwndItem
= descr
->self
;
1647 dis
.itemData
= item_data
;
1648 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1650 if (HAS_STRINGS(descr
))
1651 HeapFree( GetProcessHeap(), 0, item_str
);
1655 /***********************************************************************
1656 * LISTBOX_RemoveItem
1658 * Remove an item from the listbox and delete its content.
1660 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1665 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1667 /* We need to invalidate the original rect instead of the updated one. */
1668 LISTBOX_InvalidateItems( descr
, index
);
1671 LISTBOX_DeleteItem( descr
, index
);
1673 if (!descr
->nb_items
) return LB_OKAY
;
1675 /* Remove the item */
1677 item
= &descr
->items
[index
];
1678 if (index
< descr
->nb_items
)
1679 RtlMoveMemory( item
, item
+ 1,
1680 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1681 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1683 /* Shrink the item array if possible */
1685 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1686 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1688 max_items
-= LB_ARRAY_GRANULARITY
;
1689 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1690 max_items
* sizeof(LB_ITEMDATA
) );
1691 if (item
) descr
->items
= item
;
1693 /* Repaint the items */
1695 LISTBOX_UpdateScroll( descr
);
1696 /* if we removed the scrollbar, reset the top of the list
1697 (correct for owner-drawn ???) */
1698 if (descr
->nb_items
== descr
->page_size
)
1699 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1701 /* Move selection and focused item */
1702 if (!IS_MULTISELECT(descr
))
1704 if (index
== descr
->selected_item
)
1705 descr
->selected_item
= -1;
1706 else if (index
< descr
->selected_item
)
1708 descr
->selected_item
--;
1709 if (ISWIN31
) /* win 31 do not change the selected item number */
1710 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1714 if (descr
->focus_item
>= descr
->nb_items
)
1716 descr
->focus_item
= descr
->nb_items
- 1;
1717 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1723 /***********************************************************************
1724 * LISTBOX_ResetContent
1726 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1730 for(i
= descr
->nb_items
- 1; i
>=0; i
--) LISTBOX_DeleteItem( descr
, i
);
1731 HeapFree( GetProcessHeap(), 0, descr
->items
);
1732 descr
->nb_items
= 0;
1733 descr
->top_item
= 0;
1734 descr
->selected_item
= -1;
1735 descr
->focus_item
= 0;
1736 descr
->anchor_item
= -1;
1737 descr
->items
= NULL
;
1741 /***********************************************************************
1744 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, INT count
)
1748 if (HAS_STRINGS(descr
))
1750 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1754 /* FIXME: this is far from optimal... */
1755 if (count
> descr
->nb_items
)
1757 while (count
> descr
->nb_items
)
1758 if ((ret
= LISTBOX_InsertString( descr
, -1, 0 )) < 0)
1761 else if (count
< descr
->nb_items
)
1763 while (count
< descr
->nb_items
)
1764 if ((ret
= LISTBOX_RemoveItem( descr
, (descr
->nb_items
- 1) )) < 0)
1768 InvalidateRect( descr
->self
, NULL
, TRUE
);
1773 /***********************************************************************
1776 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1777 LPCWSTR filespec
, BOOL long_names
)
1780 LRESULT ret
= LB_OKAY
;
1781 WIN32_FIND_DATAW entry
;
1783 LRESULT maxinsert
= LB_ERR
;
1785 /* don't scan directory if we just want drives exclusively */
1786 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1787 /* scan directory */
1788 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1790 int le
= GetLastError();
1791 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1798 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1800 static const WCHAR bracketW
[] = { ']',0 };
1801 static const WCHAR dotW
[] = { '.',0 };
1802 if (!(attrib
& DDL_DIRECTORY
) ||
1803 !strcmpW( entry
.cFileName
, dotW
)) continue;
1805 if (!long_names
&& entry
.cAlternateFileName
[0])
1806 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1808 strcpyW( buffer
+ 1, entry
.cFileName
);
1809 strcatW(buffer
, bracketW
);
1811 else /* not a directory */
1813 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1814 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1816 if ((attrib
& DDL_EXCLUSIVE
) &&
1817 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1820 if (!long_names
&& entry
.cAlternateFileName
[0])
1821 strcpyW( buffer
, entry
.cAlternateFileName
);
1823 strcpyW( buffer
, entry
.cFileName
);
1825 if (!long_names
) CharLowerW( buffer
);
1826 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1827 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1829 if (ret
<= maxinsert
) maxinsert
++; else maxinsert
= ret
;
1830 } while (FindNextFileW( handle
, &entry
));
1831 FindClose( handle
);
1839 if (attrib
& DDL_DRIVES
)
1841 WCHAR buffer
[] = {'[','-','a','-',']',0};
1842 WCHAR root
[] = {'A',':','\\',0};
1844 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1846 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1847 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1856 /***********************************************************************
1857 * LISTBOX_HandleVScroll
1859 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1863 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1867 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1870 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1873 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1874 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1877 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1878 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1880 case SB_THUMBPOSITION
:
1881 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1884 info
.cbSize
= sizeof(info
);
1885 info
.fMask
= SIF_TRACKPOS
;
1886 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1887 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1890 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1893 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1900 /***********************************************************************
1901 * LISTBOX_HandleHScroll
1903 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1908 if (descr
->style
& LBS_MULTICOLUMN
)
1913 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
1917 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
1921 page
= descr
->width
/ descr
->column_width
;
1922 if (page
< 1) page
= 1;
1923 LISTBOX_SetTopItem( descr
,
1924 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1927 page
= descr
->width
/ descr
->column_width
;
1928 if (page
< 1) page
= 1;
1929 LISTBOX_SetTopItem( descr
,
1930 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1932 case SB_THUMBPOSITION
:
1933 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
1936 info
.cbSize
= sizeof(info
);
1937 info
.fMask
= SIF_TRACKPOS
;
1938 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1939 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
1943 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1946 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1950 else if (descr
->horz_extent
)
1955 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
1958 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
1961 LISTBOX_SetHorizontalPos( descr
,
1962 descr
->horz_pos
- descr
->width
);
1965 LISTBOX_SetHorizontalPos( descr
,
1966 descr
->horz_pos
+ descr
->width
);
1968 case SB_THUMBPOSITION
:
1969 LISTBOX_SetHorizontalPos( descr
, pos
);
1972 info
.cbSize
= sizeof(info
);
1973 info
.fMask
= SIF_TRACKPOS
;
1974 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
1975 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
1978 LISTBOX_SetHorizontalPos( descr
, 0 );
1981 LISTBOX_SetHorizontalPos( descr
,
1982 descr
->horz_extent
- descr
->width
);
1989 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
1991 UINT pulScrollLines
= 3;
1993 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1995 /* if scrolling changes direction, ignore left overs */
1996 if ((delta
< 0 && descr
->wheel_remain
< 0) ||
1997 (delta
> 0 && descr
->wheel_remain
> 0))
1998 descr
->wheel_remain
+= delta
;
2000 descr
->wheel_remain
= delta
;
2002 if (descr
->wheel_remain
&& pulScrollLines
)
2005 pulScrollLines
= min((UINT
) descr
->page_size
, pulScrollLines
);
2006 cLineScroll
= pulScrollLines
* (float)descr
->wheel_remain
/ WHEEL_DELTA
;
2007 descr
->wheel_remain
-= WHEEL_DELTA
* cLineScroll
/ (int)pulScrollLines
;
2008 LISTBOX_SetTopItem( descr
, descr
->top_item
- cLineScroll
, TRUE
);
2013 /***********************************************************************
2014 * LISTBOX_HandleLButtonDown
2016 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
2018 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2020 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2021 descr
->self
, x
, y
, index
, descr
->focus_item
);
2023 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2025 if (!descr
->in_focus
)
2027 if( !descr
->lphc
) SetFocus( descr
->self
);
2028 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2031 if (index
== -1) return 0;
2035 if (descr
->style
& LBS_NOTIFY
)
2036 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2037 MAKELPARAM( x
, y
) );
2040 descr
->captured
= TRUE
;
2041 SetCapture( descr
->self
);
2043 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2045 /* we should perhaps make sure that all items are deselected
2046 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2047 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2048 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2051 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2052 if (keys
& MK_CONTROL
)
2054 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2055 LISTBOX_SetSelection( descr
, index
,
2056 !descr
->items
[index
].selected
,
2057 (descr
->style
& LBS_NOTIFY
) != 0);
2061 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2063 if (descr
->style
& LBS_EXTENDEDSEL
)
2065 LISTBOX_SetSelection( descr
, index
,
2066 descr
->items
[index
].selected
,
2067 (descr
->style
& LBS_NOTIFY
) != 0 );
2071 LISTBOX_SetSelection( descr
, index
,
2072 !descr
->items
[index
].selected
,
2073 (descr
->style
& LBS_NOTIFY
) != 0 );
2079 descr
->anchor_item
= index
;
2080 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2081 LISTBOX_SetSelection( descr
, index
,
2082 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2087 if (GetWindowLongW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2094 if (DragDetect( descr
->self
, pt
))
2095 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2102 /*************************************************************************
2103 * LISTBOX_HandleLButtonDownCombo [Internal]
2105 * Process LButtonDown message for the ComboListBox
2108 * pWnd [I] The windows internal structure
2109 * pDescr [I] The ListBox internal structure
2110 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2111 * x [I] X Mouse Coordinate
2112 * y [I] Y Mouse Coordinate
2115 * 0 since we are processing the WM_LBUTTONDOWN Message
2118 * This function is only to be used when a ListBox is a ComboListBox
2121 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2123 RECT clientRect
, screenRect
;
2129 GetClientRect(descr
->self
, &clientRect
);
2131 if(PtInRect(&clientRect
, mousePos
))
2133 /* MousePos is in client, resume normal processing */
2134 if (msg
== WM_LBUTTONDOWN
)
2136 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2137 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2139 else if (descr
->style
& LBS_NOTIFY
)
2140 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2144 POINT screenMousePos
;
2145 HWND hWndOldCapture
;
2147 /* Check the Non-Client Area */
2148 screenMousePos
= mousePos
;
2149 hWndOldCapture
= GetCapture();
2151 GetWindowRect(descr
->self
, &screenRect
);
2152 ClientToScreen(descr
->self
, &screenMousePos
);
2154 if(!PtInRect(&screenRect
, screenMousePos
))
2156 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2157 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2158 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2162 /* Check to see the NC is a scrollbar */
2164 LONG style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2165 /* Check Vertical scroll bar */
2166 if (style
& WS_VSCROLL
)
2168 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2169 if (PtInRect( &clientRect
, mousePos
))
2170 nHitTestType
= HTVSCROLL
;
2172 /* Check horizontal scroll bar */
2173 if (style
& WS_HSCROLL
)
2175 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2176 if (PtInRect( &clientRect
, mousePos
))
2177 nHitTestType
= HTHSCROLL
;
2179 /* Windows sends this message when a scrollbar is clicked
2182 if(nHitTestType
!= 0)
2184 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2185 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2187 /* Resume the Capture after scrolling is complete
2189 if(hWndOldCapture
!= 0)
2190 SetCapture(hWndOldCapture
);
2196 /***********************************************************************
2197 * LISTBOX_HandleLButtonUp
2199 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2201 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2202 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2203 LISTBOX_Timer
= LB_TIMER_NONE
;
2204 if (descr
->captured
)
2206 descr
->captured
= FALSE
;
2207 if (GetCapture() == descr
->self
) ReleaseCapture();
2208 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2209 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2215 /***********************************************************************
2216 * LISTBOX_HandleTimer
2218 * Handle scrolling upon a timer event.
2219 * Return TRUE if scrolling should continue.
2221 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2226 if (descr
->top_item
) index
= descr
->top_item
- 1;
2230 if (descr
->top_item
) index
-= descr
->page_size
;
2233 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2234 if (index
== descr
->focus_item
) index
++;
2235 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2237 case LB_TIMER_RIGHT
:
2238 if (index
+ descr
->page_size
< descr
->nb_items
)
2239 index
+= descr
->page_size
;
2244 if (index
== descr
->focus_item
) return FALSE
;
2245 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2250 /***********************************************************************
2251 * LISTBOX_HandleSystemTimer
2253 * WM_SYSTIMER handler.
2255 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2257 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2259 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2260 LISTBOX_Timer
= LB_TIMER_NONE
;
2266 /***********************************************************************
2267 * LISTBOX_HandleMouseMove
2269 * WM_MOUSEMOVE handler.
2271 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2275 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2277 if (!descr
->captured
) return;
2279 if (descr
->style
& LBS_MULTICOLUMN
)
2282 else if (y
>= descr
->item_height
* descr
->page_size
)
2283 y
= descr
->item_height
* descr
->page_size
- 1;
2287 dir
= LB_TIMER_LEFT
;
2290 else if (x
>= descr
->width
)
2292 dir
= LB_TIMER_RIGHT
;
2293 x
= descr
->width
- 1;
2298 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2299 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2302 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2303 if (index
== -1) index
= descr
->focus_item
;
2304 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2306 /* Start/stop the system timer */
2308 if (dir
!= LB_TIMER_NONE
)
2309 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2310 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2311 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2312 LISTBOX_Timer
= dir
;
2316 /***********************************************************************
2317 * LISTBOX_HandleKeyDown
2319 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2322 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2323 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2324 bForceSelection
= FALSE
; /* only for single select list */
2326 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2328 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2329 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2330 (LPARAM
)descr
->self
);
2331 if (caret
== -2) return 0;
2333 if (caret
== -1) switch(key
)
2336 if (descr
->style
& LBS_MULTICOLUMN
)
2338 bForceSelection
= FALSE
;
2339 if (descr
->focus_item
>= descr
->page_size
)
2340 caret
= descr
->focus_item
- descr
->page_size
;
2345 caret
= descr
->focus_item
- 1;
2346 if (caret
< 0) caret
= 0;
2349 if (descr
->style
& LBS_MULTICOLUMN
)
2351 bForceSelection
= FALSE
;
2352 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2353 caret
= descr
->focus_item
+ descr
->page_size
;
2358 caret
= descr
->focus_item
+ 1;
2359 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2363 if (descr
->style
& LBS_MULTICOLUMN
)
2365 INT page
= descr
->width
/ descr
->column_width
;
2366 if (page
< 1) page
= 1;
2367 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2369 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2370 if (caret
< 0) caret
= 0;
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
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2386 caret
= descr
->nb_items
- 1;
2389 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2390 else if (descr
->style
& LBS_MULTIPLESEL
)
2392 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2393 !descr
->items
[descr
->focus_item
].selected
,
2394 (descr
->style
& LBS_NOTIFY
) != 0 );
2398 bForceSelection
= FALSE
;
2400 if (bForceSelection
) /* focused item is used instead of key */
2401 caret
= descr
->focus_item
;
2404 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2405 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2406 !IS_MULTISELECT(descr
))
2407 descr
->anchor_item
= caret
;
2408 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2410 if (descr
->style
& LBS_MULTIPLESEL
)
2411 descr
->selected_item
= caret
;
2413 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2414 if (descr
->style
& LBS_NOTIFY
)
2416 if (descr
->lphc
&& IsWindowVisible( descr
->self
))
2418 /* make sure that combo parent doesn't hide us */
2419 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2421 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2428 /***********************************************************************
2429 * LISTBOX_HandleChar
2431 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2439 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2441 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2442 MAKEWPARAM(charW
, descr
->focus_item
),
2443 (LPARAM
)descr
->self
);
2444 if (caret
== -2) return 0;
2447 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2450 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2451 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2452 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2453 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2454 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2460 /***********************************************************************
2463 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2466 MEASUREITEMSTRUCT mis
;
2469 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2472 GetClientRect( hwnd
, &rect
);
2474 descr
->owner
= GetParent( descr
->self
);
2475 descr
->style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2476 descr
->width
= rect
.right
- rect
.left
;
2477 descr
->height
= rect
.bottom
- rect
.top
;
2478 descr
->items
= NULL
;
2479 descr
->nb_items
= 0;
2480 descr
->top_item
= 0;
2481 descr
->selected_item
= -1;
2482 descr
->focus_item
= 0;
2483 descr
->anchor_item
= -1;
2484 descr
->item_height
= 1;
2485 descr
->page_size
= 1;
2486 descr
->column_width
= 150;
2487 descr
->horz_extent
= 0;
2488 descr
->horz_pos
= 0;
2491 descr
->wheel_remain
= 0;
2492 descr
->caret_on
= !lphc
;
2493 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2494 descr
->in_focus
= FALSE
;
2495 descr
->captured
= FALSE
;
2497 descr
->locale
= GetUserDefaultLCID();
2502 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2503 descr
->owner
= lphc
->self
;
2506 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2508 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2510 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2511 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2512 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2513 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2515 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2517 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2519 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2520 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2524 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2525 mis
.CtlType
= ODT_LISTBOX
;
2530 mis
.itemHeight
= descr
->item_height
;
2531 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2532 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2536 OpenThemeData( descr
->self
, WC_LISTBOXW
);
2538 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2543 /***********************************************************************
2546 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2548 HTHEME theme
= GetWindowTheme( descr
->self
);
2549 CloseThemeData( theme
);
2550 LISTBOX_ResetContent( descr
);
2551 SetWindowLongPtrW( descr
->self
, 0, 0 );
2552 HeapFree( GetProcessHeap(), 0, descr
);
2557 /***********************************************************************
2558 * ListBoxWndProc_common
2560 static LRESULT CALLBACK
LISTBOX_WindowProc( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
2562 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2563 HEADCOMBO
*lphc
= NULL
;
2569 if (!IsWindow(hwnd
)) return 0;
2571 if (msg
== WM_CREATE
)
2573 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2574 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= lpcs
->lpCreateParams
;
2575 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2576 TRACE("creating hwnd %p descr %p\n", hwnd
, (void *)GetWindowLongPtrW( hwnd
, 0 ) );
2579 /* Ignore all other messages before we get a WM_CREATE */
2580 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
2582 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2584 TRACE("[%p]: msg %#x wp %08lx lp %08lx\n", descr
->self
, msg
, wParam
, lParam
);
2588 case LB_RESETCONTENT
:
2589 LISTBOX_ResetContent( descr
);
2590 LISTBOX_UpdateScroll( descr
);
2591 InvalidateRect( descr
->self
, NULL
, TRUE
);
2596 const WCHAR
*textW
= (const WCHAR
*)lParam
;
2597 INT index
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2598 return LISTBOX_InsertString( descr
, index
, textW
);
2601 case LB_INSERTSTRING
:
2602 return LISTBOX_InsertString( descr
, wParam
, (const WCHAR
*)lParam
);
2606 const WCHAR
*textW
= (const WCHAR
*)lParam
;
2607 INT index
= LISTBOX_FindFileStrPos( descr
, textW
);
2608 return LISTBOX_InsertString( descr
, index
, textW
);
2611 case LB_DELETESTRING
:
2612 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2613 return descr
->nb_items
;
2616 SetLastError(ERROR_INVALID_INDEX
);
2620 case LB_GETITEMDATA
:
2621 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2623 SetLastError(ERROR_INVALID_INDEX
);
2626 return descr
->items
[wParam
].data
;
2628 case LB_SETITEMDATA
:
2629 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2631 SetLastError(ERROR_INVALID_INDEX
);
2634 descr
->items
[wParam
].data
= lParam
;
2635 /* undocumented: returns TRUE, not LB_OKAY (0) */
2639 return descr
->nb_items
;
2642 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, TRUE
);
2645 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2647 SetLastError(ERROR_INVALID_INDEX
);
2650 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2651 return strlenW( descr
->items
[wParam
].str
);
2654 if (descr
->nb_items
== 0)
2656 if (!IS_MULTISELECT(descr
))
2657 return descr
->selected_item
;
2658 if (descr
->selected_item
!= -1)
2659 return descr
->selected_item
;
2660 return descr
->focus_item
;
2661 /* otherwise, if the user tries to move the selection with the */
2662 /* arrow keys, we will give the application something to choke on */
2663 case LB_GETTOPINDEX
:
2664 return descr
->top_item
;
2666 case LB_GETITEMHEIGHT
:
2667 return LISTBOX_GetItemHeight( descr
, wParam
);
2669 case LB_SETITEMHEIGHT
:
2670 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2672 case LB_ITEMFROMPOINT
:
2679 /* The hiword of the return value is not a client area
2680 hittest as suggested by MSDN, but rather a hittest on
2681 the returned listbox item. */
2683 if(descr
->nb_items
== 0)
2684 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2686 pt
.x
= (short)LOWORD(lParam
);
2687 pt
.y
= (short)HIWORD(lParam
);
2689 SetRect(&rect
, 0, 0, descr
->width
, descr
->height
);
2691 if(!PtInRect(&rect
, pt
))
2693 pt
.x
= min(pt
.x
, rect
.right
- 1);
2694 pt
.x
= max(pt
.x
, 0);
2695 pt
.y
= min(pt
.y
, rect
.bottom
- 1);
2696 pt
.y
= max(pt
.y
, 0);
2700 index
= LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
);
2704 index
= descr
->nb_items
- 1;
2707 return MAKELONG(index
, hit
? 0 : 1);
2710 case LB_SETCARETINDEX
:
2711 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2712 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2719 case LB_GETCARETINDEX
:
2720 return descr
->focus_item
;
2722 case LB_SETTOPINDEX
:
2723 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2725 case LB_SETCOLUMNWIDTH
:
2726 return LISTBOX_SetColumnWidth( descr
, wParam
);
2728 case LB_GETITEMRECT
:
2729 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2732 return LISTBOX_FindString( descr
, wParam
, (const WCHAR
*)lParam
, FALSE
);
2734 case LB_FINDSTRINGEXACT
:
2735 return LISTBOX_FindString( descr
, wParam
, (const WCHAR
*)lParam
, TRUE
);
2737 case LB_SELECTSTRING
:
2739 const WCHAR
*textW
= (const WCHAR
*)lParam
;
2742 if (HAS_STRINGS(descr
))
2743 TRACE("LB_SELECTSTRING: %s\n", debugstr_w(textW
));
2745 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2746 if (index
!= LB_ERR
)
2748 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2749 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2755 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2757 return descr
->items
[wParam
].selected
;
2760 return LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2763 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2764 LISTBOX_SetCaretIndex( descr
, wParam
, TRUE
);
2765 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2766 if (ret
!= LB_ERR
) ret
= descr
->selected_item
;
2769 case LB_GETSELCOUNT
:
2770 return LISTBOX_GetSelCount( descr
);
2772 case LB_GETSELITEMS
:
2773 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2775 case LB_SELITEMRANGE
:
2776 if (LOWORD(lParam
) <= HIWORD(lParam
))
2777 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2778 HIWORD(lParam
), wParam
);
2780 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2781 LOWORD(lParam
), wParam
);
2783 case LB_SELITEMRANGEEX
:
2784 if ((INT
)lParam
>= (INT
)wParam
)
2785 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2787 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
2789 case LB_GETHORIZONTALEXTENT
:
2790 return descr
->horz_extent
;
2792 case LB_SETHORIZONTALEXTENT
:
2793 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
2795 case LB_GETANCHORINDEX
:
2796 return descr
->anchor_item
;
2798 case LB_SETANCHORINDEX
:
2799 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2801 SetLastError(ERROR_INVALID_INDEX
);
2804 descr
->anchor_item
= (INT
)wParam
;
2808 return LISTBOX_Directory( descr
, wParam
, (const WCHAR
*)lParam
, msg
== LB_DIR
);
2811 return descr
->locale
;
2816 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
2818 ret
= descr
->locale
;
2819 descr
->locale
= (LCID
)wParam
;
2823 case LB_INITSTORAGE
:
2824 return LISTBOX_InitStorage( descr
, wParam
);
2827 return LISTBOX_SetCount( descr
, (INT
)wParam
);
2829 case LB_SETTABSTOPS
:
2830 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
);
2833 if (descr
->caret_on
)
2835 descr
->caret_on
= TRUE
;
2836 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2837 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2841 if (!descr
->caret_on
)
2843 descr
->caret_on
= FALSE
;
2844 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2845 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2848 case LB_GETLISTBOXINFO
:
2849 return descr
->page_size
;
2852 return LISTBOX_Destroy( descr
);
2855 InvalidateRect( descr
->self
, NULL
, TRUE
);
2859 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
2863 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2865 case WM_PRINTCLIENT
:
2869 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
2870 ret
= LISTBOX_Paint( descr
, hdc
);
2871 if( !wParam
) EndPaint( descr
->self
, &ps
);
2876 LISTBOX_NCPaint( descr
, (HRGN
)wParam
);
2880 LISTBOX_UpdateSize( descr
);
2883 return (LRESULT
)descr
->font
;
2885 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
2886 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
2889 descr
->in_focus
= TRUE
;
2890 descr
->caret_on
= TRUE
;
2891 if (descr
->focus_item
!= -1)
2892 LISTBOX_DrawFocusRect( descr
, TRUE
);
2893 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
2896 LISTBOX_HandleLButtonUp( descr
); /* Release capture if we have it */
2897 descr
->in_focus
= FALSE
;
2898 descr
->wheel_remain
= 0;
2899 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2900 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2901 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
2904 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
2906 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
2908 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2909 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
2910 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
2911 case WM_LBUTTONDOWN
:
2913 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
2914 (INT16
)LOWORD(lParam
),
2915 (INT16
)HIWORD(lParam
) );
2916 return LISTBOX_HandleLButtonDown( descr
, wParam
,
2917 (INT16
)LOWORD(lParam
),
2918 (INT16
)HIWORD(lParam
) );
2919 case WM_LBUTTONDBLCLK
:
2921 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
2922 (INT16
)LOWORD(lParam
),
2923 (INT16
)HIWORD(lParam
) );
2924 if (descr
->style
& LBS_NOTIFY
)
2925 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2928 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
2930 BOOL captured
= descr
->captured
;
2934 mousePos
.x
= (INT16
)LOWORD(lParam
);
2935 mousePos
.y
= (INT16
)HIWORD(lParam
);
2938 * If we are in a dropdown combobox, we simulate that
2939 * the mouse is captured to show the tracking of the item.
2941 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
2942 descr
->captured
= TRUE
;
2944 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
2946 descr
->captured
= captured
;
2948 else if (GetCapture() == descr
->self
)
2950 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
2951 (INT16
)HIWORD(lParam
) );
2961 * If the mouse button "up" is not in the listbox,
2962 * we make sure there is no selection by re-selecting the
2963 * item that was selected when the listbox was made visible.
2965 mousePos
.x
= (INT16
)LOWORD(lParam
);
2966 mousePos
.y
= (INT16
)HIWORD(lParam
);
2968 GetClientRect(descr
->self
, &clientRect
);
2971 * When the user clicks outside the combobox and the focus
2972 * is lost, the owning combobox will send a fake buttonup with
2973 * 0xFFFFFFF as the mouse location, we must also revert the
2974 * selection to the original selection.
2976 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
2977 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
2979 return LISTBOX_HandleLButtonUp( descr
);
2981 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
2983 /* for some reason Windows makes it possible to
2984 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2986 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
2987 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
2988 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
2990 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
2994 return LISTBOX_HandleKeyDown( descr
, wParam
);
2996 return LISTBOX_HandleChar( descr
, wParam
);
2999 return LISTBOX_HandleSystemTimer( descr
);
3001 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3004 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3005 wParam
, (LPARAM
)descr
->self
);
3006 TRACE("hbrush = %p\n", hbrush
);
3008 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3011 GetClientRect(descr
->self
, &rect
);
3012 FillRect((HDC
)wParam
, &rect
, hbrush
);
3017 if( lphc
) return 0;
3018 return SendMessageW( descr
->owner
, msg
, wParam
, lParam
);
3021 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3029 case WM_THEMECHANGED
:
3030 theme
= GetWindowTheme( hwnd
);
3031 CloseThemeData( theme
);
3032 OpenThemeData( hwnd
, WC_LISTBOXW
);
3036 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3037 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3038 hwnd
, msg
, wParam
, lParam
);
3041 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
3044 void LISTBOX_Register(void)
3048 memset(&wndClass
, 0, sizeof(wndClass
));
3049 wndClass
.style
= CS_PARENTDC
| CS_DBLCLKS
| CS_GLOBALCLASS
;
3050 wndClass
.lpfnWndProc
= LISTBOX_WindowProc
;
3051 wndClass
.cbClsExtra
= 0;
3052 wndClass
.cbWndExtra
= sizeof(LB_DESCR
*);
3053 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
3054 wndClass
.hbrBackground
= NULL
;
3055 wndClass
.lpszClassName
= WC_LISTBOXW
;
3056 RegisterClassW(&wndClass
);
3059 void COMBOLBOX_Register(void)
3061 static const WCHAR combolboxW
[] = {'C','o','m','b','o','L','B','o','x',0};
3064 memset(&wndClass
, 0, sizeof(wndClass
));
3065 wndClass
.style
= CS_SAVEBITS
| CS_DBLCLKS
| CS_DROPSHADOW
| CS_GLOBALCLASS
;
3066 wndClass
.lpfnWndProc
= LISTBOX_WindowProc
;
3067 wndClass
.cbClsExtra
= 0;
3068 wndClass
.cbWndExtra
= sizeof(LB_DESCR
*);
3069 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
3070 wndClass
.hbrBackground
= NULL
;
3071 wndClass
.lpszClassName
= combolboxW
;
3072 RegisterClassW(&wndClass
);