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(listbox
);
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 dx
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
281 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
284 if (index
> descr
->top_item
)
286 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
287 dy
-= descr
->items
[i
].height
;
291 for (i
= index
; i
< descr
->top_item
; i
++)
292 dy
+= descr
->items
[i
].height
;
296 dy
= (descr
->top_item
- index
) * descr
->item_height
;
298 ScrollWindowEx( descr
->self
, dx
, dy
, NULL
, NULL
, 0, NULL
,
299 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
302 InvalidateRect( descr
->self
, NULL
, TRUE
);
303 descr
->top_item
= index
;
304 LISTBOX_UpdateScroll( descr
);
309 /***********************************************************************
312 * Update the page size. Should be called when the size of
313 * the client area or the item height changes.
315 static void LISTBOX_UpdatePage( LB_DESCR
*descr
)
319 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
321 if (page_size
== descr
->page_size
) return;
322 descr
->page_size
= page_size
;
323 if (descr
->style
& LBS_MULTICOLUMN
)
324 InvalidateRect( descr
->self
, NULL
, TRUE
);
325 LISTBOX_SetTopItem( descr
, descr
->top_item
, FALSE
);
329 /***********************************************************************
332 * Update the size of the listbox. Should be called when the size of
333 * the client area changes.
335 static void LISTBOX_UpdateSize( LB_DESCR
*descr
)
339 GetClientRect( descr
->self
, &rect
);
340 descr
->width
= rect
.right
- rect
.left
;
341 descr
->height
= rect
.bottom
- rect
.top
;
342 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
347 GetWindowRect( descr
->self
, &rect
);
348 if(descr
->item_height
!= 0)
349 remaining
= descr
->height
% descr
->item_height
;
352 if ((descr
->height
> descr
->item_height
) && remaining
)
354 TRACE("[%p]: changing height %d -> %d\n",
355 descr
->self
, descr
->height
, descr
->height
- remaining
);
356 SetWindowPos( descr
->self
, 0, 0, 0, rect
.right
- rect
.left
,
357 rect
.bottom
- rect
.top
- remaining
,
358 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
362 TRACE("[%p]: new size = %d,%d\n", descr
->self
, descr
->width
, descr
->height
);
363 LISTBOX_UpdatePage( descr
);
364 LISTBOX_UpdateScroll( descr
);
366 /* Invalidate the focused item so it will be repainted correctly */
367 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
369 InvalidateRect( descr
->self
, &rect
, FALSE
);
374 /***********************************************************************
375 * LISTBOX_GetItemRect
377 * Get the rectangle enclosing an item, in listbox client coordinates.
378 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
380 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
)
382 /* Index <= 0 is legal even on empty listboxes */
383 if (index
&& (index
>= descr
->nb_items
))
386 SetLastError(ERROR_INVALID_INDEX
);
389 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
390 if (descr
->style
& LBS_MULTICOLUMN
)
392 INT col
= (index
/ descr
->page_size
) -
393 (descr
->top_item
/ descr
->page_size
);
394 rect
->left
+= col
* descr
->column_width
;
395 rect
->right
= rect
->left
+ descr
->column_width
;
396 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
397 rect
->bottom
= rect
->top
+ descr
->item_height
;
399 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
402 rect
->right
+= descr
->horz_pos
;
403 if ((index
>= 0) && (index
< descr
->nb_items
))
405 if (index
< descr
->top_item
)
407 for (i
= descr
->top_item
-1; i
>= index
; i
--)
408 rect
->top
-= descr
->items
[i
].height
;
412 for (i
= descr
->top_item
; i
< index
; i
++)
413 rect
->top
+= descr
->items
[i
].height
;
415 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
421 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
422 rect
->bottom
= rect
->top
+ descr
->item_height
;
423 rect
->right
+= descr
->horz_pos
;
426 TRACE("item %d, rect %s\n", index
, wine_dbgstr_rect(rect
));
428 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
429 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
433 /***********************************************************************
434 * LISTBOX_GetItemFromPoint
436 * Return the item nearest from point (x,y) (in client coordinates).
438 static INT
LISTBOX_GetItemFromPoint( const LB_DESCR
*descr
, INT x
, INT y
)
440 INT index
= descr
->top_item
;
442 if (!descr
->nb_items
) return -1; /* No items */
443 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
448 while (index
< descr
->nb_items
)
450 if ((pos
+= descr
->items
[index
].height
) > y
) break;
459 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
463 else if (descr
->style
& LBS_MULTICOLUMN
)
465 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
466 if (y
>= 0) index
+= y
/ descr
->item_height
;
467 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
468 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
472 index
+= (y
/ descr
->item_height
);
474 if (index
< 0) return 0;
475 if (index
>= descr
->nb_items
) return -1;
480 /***********************************************************************
485 static void LISTBOX_PaintItem( LB_DESCR
*descr
, HDC hdc
, const RECT
*rect
,
486 INT index
, UINT action
, BOOL ignoreFocus
)
488 LB_ITEMDATA
*item
= NULL
;
489 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
491 if (IS_OWNERDRAW(descr
))
499 if (action
== ODA_FOCUS
)
500 DrawFocusRect( hdc
, rect
);
502 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
506 /* some programs mess with the clipping region when
507 drawing the item, *and* restore the previous region
508 after they are done, so a region has better to exist
509 else everything ends clipped */
510 GetClientRect(descr
->self
, &r
);
511 hrgn
= set_control_clipping( hdc
, &r
);
513 dis
.CtlType
= ODT_LISTBOX
;
514 dis
.CtlID
= GetWindowLongPtrW( descr
->self
, GWLP_ID
);
515 dis
.hwndItem
= descr
->self
;
516 dis
.itemAction
= action
;
520 if (item
->selected
) dis
.itemState
|= ODS_SELECTED
;
521 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
523 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
524 if (!IsWindowEnabled(descr
->self
)) dis
.itemState
|= ODS_DISABLED
;
525 dis
.itemData
= item
->data
;
527 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
528 descr
->self
, index
, debugstr_w(item
->str
), action
,
529 dis
.itemState
, wine_dbgstr_rect(rect
) );
530 SendMessageW(descr
->owner
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
531 SelectClipRgn( hdc
, hrgn
);
532 if (hrgn
) DeleteObject( hrgn
);
536 COLORREF oldText
= 0, oldBk
= 0;
538 if (action
== ODA_FOCUS
)
540 DrawFocusRect( hdc
, rect
);
543 if (item
&& item
->selected
)
545 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
546 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
549 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
550 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
551 wine_dbgstr_rect(rect
) );
553 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
554 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
555 else if (!(descr
->style
& LBS_USETABSTOPS
))
556 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
557 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
558 strlenW(item
->str
), NULL
);
561 /* Output empty string to paint background in the full width. */
562 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
563 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
564 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
565 item
->str
, strlenW(item
->str
),
566 descr
->nb_tabs
, descr
->tabs
, 0);
568 if (item
&& item
->selected
)
570 SetBkColor( hdc
, oldBk
);
571 SetTextColor( hdc
, oldText
);
573 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
575 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
580 /***********************************************************************
583 * Change the redraw flag.
585 static void LISTBOX_SetRedraw( LB_DESCR
*descr
, BOOL on
)
589 if (!(descr
->style
& LBS_NOREDRAW
)) return;
590 descr
->style
&= ~LBS_NOREDRAW
;
591 if (descr
->style
& LBS_DISPLAYCHANGED
)
592 { /* page was changed while setredraw false, refresh automatically */
593 InvalidateRect(descr
->self
, NULL
, TRUE
);
594 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
595 { /* reset top of page if less than number of items/page */
596 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
597 if (descr
->top_item
< 0) descr
->top_item
= 0;
599 descr
->style
&= ~LBS_DISPLAYCHANGED
;
601 LISTBOX_UpdateScroll( descr
);
603 else descr
->style
|= LBS_NOREDRAW
;
607 /***********************************************************************
608 * LISTBOX_RepaintItem
610 * Repaint a single item synchronously.
612 static void LISTBOX_RepaintItem( LB_DESCR
*descr
, INT index
, UINT action
)
617 HBRUSH hbrush
, oldBrush
= 0;
619 /* Do not repaint the item if the item is not visible */
620 if (!IsWindowVisible(descr
->self
)) return;
621 if (descr
->style
& LBS_NOREDRAW
)
623 descr
->style
|= LBS_DISPLAYCHANGED
;
626 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
627 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
628 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
629 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
630 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
631 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
632 if (!IsWindowEnabled(descr
->self
))
633 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
634 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
635 LISTBOX_PaintItem( descr
, hdc
, &rect
, index
, action
, TRUE
);
636 if (oldFont
) SelectObject( hdc
, oldFont
);
637 if (oldBrush
) SelectObject( hdc
, oldBrush
);
638 ReleaseDC( descr
->self
, hdc
);
642 /***********************************************************************
643 * LISTBOX_DrawFocusRect
645 static void LISTBOX_DrawFocusRect( LB_DESCR
*descr
, BOOL on
)
651 /* Do not repaint the item if the item is not visible */
652 if (!IsWindowVisible(descr
->self
)) return;
654 if (descr
->focus_item
== -1) return;
655 if (!descr
->caret_on
|| !descr
->in_focus
) return;
657 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) != 1) return;
658 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
659 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
660 if (!IsWindowEnabled(descr
->self
))
661 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
662 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
663 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, !on
);
664 if (oldFont
) SelectObject( hdc
, oldFont
);
665 ReleaseDC( descr
->self
, hdc
);
669 /***********************************************************************
670 * LISTBOX_InitStorage
672 static LRESULT
LISTBOX_InitStorage( LB_DESCR
*descr
, INT nb_items
)
676 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
677 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
679 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
680 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
681 nb_items
* sizeof(LB_ITEMDATA
));
684 item
= HeapAlloc( GetProcessHeap(), 0,
685 nb_items
* sizeof(LB_ITEMDATA
));
690 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
698 /***********************************************************************
699 * LISTBOX_SetTabStops
701 static BOOL
LISTBOX_SetTabStops( LB_DESCR
*descr
, INT count
, LPINT tabs
)
705 if (!(descr
->style
& LBS_USETABSTOPS
))
707 SetLastError(ERROR_LB_WITHOUT_TABSTOPS
);
711 HeapFree( GetProcessHeap(), 0, descr
->tabs
);
712 if (!(descr
->nb_tabs
= count
))
717 if (!(descr
->tabs
= HeapAlloc( GetProcessHeap(), 0,
718 descr
->nb_tabs
* sizeof(INT
) )))
720 memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
722 /* convert into "dialog units"*/
723 for (i
= 0; i
< descr
->nb_tabs
; i
++)
724 descr
->tabs
[i
] = MulDiv(descr
->tabs
[i
], descr
->avg_char_width
, 4);
730 /***********************************************************************
733 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPWSTR buffer
, BOOL unicode
)
737 if ((index
< 0) || (index
>= descr
->nb_items
))
739 SetLastError(ERROR_INVALID_INDEX
);
743 if (HAS_STRINGS(descr
))
746 return strlenW(descr
->items
[index
].str
);
748 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
750 __TRY
/* hide a Delphi bug that passes a read-only buffer */
752 strcpyW( buffer
, descr
->items
[index
].str
);
753 len
= strlenW(buffer
);
757 WARN( "got an invalid buffer (Delphi bug?)\n" );
758 SetLastError( ERROR_INVALID_PARAMETER
);
765 *((DWORD
*)buffer
) = *(DWORD
*)&descr
->items
[index
].data
;
771 static inline INT
LISTBOX_lstrcmpiW( LCID lcid
, LPCWSTR str1
, LPCWSTR str2
)
773 INT ret
= CompareStringW( lcid
, NORM_IGNORECASE
, str1
, -1, str2
, -1 );
774 if (ret
== CSTR_LESS_THAN
)
776 if (ret
== CSTR_EQUAL
)
778 if (ret
== CSTR_GREATER_THAN
)
783 /***********************************************************************
784 * LISTBOX_FindStringPos
786 * Find the nearest string located before a given string in sort order.
787 * If 'exact' is TRUE, return an error if we don't get an exact match.
789 static INT
LISTBOX_FindStringPos( LB_DESCR
*descr
, LPCWSTR str
, BOOL exact
)
791 INT index
, min
, max
, res
;
793 if (!descr
->nb_items
|| !(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
796 max
= descr
->nb_items
- 1;
799 index
= (min
+ max
) / 2;
800 if (HAS_STRINGS(descr
))
801 res
= LISTBOX_lstrcmpiW( descr
->locale
, descr
->items
[index
].str
, 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
= descr
->items
[index
].data
;
815 cis
.itemData2
= (ULONG_PTR
)str
;
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
- 1;
821 else min
= index
+ 1;
823 return exact
? -1 : min
;
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 column_width
)
1264 TRACE("[%p]: new column width = %d\n", descr
->self
, column_width
);
1266 GetClientRect(descr
->self
, &rect
);
1267 descr
->width
= rect
.right
- rect
.left
;
1268 descr
->height
= rect
.bottom
- rect
.top
;
1269 descr
->column_width
= column_width
;
1271 LISTBOX_UpdatePage(descr
);
1272 LISTBOX_UpdateScroll(descr
);
1277 /***********************************************************************
1280 * Returns the item height.
1282 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1286 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1291 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1293 ERR("unable to get DC.\n" );
1296 if (font
) oldFont
= SelectObject( hdc
, font
);
1297 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1298 if (oldFont
) SelectObject( hdc
, oldFont
);
1299 ReleaseDC( descr
->self
, hdc
);
1301 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1302 if (!IS_OWNERDRAW(descr
))
1303 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1308 /***********************************************************************
1309 * LISTBOX_MakeItemVisible
1311 * Make sure that a given item is partially or fully visible.
1313 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1317 TRACE("current top item %d, index %d, fully %d\n", descr
->top_item
, index
, fully
);
1319 if (index
<= descr
->top_item
) top
= index
;
1320 else if (descr
->style
& LBS_MULTICOLUMN
)
1322 INT cols
= descr
->width
;
1323 if (!fully
) cols
+= descr
->column_width
- 1;
1324 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1326 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1327 top
= index
- descr
->page_size
* (cols
- 1);
1329 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1331 INT height
= fully
? descr
->items
[index
].height
: 1;
1332 for (top
= index
; top
> descr
->top_item
; top
--)
1333 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1337 if (index
< descr
->top_item
+ descr
->page_size
) return;
1338 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1339 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1340 top
= index
- descr
->page_size
+ 1;
1342 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1345 /***********************************************************************
1346 * LISTBOX_SetCaretIndex
1349 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1352 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1354 INT oldfocus
= descr
->focus_item
;
1356 TRACE("old focus %d, index %d\n", oldfocus
, index
);
1358 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1359 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1360 if (index
== oldfocus
) return LB_OKAY
;
1362 LISTBOX_DrawFocusRect( descr
, FALSE
);
1363 descr
->focus_item
= index
;
1365 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1366 LISTBOX_DrawFocusRect( descr
, TRUE
);
1372 /***********************************************************************
1373 * LISTBOX_SelectItemRange
1375 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1377 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1382 /* A few sanity checks */
1384 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1385 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1387 if (!descr
->nb_items
) return LB_OKAY
;
1389 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1390 if (first
< 0) first
= 0;
1391 if (last
< first
) return LB_OKAY
;
1393 if (on
) /* Turn selection on */
1395 for (i
= first
; i
<= last
; i
++)
1397 if (descr
->items
[i
].selected
) continue;
1398 descr
->items
[i
].selected
= TRUE
;
1399 LISTBOX_InvalidateItemRect(descr
, i
);
1402 else /* Turn selection off */
1404 for (i
= first
; i
<= last
; i
++)
1406 if (!descr
->items
[i
].selected
) continue;
1407 descr
->items
[i
].selected
= FALSE
;
1408 LISTBOX_InvalidateItemRect(descr
, i
);
1414 /***********************************************************************
1415 * LISTBOX_SetSelection
1417 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1418 BOOL on
, BOOL send_notify
)
1420 TRACE( "cur_sel=%d index=%d notify=%s\n",
1421 descr
->selected_item
, index
, send_notify
? "YES" : "NO" );
1423 if (descr
->style
& LBS_NOSEL
)
1425 descr
->selected_item
= index
;
1428 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1429 if (descr
->style
& LBS_MULTIPLESEL
)
1431 if (index
== -1) /* Select all items */
1432 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1433 else /* Only one item */
1434 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1438 INT oldsel
= descr
->selected_item
;
1439 if (index
== oldsel
) return LB_OKAY
;
1440 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1441 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1442 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1443 descr
->selected_item
= index
;
1444 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1445 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1446 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1448 if( descr
->lphc
) /* set selection change flag for parent combo */
1449 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1455 /***********************************************************************
1458 * Change the caret position and extend the selection to the new caret.
1460 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1462 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1464 if ((index
< 0) || (index
>= descr
->nb_items
))
1467 /* Important, repaint needs to be done in this order if
1468 you want to mimic Windows behavior:
1469 1. Remove the focus and paint the item
1470 2. Remove the selection and paint the item(s)
1471 3. Set the selection and repaint the item(s)
1472 4. Set the focus to 'index' and repaint the item */
1474 /* 1. remove the focus and repaint the item */
1475 LISTBOX_DrawFocusRect( descr
, FALSE
);
1477 /* 2. then turn off the previous selection */
1478 /* 3. repaint the new selected item */
1479 if (descr
->style
& LBS_EXTENDEDSEL
)
1481 if (descr
->anchor_item
!= -1)
1483 INT first
= min( index
, descr
->anchor_item
);
1484 INT last
= max( index
, descr
->anchor_item
);
1486 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1487 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1488 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1491 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1493 /* Set selection to new caret item */
1494 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1497 /* 4. repaint the new item with the focus */
1498 descr
->focus_item
= index
;
1499 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1500 LISTBOX_DrawFocusRect( descr
, TRUE
);
1504 /***********************************************************************
1505 * LISTBOX_InsertItem
1507 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1508 LPWSTR str
, ULONG_PTR data
)
1512 INT oldfocus
= descr
->focus_item
;
1514 if (index
== -1) index
= descr
->nb_items
;
1515 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1516 if (!descr
->items
) max_items
= 0;
1517 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1518 if (descr
->nb_items
== max_items
)
1520 /* We need to grow the array */
1521 max_items
+= LB_ARRAY_GRANULARITY
;
1523 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1524 max_items
* sizeof(LB_ITEMDATA
) );
1526 item
= HeapAlloc( GetProcessHeap(), 0,
1527 max_items
* sizeof(LB_ITEMDATA
) );
1530 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1533 descr
->items
= item
;
1536 /* Insert the item structure */
1538 item
= &descr
->items
[index
];
1539 if (index
< descr
->nb_items
)
1540 RtlMoveMemory( item
+ 1, item
,
1541 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1543 item
->data
= HAS_STRINGS(descr
) ? 0 : data
;
1545 item
->selected
= FALSE
;
1548 /* Get item height */
1550 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1552 MEASUREITEMSTRUCT mis
;
1553 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1555 mis
.CtlType
= ODT_LISTBOX
;
1558 mis
.itemData
= data
;
1559 mis
.itemHeight
= descr
->item_height
;
1560 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1561 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1562 TRACE("[%p]: measure item %d (%s) = %d\n",
1563 descr
->self
, index
, str
? debugstr_w(str
) : "", item
->height
);
1566 /* Repaint the items */
1568 LISTBOX_UpdateScroll( descr
);
1569 LISTBOX_InvalidateItems( descr
, index
);
1571 /* Move selection and focused item */
1572 /* If listbox was empty, set focus to the first item */
1573 if (descr
->nb_items
== 1)
1574 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1575 /* single select don't change selection index in win31 */
1576 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1578 descr
->selected_item
++;
1579 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1583 if (index
<= descr
->selected_item
)
1585 descr
->selected_item
++;
1586 descr
->focus_item
= oldfocus
; /* focus not changed */
1593 /***********************************************************************
1594 * LISTBOX_InsertString
1596 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
, LPCWSTR str
)
1598 LPWSTR new_str
= NULL
;
1601 if (HAS_STRINGS(descr
))
1603 static const WCHAR empty_stringW
[] = { 0 };
1604 if (!str
) str
= empty_stringW
;
1605 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1607 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1610 strcpyW(new_str
, str
);
1613 if (index
== -1) index
= descr
->nb_items
;
1614 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, (ULONG_PTR
)str
)) != 0)
1616 HeapFree( GetProcessHeap(), 0, new_str
);
1620 TRACE("[%p]: added item %d %s\n",
1621 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1626 /***********************************************************************
1627 * LISTBOX_DeleteItem
1629 * Delete the content of an item. 'index' must be a valid index.
1631 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1633 /* save the item data before it gets freed by LB_RESETCONTENT */
1634 ULONG_PTR item_data
= descr
->items
[index
].data
;
1635 LPWSTR item_str
= descr
->items
[index
].str
;
1637 if (!descr
->nb_items
)
1638 SendMessageW( descr
->self
, LB_RESETCONTENT
, 0, 0 );
1640 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1641 * while Win95 sends it for all items with user data.
1642 * It's probably better to send it too often than not
1643 * often enough, so this is what we do here.
1645 if (IS_OWNERDRAW(descr
) || item_data
)
1647 DELETEITEMSTRUCT dis
;
1648 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1650 dis
.CtlType
= ODT_LISTBOX
;
1653 dis
.hwndItem
= descr
->self
;
1654 dis
.itemData
= item_data
;
1655 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1657 if (HAS_STRINGS(descr
))
1658 HeapFree( GetProcessHeap(), 0, item_str
);
1662 /***********************************************************************
1663 * LISTBOX_RemoveItem
1665 * Remove an item from the listbox and delete its content.
1667 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1672 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1674 /* We need to invalidate the original rect instead of the updated one. */
1675 LISTBOX_InvalidateItems( descr
, index
);
1678 LISTBOX_DeleteItem( descr
, index
);
1680 if (!descr
->nb_items
) return LB_OKAY
;
1682 /* Remove the item */
1684 item
= &descr
->items
[index
];
1685 if (index
< descr
->nb_items
)
1686 RtlMoveMemory( item
, item
+ 1,
1687 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1688 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1690 /* Shrink the item array if possible */
1692 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1693 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1695 max_items
-= LB_ARRAY_GRANULARITY
;
1696 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1697 max_items
* sizeof(LB_ITEMDATA
) );
1698 if (item
) descr
->items
= item
;
1700 /* Repaint the items */
1702 LISTBOX_UpdateScroll( descr
);
1703 /* if we removed the scrollbar, reset the top of the list
1704 (correct for owner-drawn ???) */
1705 if (descr
->nb_items
== descr
->page_size
)
1706 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1708 /* Move selection and focused item */
1709 if (!IS_MULTISELECT(descr
))
1711 if (index
== descr
->selected_item
)
1712 descr
->selected_item
= -1;
1713 else if (index
< descr
->selected_item
)
1715 descr
->selected_item
--;
1716 if (ISWIN31
) /* win 31 do not change the selected item number */
1717 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1721 if (descr
->focus_item
>= descr
->nb_items
)
1723 descr
->focus_item
= descr
->nb_items
- 1;
1724 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1730 /***********************************************************************
1731 * LISTBOX_ResetContent
1733 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1737 for(i
= descr
->nb_items
- 1; i
>=0; i
--) LISTBOX_DeleteItem( descr
, i
);
1738 HeapFree( GetProcessHeap(), 0, descr
->items
);
1739 descr
->nb_items
= 0;
1740 descr
->top_item
= 0;
1741 descr
->selected_item
= -1;
1742 descr
->focus_item
= 0;
1743 descr
->anchor_item
= -1;
1744 descr
->items
= NULL
;
1748 /***********************************************************************
1751 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, INT count
)
1755 if (HAS_STRINGS(descr
))
1757 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1761 /* FIXME: this is far from optimal... */
1762 if (count
> descr
->nb_items
)
1764 while (count
> descr
->nb_items
)
1765 if ((ret
= LISTBOX_InsertString( descr
, -1, 0 )) < 0)
1768 else if (count
< descr
->nb_items
)
1770 while (count
< descr
->nb_items
)
1771 if ((ret
= LISTBOX_RemoveItem( descr
, (descr
->nb_items
- 1) )) < 0)
1775 InvalidateRect( descr
->self
, NULL
, TRUE
);
1780 /***********************************************************************
1783 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1784 LPCWSTR filespec
, BOOL long_names
)
1787 LRESULT ret
= LB_OKAY
;
1788 WIN32_FIND_DATAW entry
;
1790 LRESULT maxinsert
= LB_ERR
;
1792 /* don't scan directory if we just want drives exclusively */
1793 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1794 /* scan directory */
1795 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1797 int le
= GetLastError();
1798 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1805 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1807 static const WCHAR bracketW
[] = { ']',0 };
1808 static const WCHAR dotW
[] = { '.',0 };
1809 if (!(attrib
& DDL_DIRECTORY
) ||
1810 !strcmpW( entry
.cFileName
, dotW
)) continue;
1812 if (!long_names
&& entry
.cAlternateFileName
[0])
1813 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1815 strcpyW( buffer
+ 1, entry
.cFileName
);
1816 strcatW(buffer
, bracketW
);
1818 else /* not a directory */
1820 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1821 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1823 if ((attrib
& DDL_EXCLUSIVE
) &&
1824 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1827 if (!long_names
&& entry
.cAlternateFileName
[0])
1828 strcpyW( buffer
, entry
.cAlternateFileName
);
1830 strcpyW( buffer
, entry
.cFileName
);
1832 if (!long_names
) CharLowerW( buffer
);
1833 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1834 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1836 if (ret
<= maxinsert
) maxinsert
++; else maxinsert
= ret
;
1837 } while (FindNextFileW( handle
, &entry
));
1838 FindClose( handle
);
1846 if (attrib
& DDL_DRIVES
)
1848 WCHAR buffer
[] = {'[','-','a','-',']',0};
1849 WCHAR root
[] = {'A',':','\\',0};
1851 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1853 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1854 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1863 /***********************************************************************
1864 * LISTBOX_HandleVScroll
1866 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1870 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1874 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1877 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1880 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1881 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1884 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1885 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1887 case SB_THUMBPOSITION
:
1888 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1891 info
.cbSize
= sizeof(info
);
1892 info
.fMask
= SIF_TRACKPOS
;
1893 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1894 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1897 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1900 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1907 /***********************************************************************
1908 * LISTBOX_HandleHScroll
1910 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1915 if (descr
->style
& LBS_MULTICOLUMN
)
1920 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
1924 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
1928 page
= descr
->width
/ descr
->column_width
;
1929 if (page
< 1) page
= 1;
1930 LISTBOX_SetTopItem( descr
,
1931 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1934 page
= descr
->width
/ descr
->column_width
;
1935 if (page
< 1) page
= 1;
1936 LISTBOX_SetTopItem( descr
,
1937 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1939 case SB_THUMBPOSITION
:
1940 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
1943 info
.cbSize
= sizeof(info
);
1944 info
.fMask
= SIF_TRACKPOS
;
1945 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1946 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
1950 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1953 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1957 else if (descr
->horz_extent
)
1962 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
1965 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
1968 LISTBOX_SetHorizontalPos( descr
,
1969 descr
->horz_pos
- descr
->width
);
1972 LISTBOX_SetHorizontalPos( descr
,
1973 descr
->horz_pos
+ descr
->width
);
1975 case SB_THUMBPOSITION
:
1976 LISTBOX_SetHorizontalPos( descr
, pos
);
1979 info
.cbSize
= sizeof(info
);
1980 info
.fMask
= SIF_TRACKPOS
;
1981 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
1982 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
1985 LISTBOX_SetHorizontalPos( descr
, 0 );
1988 LISTBOX_SetHorizontalPos( descr
,
1989 descr
->horz_extent
- descr
->width
);
1996 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
1998 UINT pulScrollLines
= 3;
2000 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
2002 /* if scrolling changes direction, ignore left overs */
2003 if ((delta
< 0 && descr
->wheel_remain
< 0) ||
2004 (delta
> 0 && descr
->wheel_remain
> 0))
2005 descr
->wheel_remain
+= delta
;
2007 descr
->wheel_remain
= delta
;
2009 if (descr
->wheel_remain
&& pulScrollLines
)
2012 pulScrollLines
= min((UINT
) descr
->page_size
, pulScrollLines
);
2013 cLineScroll
= pulScrollLines
* (float)descr
->wheel_remain
/ WHEEL_DELTA
;
2014 descr
->wheel_remain
-= WHEEL_DELTA
* cLineScroll
/ (int)pulScrollLines
;
2015 LISTBOX_SetTopItem( descr
, descr
->top_item
- cLineScroll
, TRUE
);
2020 /***********************************************************************
2021 * LISTBOX_HandleLButtonDown
2023 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
2025 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2027 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2028 descr
->self
, x
, y
, index
, descr
->focus_item
);
2030 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2032 if (!descr
->in_focus
)
2034 if( !descr
->lphc
) SetFocus( descr
->self
);
2035 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2038 if (index
== -1) return 0;
2042 if (descr
->style
& LBS_NOTIFY
)
2043 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2044 MAKELPARAM( x
, y
) );
2047 descr
->captured
= TRUE
;
2048 SetCapture( descr
->self
);
2050 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2052 /* we should perhaps make sure that all items are deselected
2053 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2054 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2055 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2058 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2059 if (keys
& MK_CONTROL
)
2061 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2062 LISTBOX_SetSelection( descr
, index
,
2063 !descr
->items
[index
].selected
,
2064 (descr
->style
& LBS_NOTIFY
) != 0);
2068 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2070 if (descr
->style
& LBS_EXTENDEDSEL
)
2072 LISTBOX_SetSelection( descr
, index
,
2073 descr
->items
[index
].selected
,
2074 (descr
->style
& LBS_NOTIFY
) != 0 );
2078 LISTBOX_SetSelection( descr
, index
,
2079 !descr
->items
[index
].selected
,
2080 (descr
->style
& LBS_NOTIFY
) != 0 );
2086 descr
->anchor_item
= index
;
2087 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2088 LISTBOX_SetSelection( descr
, index
,
2089 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2094 if (GetWindowLongW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2101 if (DragDetect( descr
->self
, pt
))
2102 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2109 /*************************************************************************
2110 * LISTBOX_HandleLButtonDownCombo [Internal]
2112 * Process LButtonDown message for the ComboListBox
2115 * pWnd [I] The windows internal structure
2116 * pDescr [I] The ListBox internal structure
2117 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2118 * x [I] X Mouse Coordinate
2119 * y [I] Y Mouse Coordinate
2122 * 0 since we are processing the WM_LBUTTONDOWN Message
2125 * This function is only to be used when a ListBox is a ComboListBox
2128 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2130 RECT clientRect
, screenRect
;
2136 GetClientRect(descr
->self
, &clientRect
);
2138 if(PtInRect(&clientRect
, mousePos
))
2140 /* MousePos is in client, resume normal processing */
2141 if (msg
== WM_LBUTTONDOWN
)
2143 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2144 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2146 else if (descr
->style
& LBS_NOTIFY
)
2147 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2151 POINT screenMousePos
;
2152 HWND hWndOldCapture
;
2154 /* Check the Non-Client Area */
2155 screenMousePos
= mousePos
;
2156 hWndOldCapture
= GetCapture();
2158 GetWindowRect(descr
->self
, &screenRect
);
2159 ClientToScreen(descr
->self
, &screenMousePos
);
2161 if(!PtInRect(&screenRect
, screenMousePos
))
2163 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2164 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2165 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2169 /* Check to see the NC is a scrollbar */
2171 LONG style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2172 /* Check Vertical scroll bar */
2173 if (style
& WS_VSCROLL
)
2175 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2176 if (PtInRect( &clientRect
, mousePos
))
2177 nHitTestType
= HTVSCROLL
;
2179 /* Check horizontal scroll bar */
2180 if (style
& WS_HSCROLL
)
2182 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2183 if (PtInRect( &clientRect
, mousePos
))
2184 nHitTestType
= HTHSCROLL
;
2186 /* Windows sends this message when a scrollbar is clicked
2189 if(nHitTestType
!= 0)
2191 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2192 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2194 /* Resume the Capture after scrolling is complete
2196 if(hWndOldCapture
!= 0)
2197 SetCapture(hWndOldCapture
);
2203 /***********************************************************************
2204 * LISTBOX_HandleLButtonUp
2206 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2208 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2209 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2210 LISTBOX_Timer
= LB_TIMER_NONE
;
2211 if (descr
->captured
)
2213 descr
->captured
= FALSE
;
2214 if (GetCapture() == descr
->self
) ReleaseCapture();
2215 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2216 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2222 /***********************************************************************
2223 * LISTBOX_HandleTimer
2225 * Handle scrolling upon a timer event.
2226 * Return TRUE if scrolling should continue.
2228 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2233 if (descr
->top_item
) index
= descr
->top_item
- 1;
2237 if (descr
->top_item
) index
-= descr
->page_size
;
2240 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2241 if (index
== descr
->focus_item
) index
++;
2242 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2244 case LB_TIMER_RIGHT
:
2245 if (index
+ descr
->page_size
< descr
->nb_items
)
2246 index
+= descr
->page_size
;
2251 if (index
== descr
->focus_item
) return FALSE
;
2252 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2257 /***********************************************************************
2258 * LISTBOX_HandleSystemTimer
2260 * WM_SYSTIMER handler.
2262 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2264 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2266 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2267 LISTBOX_Timer
= LB_TIMER_NONE
;
2273 /***********************************************************************
2274 * LISTBOX_HandleMouseMove
2276 * WM_MOUSEMOVE handler.
2278 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2282 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2284 if (!descr
->captured
) return;
2286 if (descr
->style
& LBS_MULTICOLUMN
)
2289 else if (y
>= descr
->item_height
* descr
->page_size
)
2290 y
= descr
->item_height
* descr
->page_size
- 1;
2294 dir
= LB_TIMER_LEFT
;
2297 else if (x
>= descr
->width
)
2299 dir
= LB_TIMER_RIGHT
;
2300 x
= descr
->width
- 1;
2305 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2306 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2309 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2310 if (index
== -1) index
= descr
->focus_item
;
2311 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2313 /* Start/stop the system timer */
2315 if (dir
!= LB_TIMER_NONE
)
2316 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2317 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2318 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2319 LISTBOX_Timer
= dir
;
2323 /***********************************************************************
2324 * LISTBOX_HandleKeyDown
2326 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2329 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2330 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2331 bForceSelection
= FALSE
; /* only for single select list */
2333 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2335 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2336 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2337 (LPARAM
)descr
->self
);
2338 if (caret
== -2) return 0;
2340 if (caret
== -1) switch(key
)
2343 if (descr
->style
& LBS_MULTICOLUMN
)
2345 bForceSelection
= FALSE
;
2346 if (descr
->focus_item
>= descr
->page_size
)
2347 caret
= descr
->focus_item
- descr
->page_size
;
2352 caret
= descr
->focus_item
- 1;
2353 if (caret
< 0) caret
= 0;
2356 if (descr
->style
& LBS_MULTICOLUMN
)
2358 bForceSelection
= FALSE
;
2359 caret
= min(descr
->focus_item
+ descr
->page_size
, descr
->nb_items
- 1);
2364 caret
= descr
->focus_item
+ 1;
2365 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2369 if (descr
->style
& LBS_MULTICOLUMN
)
2371 INT page
= descr
->width
/ descr
->column_width
;
2372 if (page
< 1) page
= 1;
2373 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2375 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2376 if (caret
< 0) caret
= 0;
2379 if (descr
->style
& LBS_MULTICOLUMN
)
2381 INT page
= descr
->width
/ descr
->column_width
;
2382 if (page
< 1) page
= 1;
2383 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2385 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2386 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2392 caret
= descr
->nb_items
- 1;
2395 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2396 else if (descr
->style
& LBS_MULTIPLESEL
)
2398 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2399 !descr
->items
[descr
->focus_item
].selected
,
2400 (descr
->style
& LBS_NOTIFY
) != 0 );
2404 bForceSelection
= FALSE
;
2406 if (bForceSelection
) /* focused item is used instead of key */
2407 caret
= descr
->focus_item
;
2410 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2411 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2412 !IS_MULTISELECT(descr
))
2413 descr
->anchor_item
= caret
;
2414 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2416 if (descr
->style
& LBS_MULTIPLESEL
)
2417 descr
->selected_item
= caret
;
2419 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2420 if (descr
->style
& LBS_NOTIFY
)
2422 if (descr
->lphc
&& IsWindowVisible( descr
->self
))
2424 /* make sure that combo parent doesn't hide us */
2425 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2427 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2434 /***********************************************************************
2435 * LISTBOX_HandleChar
2437 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2445 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2447 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2448 MAKEWPARAM(charW
, descr
->focus_item
),
2449 (LPARAM
)descr
->self
);
2450 if (caret
== -2) return 0;
2453 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2456 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2457 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2458 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2459 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2460 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2466 /***********************************************************************
2469 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2472 MEASUREITEMSTRUCT mis
;
2475 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2478 GetClientRect( hwnd
, &rect
);
2480 descr
->owner
= GetParent( descr
->self
);
2481 descr
->style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2482 descr
->width
= rect
.right
- rect
.left
;
2483 descr
->height
= rect
.bottom
- rect
.top
;
2484 descr
->items
= NULL
;
2485 descr
->nb_items
= 0;
2486 descr
->top_item
= 0;
2487 descr
->selected_item
= -1;
2488 descr
->focus_item
= 0;
2489 descr
->anchor_item
= -1;
2490 descr
->item_height
= 1;
2491 descr
->page_size
= 1;
2492 descr
->column_width
= 150;
2493 descr
->horz_extent
= 0;
2494 descr
->horz_pos
= 0;
2497 descr
->wheel_remain
= 0;
2498 descr
->caret_on
= !lphc
;
2499 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2500 descr
->in_focus
= FALSE
;
2501 descr
->captured
= FALSE
;
2503 descr
->locale
= GetUserDefaultLCID();
2508 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2509 descr
->owner
= lphc
->self
;
2512 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2514 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2516 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2517 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2518 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2519 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2521 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2523 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2525 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2526 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2530 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2531 mis
.CtlType
= ODT_LISTBOX
;
2536 mis
.itemHeight
= descr
->item_height
;
2537 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2538 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2542 OpenThemeData( descr
->self
, WC_LISTBOXW
);
2544 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2549 /***********************************************************************
2552 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2554 HTHEME theme
= GetWindowTheme( descr
->self
);
2555 CloseThemeData( theme
);
2556 LISTBOX_ResetContent( descr
);
2557 SetWindowLongPtrW( descr
->self
, 0, 0 );
2558 HeapFree( GetProcessHeap(), 0, descr
);
2563 /***********************************************************************
2564 * ListBoxWndProc_common
2566 static LRESULT CALLBACK
LISTBOX_WindowProc( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
2568 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2569 HEADCOMBO
*lphc
= NULL
;
2575 if (!IsWindow(hwnd
)) return 0;
2577 if (msg
== WM_CREATE
)
2579 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2580 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= lpcs
->lpCreateParams
;
2581 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2582 TRACE("creating hwnd %p descr %p\n", hwnd
, (void *)GetWindowLongPtrW( hwnd
, 0 ) );
2585 /* Ignore all other messages before we get a WM_CREATE */
2586 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
2588 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2590 TRACE("[%p]: msg %#x wp %08lx lp %08lx\n", descr
->self
, msg
, wParam
, lParam
);
2594 case LB_RESETCONTENT
:
2595 LISTBOX_ResetContent( descr
);
2596 LISTBOX_UpdateScroll( descr
);
2597 InvalidateRect( descr
->self
, NULL
, TRUE
);
2602 const WCHAR
*textW
= (const WCHAR
*)lParam
;
2603 INT index
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2604 return LISTBOX_InsertString( descr
, index
, textW
);
2607 case LB_INSERTSTRING
:
2608 return LISTBOX_InsertString( descr
, wParam
, (const WCHAR
*)lParam
);
2612 const WCHAR
*textW
= (const WCHAR
*)lParam
;
2613 INT index
= LISTBOX_FindFileStrPos( descr
, textW
);
2614 return LISTBOX_InsertString( descr
, index
, textW
);
2617 case LB_DELETESTRING
:
2618 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2619 return descr
->nb_items
;
2622 SetLastError(ERROR_INVALID_INDEX
);
2626 case LB_GETITEMDATA
:
2627 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2629 SetLastError(ERROR_INVALID_INDEX
);
2632 return descr
->items
[wParam
].data
;
2634 case LB_SETITEMDATA
:
2635 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2637 SetLastError(ERROR_INVALID_INDEX
);
2640 descr
->items
[wParam
].data
= lParam
;
2641 /* undocumented: returns TRUE, not LB_OKAY (0) */
2645 return descr
->nb_items
;
2648 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, TRUE
);
2651 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2653 SetLastError(ERROR_INVALID_INDEX
);
2656 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2657 return strlenW( descr
->items
[wParam
].str
);
2660 if (descr
->nb_items
== 0)
2662 if (!IS_MULTISELECT(descr
))
2663 return descr
->selected_item
;
2664 if (descr
->selected_item
!= -1)
2665 return descr
->selected_item
;
2666 return descr
->focus_item
;
2667 /* otherwise, if the user tries to move the selection with the */
2668 /* arrow keys, we will give the application something to choke on */
2669 case LB_GETTOPINDEX
:
2670 return descr
->top_item
;
2672 case LB_GETITEMHEIGHT
:
2673 return LISTBOX_GetItemHeight( descr
, wParam
);
2675 case LB_SETITEMHEIGHT
:
2676 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2678 case LB_ITEMFROMPOINT
:
2685 /* The hiword of the return value is not a client area
2686 hittest as suggested by MSDN, but rather a hittest on
2687 the returned listbox item. */
2689 if(descr
->nb_items
== 0)
2690 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2692 pt
.x
= (short)LOWORD(lParam
);
2693 pt
.y
= (short)HIWORD(lParam
);
2695 SetRect(&rect
, 0, 0, descr
->width
, descr
->height
);
2697 if(!PtInRect(&rect
, pt
))
2699 pt
.x
= min(pt
.x
, rect
.right
- 1);
2700 pt
.x
= max(pt
.x
, 0);
2701 pt
.y
= min(pt
.y
, rect
.bottom
- 1);
2702 pt
.y
= max(pt
.y
, 0);
2706 index
= LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
);
2710 index
= descr
->nb_items
- 1;
2713 return MAKELONG(index
, hit
? 0 : 1);
2716 case LB_SETCARETINDEX
:
2717 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2718 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2725 case LB_GETCARETINDEX
:
2726 return descr
->focus_item
;
2728 case LB_SETTOPINDEX
:
2729 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2731 case LB_SETCOLUMNWIDTH
:
2732 return LISTBOX_SetColumnWidth( descr
, wParam
);
2734 case LB_GETITEMRECT
:
2735 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2738 return LISTBOX_FindString( descr
, wParam
, (const WCHAR
*)lParam
, FALSE
);
2740 case LB_FINDSTRINGEXACT
:
2741 return LISTBOX_FindString( descr
, wParam
, (const WCHAR
*)lParam
, TRUE
);
2743 case LB_SELECTSTRING
:
2745 const WCHAR
*textW
= (const WCHAR
*)lParam
;
2748 if (HAS_STRINGS(descr
))
2749 TRACE("LB_SELECTSTRING: %s\n", debugstr_w(textW
));
2751 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2752 if (index
!= LB_ERR
)
2754 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2755 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2761 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2763 return descr
->items
[wParam
].selected
;
2766 ret
= LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2767 if (ret
!= LB_ERR
&& wParam
)
2768 descr
->anchor_item
= lParam
;
2772 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2773 LISTBOX_SetCaretIndex( descr
, wParam
, TRUE
);
2774 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2775 if (ret
!= LB_ERR
) ret
= descr
->selected_item
;
2778 case LB_GETSELCOUNT
:
2779 return LISTBOX_GetSelCount( descr
);
2781 case LB_GETSELITEMS
:
2782 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2784 case LB_SELITEMRANGE
:
2785 if (LOWORD(lParam
) <= HIWORD(lParam
))
2786 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2787 HIWORD(lParam
), wParam
);
2789 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2790 LOWORD(lParam
), wParam
);
2792 case LB_SELITEMRANGEEX
:
2793 if ((INT
)lParam
>= (INT
)wParam
)
2794 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2796 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
2798 case LB_GETHORIZONTALEXTENT
:
2799 return descr
->horz_extent
;
2801 case LB_SETHORIZONTALEXTENT
:
2802 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
2804 case LB_GETANCHORINDEX
:
2805 return descr
->anchor_item
;
2807 case LB_SETANCHORINDEX
:
2808 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2810 SetLastError(ERROR_INVALID_INDEX
);
2813 descr
->anchor_item
= (INT
)wParam
;
2817 return LISTBOX_Directory( descr
, wParam
, (const WCHAR
*)lParam
, msg
== LB_DIR
);
2820 return descr
->locale
;
2825 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
2827 ret
= descr
->locale
;
2828 descr
->locale
= (LCID
)wParam
;
2832 case LB_INITSTORAGE
:
2833 return LISTBOX_InitStorage( descr
, wParam
);
2836 return LISTBOX_SetCount( descr
, (INT
)wParam
);
2838 case LB_SETTABSTOPS
:
2839 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
);
2842 if (descr
->caret_on
)
2844 descr
->caret_on
= TRUE
;
2845 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2846 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2850 if (!descr
->caret_on
)
2852 descr
->caret_on
= FALSE
;
2853 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2854 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2857 case LB_GETLISTBOXINFO
:
2858 return descr
->page_size
;
2861 return LISTBOX_Destroy( descr
);
2864 InvalidateRect( descr
->self
, NULL
, TRUE
);
2868 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
2872 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2874 case WM_PRINTCLIENT
:
2878 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
2879 ret
= LISTBOX_Paint( descr
, hdc
);
2880 if( !wParam
) EndPaint( descr
->self
, &ps
);
2885 LISTBOX_NCPaint( descr
, (HRGN
)wParam
);
2889 LISTBOX_UpdateSize( descr
);
2892 return (LRESULT
)descr
->font
;
2894 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
2895 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
2898 descr
->in_focus
= TRUE
;
2899 descr
->caret_on
= TRUE
;
2900 if (descr
->focus_item
!= -1)
2901 LISTBOX_DrawFocusRect( descr
, TRUE
);
2902 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
2905 LISTBOX_HandleLButtonUp( descr
); /* Release capture if we have it */
2906 descr
->in_focus
= FALSE
;
2907 descr
->wheel_remain
= 0;
2908 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2909 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2910 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
2913 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
2915 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
2917 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2918 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
2919 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
2920 case WM_LBUTTONDOWN
:
2922 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
2923 (INT16
)LOWORD(lParam
),
2924 (INT16
)HIWORD(lParam
) );
2925 return LISTBOX_HandleLButtonDown( descr
, wParam
,
2926 (INT16
)LOWORD(lParam
),
2927 (INT16
)HIWORD(lParam
) );
2928 case WM_LBUTTONDBLCLK
:
2930 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
2931 (INT16
)LOWORD(lParam
),
2932 (INT16
)HIWORD(lParam
) );
2933 if (descr
->style
& LBS_NOTIFY
)
2934 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2937 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
2939 BOOL captured
= descr
->captured
;
2943 mousePos
.x
= (INT16
)LOWORD(lParam
);
2944 mousePos
.y
= (INT16
)HIWORD(lParam
);
2947 * If we are in a dropdown combobox, we simulate that
2948 * the mouse is captured to show the tracking of the item.
2950 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
2951 descr
->captured
= TRUE
;
2953 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
2955 descr
->captured
= captured
;
2957 else if (GetCapture() == descr
->self
)
2959 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
2960 (INT16
)HIWORD(lParam
) );
2970 * If the mouse button "up" is not in the listbox,
2971 * we make sure there is no selection by re-selecting the
2972 * item that was selected when the listbox was made visible.
2974 mousePos
.x
= (INT16
)LOWORD(lParam
);
2975 mousePos
.y
= (INT16
)HIWORD(lParam
);
2977 GetClientRect(descr
->self
, &clientRect
);
2980 * When the user clicks outside the combobox and the focus
2981 * is lost, the owning combobox will send a fake buttonup with
2982 * 0xFFFFFFF as the mouse location, we must also revert the
2983 * selection to the original selection.
2985 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
2986 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
2988 return LISTBOX_HandleLButtonUp( descr
);
2990 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
2992 /* for some reason Windows makes it possible to
2993 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2995 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
2996 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
2997 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
2999 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3003 return LISTBOX_HandleKeyDown( descr
, wParam
);
3005 return LISTBOX_HandleChar( descr
, wParam
);
3008 return LISTBOX_HandleSystemTimer( descr
);
3010 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3013 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3014 wParam
, (LPARAM
)descr
->self
);
3015 TRACE("hbrush = %p\n", hbrush
);
3017 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3020 GetClientRect(descr
->self
, &rect
);
3021 FillRect((HDC
)wParam
, &rect
, hbrush
);
3026 if( lphc
) return 0;
3027 return SendMessageW( descr
->owner
, msg
, wParam
, lParam
);
3030 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3038 case WM_THEMECHANGED
:
3039 theme
= GetWindowTheme( hwnd
);
3040 CloseThemeData( theme
);
3041 OpenThemeData( hwnd
, WC_LISTBOXW
);
3045 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3046 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3047 hwnd
, msg
, wParam
, lParam
);
3050 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
3053 void LISTBOX_Register(void)
3057 memset(&wndClass
, 0, sizeof(wndClass
));
3058 wndClass
.style
= CS_PARENTDC
| CS_DBLCLKS
| CS_GLOBALCLASS
;
3059 wndClass
.lpfnWndProc
= LISTBOX_WindowProc
;
3060 wndClass
.cbClsExtra
= 0;
3061 wndClass
.cbWndExtra
= sizeof(LB_DESCR
*);
3062 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
3063 wndClass
.hbrBackground
= NULL
;
3064 wndClass
.lpszClassName
= WC_LISTBOXW
;
3065 RegisterClassW(&wndClass
);
3068 void COMBOLBOX_Register(void)
3070 static const WCHAR combolboxW
[] = {'C','o','m','b','o','L','B','o','x',0};
3073 memset(&wndClass
, 0, sizeof(wndClass
));
3074 wndClass
.style
= CS_SAVEBITS
| CS_DBLCLKS
| CS_DROPSHADOW
| CS_GLOBALCLASS
;
3075 wndClass
.lpfnWndProc
= LISTBOX_WindowProc
;
3076 wndClass
.cbClsExtra
= 0;
3077 wndClass
.cbWndExtra
= sizeof(LB_DESCR
*);
3078 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
3079 wndClass
.hbrBackground
= NULL
;
3080 wndClass
.lpszClassName
= combolboxW
;
3081 RegisterClassW(&wndClass
);