4 * Copyright 1996 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
33 #include "wine/unicode.h"
34 #include "wine/exception.h"
35 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(listbox2
);
41 /* Items array granularity */
42 #define LB_ARRAY_GRANULARITY 16
44 /* Scrolling timeout in ms */
45 #define LB_SCROLL_TIMEOUT 50
47 /* Listbox system timer id */
50 /* flag listbox changed while setredraw false - internal style */
51 #define LBS_DISPLAYCHANGED 0x80000000
56 LPWSTR str
; /* Item text */
57 BOOL selected
; /* Is item selected? */
58 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
59 ULONG_PTR data
; /* User data */
62 /* Listbox structure */
65 HWND self
; /* Our own window handle */
66 HWND owner
; /* Owner window to send notifications to */
67 UINT style
; /* Window style */
68 INT width
; /* Window width */
69 INT height
; /* Window height */
70 LB_ITEMDATA
*items
; /* Array of items */
71 INT nb_items
; /* Number of items */
72 INT top_item
; /* Top visible item */
73 INT selected_item
; /* Selected item */
74 INT focus_item
; /* Item that has the focus */
75 INT anchor_item
; /* Anchor item for extended selection */
76 INT item_height
; /* Default item height */
77 INT page_size
; /* Items per listbox page */
78 INT column_width
; /* Column width for multi-column listboxes */
79 INT horz_extent
; /* Horizontal extent */
80 INT horz_pos
; /* Horizontal position */
81 INT nb_tabs
; /* Number of tabs in array */
82 INT
*tabs
; /* Array of tabs */
83 INT avg_char_width
; /* Average width of characters */
84 INT wheel_remain
; /* Left over scroll amount */
85 BOOL caret_on
; /* Is caret on? */
86 BOOL captured
; /* Is mouse captured? */
88 HFONT font
; /* Current font */
89 LCID locale
; /* Current locale for string comparisons */
90 HEADCOMBO
*lphc
; /* ComboLBox */
94 #define IS_OWNERDRAW(descr) \
95 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
97 #define HAS_STRINGS(descr) \
98 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
101 #define IS_MULTISELECT(descr) \
102 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
103 !((descr)->style & LBS_NOSEL))
105 #define SEND_NOTIFICATION(descr,code) \
106 (SendMessageW( (descr)->owner, WM_COMMAND, \
107 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
109 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
111 /* Current timer status */
121 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
123 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
);
125 /***********************************************************************
126 * LISTBOX_GetCurrentPageSize
128 * Return the current page size
130 static INT
LISTBOX_GetCurrentPageSize( const LB_DESCR
*descr
)
133 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
134 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
136 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
138 if (i
== descr
->top_item
) return 1;
139 else return i
- descr
->top_item
;
143 /***********************************************************************
144 * LISTBOX_GetMaxTopIndex
146 * Return the maximum possible index for the top of the listbox.
148 static INT
LISTBOX_GetMaxTopIndex( const LB_DESCR
*descr
)
152 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
154 page
= descr
->height
;
155 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
156 if ((page
-= descr
->items
[max
].height
) < 0) break;
157 if (max
< descr
->nb_items
- 1) max
++;
159 else if (descr
->style
& LBS_MULTICOLUMN
)
161 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
162 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
163 max
= (max
- page
) * descr
->page_size
;
167 max
= descr
->nb_items
- descr
->page_size
;
169 if (max
< 0) max
= 0;
174 /***********************************************************************
175 * LISTBOX_UpdateScroll
177 * Update the scrollbars. Should be called whenever the content
178 * of the listbox changes.
180 static void LISTBOX_UpdateScroll( LB_DESCR
*descr
)
184 /* Check the listbox scroll bar flags individually before we call
185 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
186 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
187 scroll bar when we do not need one.
188 if (!(descr->style & WS_VSCROLL)) return;
191 /* It is important that we check descr->style, and not wnd->dwStyle,
192 for WS_VSCROLL, as the former is exactly the one passed in
193 argument to CreateWindow.
194 In Windows (and from now on in Wine :) a listbox created
195 with such a style (no WS_SCROLL) does not update
196 the scrollbar with listbox-related data, thus letting
197 the programmer use it for his/her own purposes. */
199 if (descr
->style
& LBS_NOREDRAW
) return;
200 info
.cbSize
= sizeof(info
);
202 if (descr
->style
& LBS_MULTICOLUMN
)
205 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
206 info
.nPos
= descr
->top_item
/ descr
->page_size
;
207 info
.nPage
= descr
->width
/ descr
->column_width
;
208 if (info
.nPage
< 1) info
.nPage
= 1;
209 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
210 if (descr
->style
& LBS_DISABLENOSCROLL
)
211 info
.fMask
|= SIF_DISABLENOSCROLL
;
212 if (descr
->style
& WS_HSCROLL
)
213 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
215 info
.fMask
= SIF_RANGE
;
216 if (descr
->style
& WS_VSCROLL
)
217 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
222 info
.nMax
= descr
->nb_items
- 1;
223 info
.nPos
= descr
->top_item
;
224 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
225 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
226 if (descr
->style
& LBS_DISABLENOSCROLL
)
227 info
.fMask
|= SIF_DISABLENOSCROLL
;
228 if (descr
->style
& WS_VSCROLL
)
229 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
231 if ((descr
->style
& WS_HSCROLL
) && descr
->horz_extent
)
233 info
.nPos
= descr
->horz_pos
;
234 info
.nPage
= descr
->width
;
235 info
.fMask
= SIF_POS
| SIF_PAGE
;
236 if (descr
->style
& LBS_DISABLENOSCROLL
)
237 info
.fMask
|= SIF_DISABLENOSCROLL
;
238 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
242 if (descr
->style
& LBS_DISABLENOSCROLL
)
246 info
.fMask
= SIF_RANGE
| SIF_DISABLENOSCROLL
;
247 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
251 ShowScrollBar( descr
->self
, SB_HORZ
, FALSE
);
258 /***********************************************************************
261 * Set the top item of the listbox, scrolling up or down if necessary.
263 static LRESULT
LISTBOX_SetTopItem( LB_DESCR
*descr
, INT index
, BOOL scroll
)
265 INT max
= LISTBOX_GetMaxTopIndex( descr
);
267 TRACE("setting top item %d, scroll %d\n", index
, scroll
);
269 if (index
> max
) index
= max
;
270 if (index
< 0) index
= 0;
271 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
272 if (descr
->top_item
== index
) return LB_OKAY
;
276 if (descr
->style
& LBS_MULTICOLUMN
)
277 diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
278 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
282 if (index
> descr
->top_item
)
284 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
285 diff
-= descr
->items
[i
].height
;
289 for (i
= index
; i
< descr
->top_item
; i
++)
290 diff
+= descr
->items
[i
].height
;
294 diff
= (descr
->top_item
- index
) * descr
->item_height
;
296 ScrollWindowEx( descr
->self
, 0, diff
, NULL
, NULL
, 0, NULL
,
297 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
300 InvalidateRect( descr
->self
, NULL
, TRUE
);
301 descr
->top_item
= index
;
302 LISTBOX_UpdateScroll( descr
);
307 /***********************************************************************
310 * Update the page size. Should be called when the size of
311 * the client area or the item height changes.
313 static void LISTBOX_UpdatePage( LB_DESCR
*descr
)
317 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
319 if (page_size
== descr
->page_size
) return;
320 descr
->page_size
= page_size
;
321 if (descr
->style
& LBS_MULTICOLUMN
)
322 InvalidateRect( descr
->self
, NULL
, TRUE
);
323 LISTBOX_SetTopItem( descr
, descr
->top_item
, FALSE
);
327 /***********************************************************************
330 * Update the size of the listbox. Should be called when the size of
331 * the client area changes.
333 static void LISTBOX_UpdateSize( LB_DESCR
*descr
)
337 GetClientRect( descr
->self
, &rect
);
338 descr
->width
= rect
.right
- rect
.left
;
339 descr
->height
= rect
.bottom
- rect
.top
;
340 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
345 GetWindowRect( descr
->self
, &rect
);
346 if(descr
->item_height
!= 0)
347 remaining
= descr
->height
% descr
->item_height
;
350 if ((descr
->height
> descr
->item_height
) && remaining
)
352 TRACE("[%p]: changing height %d -> %d\n",
353 descr
->self
, descr
->height
, descr
->height
- remaining
);
354 SetWindowPos( descr
->self
, 0, 0, 0, rect
.right
- rect
.left
,
355 rect
.bottom
- rect
.top
- remaining
,
356 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
360 TRACE("[%p]: new size = %d,%d\n", descr
->self
, descr
->width
, descr
->height
);
361 LISTBOX_UpdatePage( descr
);
362 LISTBOX_UpdateScroll( descr
);
364 /* Invalidate the focused item so it will be repainted correctly */
365 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
367 InvalidateRect( descr
->self
, &rect
, FALSE
);
372 /***********************************************************************
373 * LISTBOX_GetItemRect
375 * Get the rectangle enclosing an item, in listbox client coordinates.
376 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
378 static LRESULT
LISTBOX_GetItemRect( const LB_DESCR
*descr
, INT index
, RECT
*rect
)
380 /* Index <= 0 is legal even on empty listboxes */
381 if (index
&& (index
>= descr
->nb_items
))
384 SetLastError(ERROR_INVALID_INDEX
);
387 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
388 if (descr
->style
& LBS_MULTICOLUMN
)
390 INT col
= (index
/ descr
->page_size
) -
391 (descr
->top_item
/ descr
->page_size
);
392 rect
->left
+= col
* descr
->column_width
;
393 rect
->right
= rect
->left
+ descr
->column_width
;
394 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
395 rect
->bottom
= rect
->top
+ descr
->item_height
;
397 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
400 rect
->right
+= descr
->horz_pos
;
401 if ((index
>= 0) && (index
< descr
->nb_items
))
403 if (index
< descr
->top_item
)
405 for (i
= descr
->top_item
-1; i
>= index
; i
--)
406 rect
->top
-= descr
->items
[i
].height
;
410 for (i
= descr
->top_item
; i
< index
; i
++)
411 rect
->top
+= descr
->items
[i
].height
;
413 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
419 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
420 rect
->bottom
= rect
->top
+ descr
->item_height
;
421 rect
->right
+= descr
->horz_pos
;
424 TRACE("item %d, rect %s\n", index
, wine_dbgstr_rect(rect
));
426 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
427 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
431 /***********************************************************************
432 * LISTBOX_GetItemFromPoint
434 * Return the item nearest from point (x,y) (in client coordinates).
436 static INT
LISTBOX_GetItemFromPoint( const LB_DESCR
*descr
, INT x
, INT y
)
438 INT index
= descr
->top_item
;
440 if (!descr
->nb_items
) return -1; /* No items */
441 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
446 while (index
< descr
->nb_items
)
448 if ((pos
+= descr
->items
[index
].height
) > y
) break;
457 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
461 else if (descr
->style
& LBS_MULTICOLUMN
)
463 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
464 if (y
>= 0) index
+= y
/ descr
->item_height
;
465 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
466 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
470 index
+= (y
/ descr
->item_height
);
472 if (index
< 0) return 0;
473 if (index
>= descr
->nb_items
) return -1;
478 /***********************************************************************
483 static void LISTBOX_PaintItem( LB_DESCR
*descr
, HDC hdc
, const RECT
*rect
,
484 INT index
, UINT action
, BOOL ignoreFocus
)
486 LB_ITEMDATA
*item
= NULL
;
487 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
489 if (IS_OWNERDRAW(descr
))
497 if (action
== ODA_FOCUS
)
498 DrawFocusRect( hdc
, rect
);
500 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
504 /* some programs mess with the clipping region when
505 drawing the item, *and* restore the previous region
506 after they are done, so a region has better to exist
507 else everything ends clipped */
508 GetClientRect(descr
->self
, &r
);
509 hrgn
= set_control_clipping( hdc
, &r
);
511 dis
.CtlType
= ODT_LISTBOX
;
512 dis
.CtlID
= GetWindowLongPtrW( descr
->self
, GWLP_ID
);
513 dis
.hwndItem
= descr
->self
;
514 dis
.itemAction
= action
;
518 if (item
->selected
) dis
.itemState
|= ODS_SELECTED
;
519 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
521 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
522 if (!IsWindowEnabled(descr
->self
)) dis
.itemState
|= ODS_DISABLED
;
523 dis
.itemData
= item
->data
;
525 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
526 descr
->self
, index
, debugstr_w(item
->str
), action
,
527 dis
.itemState
, wine_dbgstr_rect(rect
) );
528 SendMessageW(descr
->owner
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
529 SelectClipRgn( hdc
, hrgn
);
530 if (hrgn
) DeleteObject( hrgn
);
534 COLORREF oldText
= 0, oldBk
= 0;
536 if (action
== ODA_FOCUS
)
538 DrawFocusRect( hdc
, rect
);
541 if (item
&& item
->selected
)
543 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
544 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
547 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
548 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
549 wine_dbgstr_rect(rect
) );
551 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
552 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
553 else if (!(descr
->style
& LBS_USETABSTOPS
))
554 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
555 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
556 strlenW(item
->str
), NULL
);
559 /* Output empty string to paint background in the full width. */
560 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
561 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
562 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
563 item
->str
, strlenW(item
->str
),
564 descr
->nb_tabs
, descr
->tabs
, 0);
566 if (item
&& item
->selected
)
568 SetBkColor( hdc
, oldBk
);
569 SetTextColor( hdc
, oldText
);
571 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
573 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
578 /***********************************************************************
581 * Change the redraw flag.
583 static void LISTBOX_SetRedraw( LB_DESCR
*descr
, BOOL on
)
587 if (!(descr
->style
& LBS_NOREDRAW
)) return;
588 descr
->style
&= ~LBS_NOREDRAW
;
589 if (descr
->style
& LBS_DISPLAYCHANGED
)
590 { /* page was changed while setredraw false, refresh automatically */
591 InvalidateRect(descr
->self
, NULL
, TRUE
);
592 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
593 { /* reset top of page if less than number of items/page */
594 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
595 if (descr
->top_item
< 0) descr
->top_item
= 0;
597 descr
->style
&= ~LBS_DISPLAYCHANGED
;
599 LISTBOX_UpdateScroll( descr
);
601 else descr
->style
|= LBS_NOREDRAW
;
605 /***********************************************************************
606 * LISTBOX_RepaintItem
608 * Repaint a single item synchronously.
610 static void LISTBOX_RepaintItem( LB_DESCR
*descr
, INT index
, UINT action
)
615 HBRUSH hbrush
, oldBrush
= 0;
617 /* Do not repaint the item if the item is not visible */
618 if (!IsWindowVisible(descr
->self
)) return;
619 if (descr
->style
& LBS_NOREDRAW
)
621 descr
->style
|= LBS_DISPLAYCHANGED
;
624 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
625 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
626 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
627 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
628 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
629 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
630 if (!IsWindowEnabled(descr
->self
))
631 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
632 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
633 LISTBOX_PaintItem( descr
, hdc
, &rect
, index
, action
, TRUE
);
634 if (oldFont
) SelectObject( hdc
, oldFont
);
635 if (oldBrush
) SelectObject( hdc
, oldBrush
);
636 ReleaseDC( descr
->self
, hdc
);
640 /***********************************************************************
641 * LISTBOX_DrawFocusRect
643 static void LISTBOX_DrawFocusRect( LB_DESCR
*descr
, BOOL on
)
649 /* Do not repaint the item if the item is not visible */
650 if (!IsWindowVisible(descr
->self
)) return;
652 if (descr
->focus_item
== -1) return;
653 if (!descr
->caret_on
|| !descr
->in_focus
) return;
655 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) != 1) return;
656 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
657 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
658 if (!IsWindowEnabled(descr
->self
))
659 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
660 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
661 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, !on
);
662 if (oldFont
) SelectObject( hdc
, oldFont
);
663 ReleaseDC( descr
->self
, hdc
);
667 /***********************************************************************
668 * LISTBOX_InitStorage
670 static LRESULT
LISTBOX_InitStorage( LB_DESCR
*descr
, INT nb_items
)
674 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
675 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
677 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
678 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
679 nb_items
* sizeof(LB_ITEMDATA
));
682 item
= HeapAlloc( GetProcessHeap(), 0,
683 nb_items
* sizeof(LB_ITEMDATA
));
688 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
696 /***********************************************************************
697 * LISTBOX_SetTabStops
699 static BOOL
LISTBOX_SetTabStops( LB_DESCR
*descr
, INT count
, LPINT tabs
)
703 if (!(descr
->style
& LBS_USETABSTOPS
))
705 SetLastError(ERROR_LB_WITHOUT_TABSTOPS
);
709 HeapFree( GetProcessHeap(), 0, descr
->tabs
);
710 if (!(descr
->nb_tabs
= count
))
715 if (!(descr
->tabs
= HeapAlloc( GetProcessHeap(), 0,
716 descr
->nb_tabs
* sizeof(INT
) )))
718 memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
720 /* convert into "dialog units"*/
721 for (i
= 0; i
< descr
->nb_tabs
; i
++)
722 descr
->tabs
[i
] = MulDiv(descr
->tabs
[i
], descr
->avg_char_width
, 4);
728 /***********************************************************************
731 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPWSTR buffer
, BOOL unicode
)
735 if ((index
< 0) || (index
>= descr
->nb_items
))
737 SetLastError(ERROR_INVALID_INDEX
);
741 if (HAS_STRINGS(descr
))
744 return strlenW(descr
->items
[index
].str
);
746 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
748 __TRY
/* hide a Delphi bug that passes a read-only buffer */
750 strcpyW( buffer
, descr
->items
[index
].str
);
751 len
= strlenW(buffer
);
755 WARN( "got an invalid buffer (Delphi bug?)\n" );
756 SetLastError( ERROR_INVALID_PARAMETER
);
763 *((DWORD
*)buffer
) = *(DWORD
*)&descr
->items
[index
].data
;
769 static inline INT
LISTBOX_lstrcmpiW( LCID lcid
, LPCWSTR str1
, LPCWSTR str2
)
771 INT ret
= CompareStringW( lcid
, NORM_IGNORECASE
, str1
, -1, str2
, -1 );
772 if (ret
== CSTR_LESS_THAN
)
774 if (ret
== CSTR_EQUAL
)
776 if (ret
== CSTR_GREATER_THAN
)
781 /***********************************************************************
782 * LISTBOX_FindStringPos
784 * Find the nearest string located before a given string in sort order.
785 * If 'exact' is TRUE, return an error if we don't get an exact match.
787 static INT
LISTBOX_FindStringPos( LB_DESCR
*descr
, LPCWSTR str
, BOOL exact
)
789 INT index
, min
, max
, res
;
791 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
793 max
= descr
->nb_items
;
796 index
= (min
+ max
) / 2;
797 if (HAS_STRINGS(descr
))
798 res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, descr
->items
[index
].str
);
801 COMPAREITEMSTRUCT cis
;
802 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
804 cis
.CtlType
= ODT_LISTBOX
;
806 cis
.hwndItem
= descr
->self
;
807 /* note that some application (MetaStock) expects the second item
808 * to be in the listbox */
810 cis
.itemData1
= (ULONG_PTR
)str
;
812 cis
.itemData2
= descr
->items
[index
].data
;
813 cis
.dwLocaleId
= descr
->locale
;
814 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
816 if (!res
) return index
;
817 if (res
< 0) max
= index
;
818 else min
= index
+ 1;
820 return exact
? -1 : max
;
824 /***********************************************************************
825 * LISTBOX_FindFileStrPos
827 * Find the nearest string located before a given string in directory
828 * sort order (i.e. first files, then directories, then drives).
830 static INT
LISTBOX_FindFileStrPos( LB_DESCR
*descr
, LPCWSTR str
)
834 if (!HAS_STRINGS(descr
))
835 return LISTBOX_FindStringPos( descr
, str
, FALSE
);
837 max
= descr
->nb_items
;
840 INT index
= (min
+ max
) / 2;
841 LPCWSTR p
= descr
->items
[index
].str
;
842 if (*p
== '[') /* drive or directory */
844 if (*str
!= '[') res
= -1;
845 else if (p
[1] == '-') /* drive */
847 if (str
[1] == '-') res
= str
[2] - p
[2];
852 if (str
[1] == '-') res
= 1;
853 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
858 if (*str
== '[') res
= 1;
859 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
861 if (!res
) return index
;
862 if (res
< 0) max
= index
;
863 else min
= index
+ 1;
869 /***********************************************************************
872 * Find the item beginning with a given string.
874 static INT
LISTBOX_FindString( LB_DESCR
*descr
, INT start
, LPCWSTR str
, BOOL exact
)
879 if (start
>= descr
->nb_items
) start
= -1;
880 item
= descr
->items
+ start
+ 1;
881 if (HAS_STRINGS(descr
))
883 if (!str
|| ! str
[0] ) return LB_ERR
;
886 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
887 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
888 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
889 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
893 /* Special case for drives and directories: ignore prefix */
894 #define CHECK_DRIVE(item) \
895 if ((item)->str[0] == '[') \
897 if (!strncmpiW( str, (item)->str+1, len )) return i; \
898 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
902 INT len
= strlenW(str
);
903 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
905 if (!strncmpiW( str
, item
->str
, len
)) return i
;
908 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
910 if (!strncmpiW( str
, item
->str
, len
)) return i
;
918 if (exact
&& (descr
->style
& LBS_SORT
))
919 /* If sorted, use a WM_COMPAREITEM binary search */
920 return LISTBOX_FindStringPos( descr
, str
, TRUE
);
922 /* Otherwise use a linear search */
923 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
924 if (item
->data
== (ULONG_PTR
)str
) return i
;
925 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
926 if (item
->data
== (ULONG_PTR
)str
) return i
;
932 /***********************************************************************
933 * LISTBOX_GetSelCount
935 static LRESULT
LISTBOX_GetSelCount( const LB_DESCR
*descr
)
938 const LB_ITEMDATA
*item
= descr
->items
;
940 if (!(descr
->style
& LBS_MULTIPLESEL
) ||
941 (descr
->style
& LBS_NOSEL
))
943 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
944 if (item
->selected
) count
++;
949 /***********************************************************************
950 * LISTBOX_GetSelItems
952 static LRESULT
LISTBOX_GetSelItems( const LB_DESCR
*descr
, INT max
, LPINT array
)
955 const LB_ITEMDATA
*item
= descr
->items
;
957 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
958 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
959 if (item
->selected
) array
[count
++] = i
;
964 /***********************************************************************
967 static LRESULT
LISTBOX_Paint( LB_DESCR
*descr
, HDC hdc
)
969 INT i
, col_pos
= descr
->page_size
- 1;
971 RECT focusRect
= {-1, -1, -1, -1};
973 HBRUSH hbrush
, oldBrush
= 0;
975 if (descr
->style
& LBS_NOREDRAW
) return 0;
977 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
978 if (descr
->style
& LBS_MULTICOLUMN
)
979 rect
.right
= rect
.left
+ descr
->column_width
;
980 else if (descr
->horz_pos
)
982 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
983 rect
.right
+= descr
->horz_pos
;
986 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
987 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
988 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
989 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
990 if (!IsWindowEnabled(descr
->self
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
992 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
995 /* Special case for empty listbox: paint focus rect */
996 rect
.bottom
= rect
.top
+ descr
->item_height
;
997 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
998 &rect
, NULL
, 0, NULL
);
999 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1000 rect
.top
= rect
.bottom
;
1003 /* Paint all the item, regarding the selection
1004 Focus state will be painted after */
1006 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1008 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1009 rect
.bottom
= rect
.top
+ descr
->item_height
;
1011 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1013 /* keep the focus rect, to paint the focus item after */
1014 if (i
== descr
->focus_item
)
1017 LISTBOX_PaintItem( descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1018 rect
.top
= rect
.bottom
;
1020 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1022 if (!IS_OWNERDRAW(descr
))
1024 /* Clear the bottom of the column */
1025 if (rect
.top
< descr
->height
)
1027 rect
.bottom
= descr
->height
;
1028 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1029 &rect
, NULL
, 0, NULL
);
1033 /* Go to the next column */
1034 rect
.left
+= descr
->column_width
;
1035 rect
.right
+= descr
->column_width
;
1037 col_pos
= descr
->page_size
- 1;
1042 if (rect
.top
>= descr
->height
) break;
1046 /* Paint the focus item now */
1047 if (focusRect
.top
!= focusRect
.bottom
&&
1048 descr
->caret_on
&& descr
->in_focus
)
1049 LISTBOX_PaintItem( descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1051 if (!IS_OWNERDRAW(descr
))
1053 /* Clear the remainder of the client area */
1054 if (rect
.top
< descr
->height
)
1056 rect
.bottom
= descr
->height
;
1057 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1058 &rect
, NULL
, 0, NULL
);
1060 if (rect
.right
< descr
->width
)
1062 rect
.left
= rect
.right
;
1063 rect
.right
= descr
->width
;
1065 rect
.bottom
= descr
->height
;
1066 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1067 &rect
, NULL
, 0, NULL
);
1070 if (oldFont
) SelectObject( hdc
, oldFont
);
1071 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1076 /***********************************************************************
1077 * LISTBOX_InvalidateItems
1079 * Invalidate all items from a given item. If the specified item is not
1080 * visible, nothing happens.
1082 static void LISTBOX_InvalidateItems( LB_DESCR
*descr
, INT index
)
1086 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1088 if (descr
->style
& LBS_NOREDRAW
)
1090 descr
->style
|= LBS_DISPLAYCHANGED
;
1093 rect
.bottom
= descr
->height
;
1094 InvalidateRect( descr
->self
, &rect
, TRUE
);
1095 if (descr
->style
& LBS_MULTICOLUMN
)
1097 /* Repaint the other columns */
1098 rect
.left
= rect
.right
;
1099 rect
.right
= descr
->width
;
1101 InvalidateRect( descr
->self
, &rect
, TRUE
);
1106 static void LISTBOX_InvalidateItemRect( LB_DESCR
*descr
, INT index
)
1110 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1111 InvalidateRect( descr
->self
, &rect
, TRUE
);
1114 /***********************************************************************
1115 * LISTBOX_GetItemHeight
1117 static LRESULT
LISTBOX_GetItemHeight( const LB_DESCR
*descr
, INT index
)
1119 if (descr
->style
& LBS_OWNERDRAWVARIABLE
&& descr
->nb_items
> 0)
1121 if ((index
< 0) || (index
>= descr
->nb_items
))
1123 SetLastError(ERROR_INVALID_INDEX
);
1126 return descr
->items
[index
].height
;
1128 else return descr
->item_height
;
1132 /***********************************************************************
1133 * LISTBOX_SetItemHeight
1135 static LRESULT
LISTBOX_SetItemHeight( LB_DESCR
*descr
, INT index
, INT height
, BOOL repaint
)
1137 if (height
> MAXBYTE
)
1140 if (!height
) height
= 1;
1142 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1144 if ((index
< 0) || (index
>= descr
->nb_items
))
1146 SetLastError(ERROR_INVALID_INDEX
);
1149 TRACE("[%p]: item %d height = %d\n", descr
->self
, index
, height
);
1150 descr
->items
[index
].height
= height
;
1151 LISTBOX_UpdateScroll( descr
);
1153 LISTBOX_InvalidateItems( descr
, index
);
1155 else if (height
!= descr
->item_height
)
1157 TRACE("[%p]: new height = %d\n", descr
->self
, height
);
1158 descr
->item_height
= height
;
1159 LISTBOX_UpdatePage( descr
);
1160 LISTBOX_UpdateScroll( descr
);
1162 InvalidateRect( descr
->self
, 0, TRUE
);
1168 /***********************************************************************
1169 * LISTBOX_SetHorizontalPos
1171 static void LISTBOX_SetHorizontalPos( LB_DESCR
*descr
, INT pos
)
1175 if (pos
> descr
->horz_extent
- descr
->width
)
1176 pos
= descr
->horz_extent
- descr
->width
;
1177 if (pos
< 0) pos
= 0;
1178 if (!(diff
= descr
->horz_pos
- pos
)) return;
1179 TRACE("[%p]: new horz pos = %d\n", descr
->self
, pos
);
1180 descr
->horz_pos
= pos
;
1181 LISTBOX_UpdateScroll( descr
);
1182 if (abs(diff
) < descr
->width
)
1185 /* Invalidate the focused item so it will be repainted correctly */
1186 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1187 InvalidateRect( descr
->self
, &rect
, TRUE
);
1188 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
1189 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1192 InvalidateRect( descr
->self
, NULL
, TRUE
);
1196 /***********************************************************************
1197 * LISTBOX_SetHorizontalExtent
1199 static LRESULT
LISTBOX_SetHorizontalExtent( LB_DESCR
*descr
, INT extent
)
1201 if (descr
->style
& LBS_MULTICOLUMN
)
1203 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1204 TRACE("[%p]: new horz extent = %d\n", descr
->self
, extent
);
1205 descr
->horz_extent
= extent
;
1206 if (descr
->style
& WS_HSCROLL
) {
1208 info
.cbSize
= sizeof(info
);
1210 info
.nMax
= descr
->horz_extent
? descr
->horz_extent
- 1 : 0;
1211 info
.fMask
= SIF_RANGE
;
1212 if (descr
->style
& LBS_DISABLENOSCROLL
)
1213 info
.fMask
|= SIF_DISABLENOSCROLL
;
1214 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
1216 if (descr
->horz_pos
> extent
- descr
->width
)
1217 LISTBOX_SetHorizontalPos( descr
, extent
- descr
->width
);
1222 /***********************************************************************
1223 * LISTBOX_SetColumnWidth
1225 static LRESULT
LISTBOX_SetColumnWidth( LB_DESCR
*descr
, INT width
)
1227 if (width
== descr
->column_width
) return LB_OKAY
;
1228 TRACE("[%p]: new column width = %d\n", descr
->self
, width
);
1229 descr
->column_width
= width
;
1230 LISTBOX_UpdatePage( descr
);
1235 /***********************************************************************
1238 * Returns the item height.
1240 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1244 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1249 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1251 ERR("unable to get DC.\n" );
1254 if (font
) oldFont
= SelectObject( hdc
, font
);
1255 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1256 if (oldFont
) SelectObject( hdc
, oldFont
);
1257 ReleaseDC( descr
->self
, hdc
);
1259 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1260 if (!IS_OWNERDRAW(descr
))
1261 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1266 /***********************************************************************
1267 * LISTBOX_MakeItemVisible
1269 * Make sure that a given item is partially or fully visible.
1271 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
, BOOL fully
)
1275 TRACE("current top item %d, index %d, fully %d\n", descr
->top_item
, index
, fully
);
1277 if (index
<= descr
->top_item
) top
= index
;
1278 else if (descr
->style
& LBS_MULTICOLUMN
)
1280 INT cols
= descr
->width
;
1281 if (!fully
) cols
+= descr
->column_width
- 1;
1282 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1284 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1285 top
= index
- descr
->page_size
* (cols
- 1);
1287 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1289 INT height
= fully
? descr
->items
[index
].height
: 1;
1290 for (top
= index
; top
> descr
->top_item
; top
--)
1291 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1295 if (index
< descr
->top_item
+ descr
->page_size
) return;
1296 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1297 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1298 top
= index
- descr
->page_size
+ 1;
1300 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1303 /***********************************************************************
1304 * LISTBOX_SetCaretIndex
1307 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1310 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1312 INT oldfocus
= descr
->focus_item
;
1314 TRACE("old focus %d, index %d\n", oldfocus
, index
);
1316 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1317 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1318 if (index
== oldfocus
) return LB_OKAY
;
1320 LISTBOX_DrawFocusRect( descr
, FALSE
);
1321 descr
->focus_item
= index
;
1323 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1324 LISTBOX_DrawFocusRect( descr
, TRUE
);
1330 /***********************************************************************
1331 * LISTBOX_SelectItemRange
1333 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1335 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1340 /* A few sanity checks */
1342 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1343 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1345 if (!descr
->nb_items
) return LB_OKAY
;
1347 if (last
== -1 || last
>= descr
->nb_items
) last
= descr
->nb_items
- 1;
1348 if (first
< 0) first
= 0;
1349 if (last
< first
) return LB_OKAY
;
1351 if (on
) /* Turn selection on */
1353 for (i
= first
; i
<= last
; i
++)
1355 if (descr
->items
[i
].selected
) continue;
1356 descr
->items
[i
].selected
= TRUE
;
1357 LISTBOX_InvalidateItemRect(descr
, i
);
1360 else /* Turn selection off */
1362 for (i
= first
; i
<= last
; i
++)
1364 if (!descr
->items
[i
].selected
) continue;
1365 descr
->items
[i
].selected
= FALSE
;
1366 LISTBOX_InvalidateItemRect(descr
, i
);
1372 /***********************************************************************
1373 * LISTBOX_SetSelection
1375 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1376 BOOL on
, BOOL send_notify
)
1378 TRACE( "cur_sel=%d index=%d notify=%s\n",
1379 descr
->selected_item
, index
, send_notify
? "YES" : "NO" );
1381 if (descr
->style
& LBS_NOSEL
)
1383 descr
->selected_item
= index
;
1386 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1387 if (descr
->style
& LBS_MULTIPLESEL
)
1389 if (index
== -1) /* Select all items */
1390 return LISTBOX_SelectItemRange( descr
, 0, descr
->nb_items
, on
);
1391 else /* Only one item */
1392 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1396 INT oldsel
= descr
->selected_item
;
1397 if (index
== oldsel
) return LB_OKAY
;
1398 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1399 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1400 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1401 descr
->selected_item
= index
;
1402 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1403 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1404 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1406 if( descr
->lphc
) /* set selection change flag for parent combo */
1407 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1413 /***********************************************************************
1416 * Change the caret position and extend the selection to the new caret.
1418 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
, BOOL fully_visible
)
1420 TRACE("old focus %d, index %d\n", descr
->focus_item
, index
);
1422 if ((index
< 0) || (index
>= descr
->nb_items
))
1425 /* Important, repaint needs to be done in this order if
1426 you want to mimic Windows behavior:
1427 1. Remove the focus and paint the item
1428 2. Remove the selection and paint the item(s)
1429 3. Set the selection and repaint the item(s)
1430 4. Set the focus to 'index' and repaint the item */
1432 /* 1. remove the focus and repaint the item */
1433 LISTBOX_DrawFocusRect( descr
, FALSE
);
1435 /* 2. then turn off the previous selection */
1436 /* 3. repaint the new selected item */
1437 if (descr
->style
& LBS_EXTENDEDSEL
)
1439 if (descr
->anchor_item
!= -1)
1441 INT first
= min( index
, descr
->anchor_item
);
1442 INT last
= max( index
, descr
->anchor_item
);
1444 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1445 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1446 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1449 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1451 /* Set selection to new caret item */
1452 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1455 /* 4. repaint the new item with the focus */
1456 descr
->focus_item
= index
;
1457 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1458 LISTBOX_DrawFocusRect( descr
, TRUE
);
1462 /***********************************************************************
1463 * LISTBOX_InsertItem
1465 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1466 LPWSTR str
, ULONG_PTR data
)
1470 INT oldfocus
= descr
->focus_item
;
1472 if (index
== -1) index
= descr
->nb_items
;
1473 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1474 if (!descr
->items
) max_items
= 0;
1475 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1476 if (descr
->nb_items
== max_items
)
1478 /* We need to grow the array */
1479 max_items
+= LB_ARRAY_GRANULARITY
;
1481 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1482 max_items
* sizeof(LB_ITEMDATA
) );
1484 item
= HeapAlloc( GetProcessHeap(), 0,
1485 max_items
* sizeof(LB_ITEMDATA
) );
1488 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1491 descr
->items
= item
;
1494 /* Insert the item structure */
1496 item
= &descr
->items
[index
];
1497 if (index
< descr
->nb_items
)
1498 RtlMoveMemory( item
+ 1, item
,
1499 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1503 item
->selected
= FALSE
;
1506 /* Get item height */
1508 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1510 MEASUREITEMSTRUCT mis
;
1511 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1513 mis
.CtlType
= ODT_LISTBOX
;
1516 mis
.itemData
= descr
->items
[index
].data
;
1517 mis
.itemHeight
= descr
->item_height
;
1518 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1519 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1520 TRACE("[%p]: measure item %d (%s) = %d\n",
1521 descr
->self
, index
, str
? debugstr_w(str
) : "", item
->height
);
1524 /* Repaint the items */
1526 LISTBOX_UpdateScroll( descr
);
1527 LISTBOX_InvalidateItems( descr
, index
);
1529 /* Move selection and focused item */
1530 /* If listbox was empty, set focus to the first item */
1531 if (descr
->nb_items
== 1)
1532 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1533 /* single select don't change selection index in win31 */
1534 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1536 descr
->selected_item
++;
1537 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1541 if (index
<= descr
->selected_item
)
1543 descr
->selected_item
++;
1544 descr
->focus_item
= oldfocus
; /* focus not changed */
1551 /***********************************************************************
1552 * LISTBOX_InsertString
1554 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
, LPCWSTR str
)
1556 LPWSTR new_str
= NULL
;
1560 if (HAS_STRINGS(descr
))
1562 static const WCHAR empty_stringW
[] = { 0 };
1563 if (!str
) str
= empty_stringW
;
1564 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1566 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1569 strcpyW(new_str
, str
);
1571 else data
= (ULONG_PTR
)str
;
1573 if (index
== -1) index
= descr
->nb_items
;
1574 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, data
)) != 0)
1576 HeapFree( GetProcessHeap(), 0, new_str
);
1580 TRACE("[%p]: added item %d %s\n",
1581 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1586 /***********************************************************************
1587 * LISTBOX_DeleteItem
1589 * Delete the content of an item. 'index' must be a valid index.
1591 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1593 /* save the item data before it gets freed by LB_RESETCONTENT */
1594 ULONG_PTR item_data
= descr
->items
[index
].data
;
1595 LPWSTR item_str
= descr
->items
[index
].str
;
1597 if (!descr
->nb_items
)
1598 SendMessageW( descr
->self
, LB_RESETCONTENT
, 0, 0 );
1600 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1601 * while Win95 sends it for all items with user data.
1602 * It's probably better to send it too often than not
1603 * often enough, so this is what we do here.
1605 if (IS_OWNERDRAW(descr
) || item_data
)
1607 DELETEITEMSTRUCT dis
;
1608 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1610 dis
.CtlType
= ODT_LISTBOX
;
1613 dis
.hwndItem
= descr
->self
;
1614 dis
.itemData
= item_data
;
1615 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1617 if (HAS_STRINGS(descr
))
1618 HeapFree( GetProcessHeap(), 0, item_str
);
1622 /***********************************************************************
1623 * LISTBOX_RemoveItem
1625 * Remove an item from the listbox and delete its content.
1627 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1632 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1634 /* We need to invalidate the original rect instead of the updated one. */
1635 LISTBOX_InvalidateItems( descr
, index
);
1638 LISTBOX_DeleteItem( descr
, index
);
1640 if (!descr
->nb_items
) return LB_OKAY
;
1642 /* Remove the item */
1644 item
= &descr
->items
[index
];
1645 if (index
< descr
->nb_items
)
1646 RtlMoveMemory( item
, item
+ 1,
1647 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1648 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1650 /* Shrink the item array if possible */
1652 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1653 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1655 max_items
-= LB_ARRAY_GRANULARITY
;
1656 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1657 max_items
* sizeof(LB_ITEMDATA
) );
1658 if (item
) descr
->items
= item
;
1660 /* Repaint the items */
1662 LISTBOX_UpdateScroll( descr
);
1663 /* if we removed the scrollbar, reset the top of the list
1664 (correct for owner-drawn ???) */
1665 if (descr
->nb_items
== descr
->page_size
)
1666 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1668 /* Move selection and focused item */
1669 if (!IS_MULTISELECT(descr
))
1671 if (index
== descr
->selected_item
)
1672 descr
->selected_item
= -1;
1673 else if (index
< descr
->selected_item
)
1675 descr
->selected_item
--;
1676 if (ISWIN31
) /* win 31 do not change the selected item number */
1677 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1681 if (descr
->focus_item
>= descr
->nb_items
)
1683 descr
->focus_item
= descr
->nb_items
- 1;
1684 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1690 /***********************************************************************
1691 * LISTBOX_ResetContent
1693 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1697 for(i
= descr
->nb_items
- 1; i
>=0; i
--) LISTBOX_DeleteItem( descr
, i
);
1698 HeapFree( GetProcessHeap(), 0, descr
->items
);
1699 descr
->nb_items
= 0;
1700 descr
->top_item
= 0;
1701 descr
->selected_item
= -1;
1702 descr
->focus_item
= 0;
1703 descr
->anchor_item
= -1;
1704 descr
->items
= NULL
;
1708 /***********************************************************************
1711 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, INT count
)
1715 if (HAS_STRINGS(descr
))
1717 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1721 /* FIXME: this is far from optimal... */
1722 if (count
> descr
->nb_items
)
1724 while (count
> descr
->nb_items
)
1725 if ((ret
= LISTBOX_InsertString( descr
, -1, 0 )) < 0)
1728 else if (count
< descr
->nb_items
)
1730 while (count
< descr
->nb_items
)
1731 if ((ret
= LISTBOX_RemoveItem( descr
, (descr
->nb_items
- 1) )) < 0)
1735 InvalidateRect( descr
->self
, NULL
, TRUE
);
1740 /***********************************************************************
1743 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1744 LPCWSTR filespec
, BOOL long_names
)
1747 LRESULT ret
= LB_OKAY
;
1748 WIN32_FIND_DATAW entry
;
1750 LRESULT maxinsert
= LB_ERR
;
1752 /* don't scan directory if we just want drives exclusively */
1753 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1754 /* scan directory */
1755 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1757 int le
= GetLastError();
1758 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1765 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1767 static const WCHAR bracketW
[] = { ']',0 };
1768 static const WCHAR dotW
[] = { '.',0 };
1769 if (!(attrib
& DDL_DIRECTORY
) ||
1770 !strcmpW( entry
.cFileName
, dotW
)) continue;
1772 if (!long_names
&& entry
.cAlternateFileName
[0])
1773 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1775 strcpyW( buffer
+ 1, entry
.cFileName
);
1776 strcatW(buffer
, bracketW
);
1778 else /* not a directory */
1780 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1781 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1783 if ((attrib
& DDL_EXCLUSIVE
) &&
1784 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1787 if (!long_names
&& entry
.cAlternateFileName
[0])
1788 strcpyW( buffer
, entry
.cAlternateFileName
);
1790 strcpyW( buffer
, entry
.cFileName
);
1792 if (!long_names
) CharLowerW( buffer
);
1793 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1794 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1796 if (ret
<= maxinsert
) maxinsert
++; else maxinsert
= ret
;
1797 } while (FindNextFileW( handle
, &entry
));
1798 FindClose( handle
);
1806 if (attrib
& DDL_DRIVES
)
1808 WCHAR buffer
[] = {'[','-','a','-',']',0};
1809 WCHAR root
[] = {'A',':','\\',0};
1811 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1813 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1814 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1823 /***********************************************************************
1824 * LISTBOX_HandleVScroll
1826 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1830 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1834 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1837 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1840 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1841 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1844 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1845 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1847 case SB_THUMBPOSITION
:
1848 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1851 info
.cbSize
= sizeof(info
);
1852 info
.fMask
= SIF_TRACKPOS
;
1853 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1854 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1857 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1860 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1867 /***********************************************************************
1868 * LISTBOX_HandleHScroll
1870 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1875 if (descr
->style
& LBS_MULTICOLUMN
)
1880 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
1884 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
1888 page
= descr
->width
/ descr
->column_width
;
1889 if (page
< 1) page
= 1;
1890 LISTBOX_SetTopItem( descr
,
1891 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1894 page
= descr
->width
/ descr
->column_width
;
1895 if (page
< 1) page
= 1;
1896 LISTBOX_SetTopItem( descr
,
1897 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1899 case SB_THUMBPOSITION
:
1900 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
, TRUE
);
1903 info
.cbSize
= sizeof(info
);
1904 info
.fMask
= SIF_TRACKPOS
;
1905 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1906 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
1910 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1913 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1917 else if (descr
->horz_extent
)
1922 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
1925 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
1928 LISTBOX_SetHorizontalPos( descr
,
1929 descr
->horz_pos
- descr
->width
);
1932 LISTBOX_SetHorizontalPos( descr
,
1933 descr
->horz_pos
+ descr
->width
);
1935 case SB_THUMBPOSITION
:
1936 LISTBOX_SetHorizontalPos( descr
, pos
);
1939 info
.cbSize
= sizeof(info
);
1940 info
.fMask
= SIF_TRACKPOS
;
1941 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
1942 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
1945 LISTBOX_SetHorizontalPos( descr
, 0 );
1948 LISTBOX_SetHorizontalPos( descr
,
1949 descr
->horz_extent
- descr
->width
);
1956 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
1958 UINT pulScrollLines
= 3;
1960 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1962 /* if scrolling changes direction, ignore left overs */
1963 if ((delta
< 0 && descr
->wheel_remain
< 0) ||
1964 (delta
> 0 && descr
->wheel_remain
> 0))
1965 descr
->wheel_remain
+= delta
;
1967 descr
->wheel_remain
= delta
;
1969 if (descr
->wheel_remain
&& pulScrollLines
)
1972 pulScrollLines
= min((UINT
) descr
->page_size
, pulScrollLines
);
1973 cLineScroll
= pulScrollLines
* (float)descr
->wheel_remain
/ WHEEL_DELTA
;
1974 descr
->wheel_remain
-= WHEEL_DELTA
* cLineScroll
/ (int)pulScrollLines
;
1975 LISTBOX_SetTopItem( descr
, descr
->top_item
- cLineScroll
, TRUE
);
1980 /***********************************************************************
1981 * LISTBOX_HandleLButtonDown
1983 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
, DWORD keys
, INT x
, INT y
)
1985 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
1987 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
1988 descr
->self
, x
, y
, index
, descr
->focus_item
);
1990 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
1992 if (!descr
->in_focus
)
1994 if( !descr
->lphc
) SetFocus( descr
->self
);
1995 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
1998 if (index
== -1) return 0;
2002 if (descr
->style
& LBS_NOTIFY
)
2003 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2004 MAKELPARAM( x
, y
) );
2007 descr
->captured
= TRUE
;
2008 SetCapture( descr
->self
);
2010 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2012 /* we should perhaps make sure that all items are deselected
2013 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2014 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2015 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2018 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2019 if (keys
& MK_CONTROL
)
2021 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2022 LISTBOX_SetSelection( descr
, index
,
2023 !descr
->items
[index
].selected
,
2024 (descr
->style
& LBS_NOTIFY
) != 0);
2028 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2030 if (descr
->style
& LBS_EXTENDEDSEL
)
2032 LISTBOX_SetSelection( descr
, index
,
2033 descr
->items
[index
].selected
,
2034 (descr
->style
& LBS_NOTIFY
) != 0 );
2038 LISTBOX_SetSelection( descr
, index
,
2039 !descr
->items
[index
].selected
,
2040 (descr
->style
& LBS_NOTIFY
) != 0 );
2046 descr
->anchor_item
= index
;
2047 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2048 LISTBOX_SetSelection( descr
, index
,
2049 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2054 if (GetWindowLongW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2061 if (DragDetect( descr
->self
, pt
))
2062 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2069 /*************************************************************************
2070 * LISTBOX_HandleLButtonDownCombo [Internal]
2072 * Process LButtonDown message for the ComboListBox
2075 * pWnd [I] The windows internal structure
2076 * pDescr [I] The ListBox internal structure
2077 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2078 * x [I] X Mouse Coordinate
2079 * y [I] Y Mouse Coordinate
2082 * 0 since we are processing the WM_LBUTTONDOWN Message
2085 * This function is only to be used when a ListBox is a ComboListBox
2088 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*descr
, UINT msg
, DWORD keys
, INT x
, INT y
)
2090 RECT clientRect
, screenRect
;
2096 GetClientRect(descr
->self
, &clientRect
);
2098 if(PtInRect(&clientRect
, mousePos
))
2100 /* MousePos is in client, resume normal processing */
2101 if (msg
== WM_LBUTTONDOWN
)
2103 descr
->lphc
->droppedIndex
= descr
->nb_items
? descr
->selected_item
: -1;
2104 return LISTBOX_HandleLButtonDown( descr
, keys
, x
, y
);
2106 else if (descr
->style
& LBS_NOTIFY
)
2107 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2111 POINT screenMousePos
;
2112 HWND hWndOldCapture
;
2114 /* Check the Non-Client Area */
2115 screenMousePos
= mousePos
;
2116 hWndOldCapture
= GetCapture();
2118 GetWindowRect(descr
->self
, &screenRect
);
2119 ClientToScreen(descr
->self
, &screenMousePos
);
2121 if(!PtInRect(&screenRect
, screenMousePos
))
2123 LISTBOX_SetCaretIndex( descr
, descr
->lphc
->droppedIndex
, FALSE
);
2124 LISTBOX_SetSelection( descr
, descr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2125 COMBO_FlipListbox( descr
->lphc
, FALSE
, FALSE
);
2129 /* Check to see the NC is a scrollbar */
2131 LONG style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2132 /* Check Vertical scroll bar */
2133 if (style
& WS_VSCROLL
)
2135 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2136 if (PtInRect( &clientRect
, mousePos
))
2137 nHitTestType
= HTVSCROLL
;
2139 /* Check horizontal scroll bar */
2140 if (style
& WS_HSCROLL
)
2142 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2143 if (PtInRect( &clientRect
, mousePos
))
2144 nHitTestType
= HTHSCROLL
;
2146 /* Windows sends this message when a scrollbar is clicked
2149 if(nHitTestType
!= 0)
2151 SendMessageW(descr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2152 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2154 /* Resume the Capture after scrolling is complete
2156 if(hWndOldCapture
!= 0)
2157 SetCapture(hWndOldCapture
);
2163 /***********************************************************************
2164 * LISTBOX_HandleLButtonUp
2166 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2168 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2169 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2170 LISTBOX_Timer
= LB_TIMER_NONE
;
2171 if (descr
->captured
)
2173 descr
->captured
= FALSE
;
2174 if (GetCapture() == descr
->self
) ReleaseCapture();
2175 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2176 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2182 /***********************************************************************
2183 * LISTBOX_HandleTimer
2185 * Handle scrolling upon a timer event.
2186 * Return TRUE if scrolling should continue.
2188 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
, INT index
, TIMER_DIRECTION dir
)
2193 if (descr
->top_item
) index
= descr
->top_item
- 1;
2197 if (descr
->top_item
) index
-= descr
->page_size
;
2200 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2201 if (index
== descr
->focus_item
) index
++;
2202 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2204 case LB_TIMER_RIGHT
:
2205 if (index
+ descr
->page_size
< descr
->nb_items
)
2206 index
+= descr
->page_size
;
2211 if (index
== descr
->focus_item
) return FALSE
;
2212 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2217 /***********************************************************************
2218 * LISTBOX_HandleSystemTimer
2220 * WM_SYSTIMER handler.
2222 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2224 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2226 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2227 LISTBOX_Timer
= LB_TIMER_NONE
;
2233 /***********************************************************************
2234 * LISTBOX_HandleMouseMove
2236 * WM_MOUSEMOVE handler.
2238 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2242 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2244 if (!descr
->captured
) return;
2246 if (descr
->style
& LBS_MULTICOLUMN
)
2249 else if (y
>= descr
->item_height
* descr
->page_size
)
2250 y
= descr
->item_height
* descr
->page_size
- 1;
2254 dir
= LB_TIMER_LEFT
;
2257 else if (x
>= descr
->width
)
2259 dir
= LB_TIMER_RIGHT
;
2260 x
= descr
->width
- 1;
2265 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2266 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2269 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2270 if (index
== -1) index
= descr
->focus_item
;
2271 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2273 /* Start/stop the system timer */
2275 if (dir
!= LB_TIMER_NONE
)
2276 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2277 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2278 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2279 LISTBOX_Timer
= dir
;
2283 /***********************************************************************
2284 * LISTBOX_HandleKeyDown
2286 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2289 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2290 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2291 bForceSelection
= FALSE
; /* only for single select list */
2293 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2295 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2296 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2297 (LPARAM
)descr
->self
);
2298 if (caret
== -2) return 0;
2300 if (caret
== -1) switch(key
)
2303 if (descr
->style
& LBS_MULTICOLUMN
)
2305 bForceSelection
= FALSE
;
2306 if (descr
->focus_item
>= descr
->page_size
)
2307 caret
= descr
->focus_item
- descr
->page_size
;
2312 caret
= descr
->focus_item
- 1;
2313 if (caret
< 0) caret
= 0;
2316 if (descr
->style
& LBS_MULTICOLUMN
)
2318 bForceSelection
= FALSE
;
2319 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2320 caret
= descr
->focus_item
+ descr
->page_size
;
2325 caret
= descr
->focus_item
+ 1;
2326 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2330 if (descr
->style
& LBS_MULTICOLUMN
)
2332 INT page
= descr
->width
/ descr
->column_width
;
2333 if (page
< 1) page
= 1;
2334 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2336 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2337 if (caret
< 0) caret
= 0;
2340 if (descr
->style
& LBS_MULTICOLUMN
)
2342 INT page
= descr
->width
/ descr
->column_width
;
2343 if (page
< 1) page
= 1;
2344 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2346 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2347 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2353 caret
= descr
->nb_items
- 1;
2356 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2357 else if (descr
->style
& LBS_MULTIPLESEL
)
2359 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2360 !descr
->items
[descr
->focus_item
].selected
,
2361 (descr
->style
& LBS_NOTIFY
) != 0 );
2365 bForceSelection
= FALSE
;
2367 if (bForceSelection
) /* focused item is used instead of key */
2368 caret
= descr
->focus_item
;
2371 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2372 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2373 !IS_MULTISELECT(descr
))
2374 descr
->anchor_item
= caret
;
2375 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2377 if (descr
->style
& LBS_MULTIPLESEL
)
2378 descr
->selected_item
= caret
;
2380 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2381 if (descr
->style
& LBS_NOTIFY
)
2383 if (descr
->lphc
&& IsWindowVisible( descr
->self
))
2385 /* make sure that combo parent doesn't hide us */
2386 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2388 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2395 /***********************************************************************
2396 * LISTBOX_HandleChar
2398 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2406 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2408 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2409 MAKEWPARAM(charW
, descr
->focus_item
),
2410 (LPARAM
)descr
->self
);
2411 if (caret
== -2) return 0;
2414 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2417 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2418 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2419 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2420 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2421 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2427 /***********************************************************************
2430 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2433 MEASUREITEMSTRUCT mis
;
2436 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2439 GetClientRect( hwnd
, &rect
);
2441 descr
->owner
= GetParent( descr
->self
);
2442 descr
->style
= GetWindowLongW( descr
->self
, GWL_STYLE
);
2443 descr
->width
= rect
.right
- rect
.left
;
2444 descr
->height
= rect
.bottom
- rect
.top
;
2445 descr
->items
= NULL
;
2446 descr
->nb_items
= 0;
2447 descr
->top_item
= 0;
2448 descr
->selected_item
= -1;
2449 descr
->focus_item
= 0;
2450 descr
->anchor_item
= -1;
2451 descr
->item_height
= 1;
2452 descr
->page_size
= 1;
2453 descr
->column_width
= 150;
2454 descr
->horz_extent
= 0;
2455 descr
->horz_pos
= 0;
2458 descr
->wheel_remain
= 0;
2459 descr
->caret_on
= !lphc
;
2460 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2461 descr
->in_focus
= FALSE
;
2462 descr
->captured
= FALSE
;
2464 descr
->locale
= GetUserDefaultLCID();
2469 TRACE("[%p]: resetting owner %p -> %p\n", descr
->self
, descr
->owner
, lphc
->self
);
2470 descr
->owner
= lphc
->self
;
2473 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2475 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2477 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2478 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2479 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2480 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2482 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2484 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2486 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2487 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2491 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2492 mis
.CtlType
= ODT_LISTBOX
;
2497 mis
.itemHeight
= descr
->item_height
;
2498 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2499 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2503 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2508 /***********************************************************************
2511 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2513 LISTBOX_ResetContent( descr
);
2514 SetWindowLongPtrW( descr
->self
, 0, 0 );
2515 HeapFree( GetProcessHeap(), 0, descr
);
2520 /***********************************************************************
2521 * ListBoxWndProc_common
2523 static LRESULT CALLBACK
LISTBOX_WindowProc( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
2525 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2526 LPHEADCOMBO lphc
= 0;
2531 if (!IsWindow(hwnd
)) return 0;
2533 if (msg
== WM_CREATE
)
2535 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2536 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= lpcs
->lpCreateParams
;
2537 if (!LISTBOX_Create( hwnd
, lphc
)) return -1;
2538 TRACE("creating hwnd %p descr %p\n", hwnd
, (void *)GetWindowLongPtrW( hwnd
, 0 ) );
2541 /* Ignore all other messages before we get a WM_CREATE */
2542 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
2544 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2546 TRACE("[%p]: msg %#x wp %08lx lp %08lx\n", descr
->self
, msg
, wParam
, lParam
);
2550 case LB_RESETCONTENT
:
2551 LISTBOX_ResetContent( descr
);
2552 LISTBOX_UpdateScroll( descr
);
2553 InvalidateRect( descr
->self
, NULL
, TRUE
);
2558 const WCHAR
*textW
= (const WCHAR
*)lParam
;
2559 INT index
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2560 return LISTBOX_InsertString( descr
, index
, textW
);
2563 case LB_INSERTSTRING
:
2564 return LISTBOX_InsertString( descr
, wParam
, (const WCHAR
*)lParam
);
2568 const WCHAR
*textW
= (const WCHAR
*)lParam
;
2569 INT index
= LISTBOX_FindFileStrPos( descr
, textW
);
2570 return LISTBOX_InsertString( descr
, index
, textW
);
2573 case LB_DELETESTRING
:
2574 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2575 return descr
->nb_items
;
2578 SetLastError(ERROR_INVALID_INDEX
);
2582 case LB_GETITEMDATA
:
2583 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2585 SetLastError(ERROR_INVALID_INDEX
);
2588 return descr
->items
[wParam
].data
;
2590 case LB_SETITEMDATA
:
2591 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2593 SetLastError(ERROR_INVALID_INDEX
);
2596 descr
->items
[wParam
].data
= lParam
;
2597 /* undocumented: returns TRUE, not LB_OKAY (0) */
2601 return descr
->nb_items
;
2604 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, TRUE
);
2607 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2609 SetLastError(ERROR_INVALID_INDEX
);
2612 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2613 return strlenW( descr
->items
[wParam
].str
);
2616 if (descr
->nb_items
== 0)
2618 if (!IS_MULTISELECT(descr
))
2619 return descr
->selected_item
;
2620 if (descr
->selected_item
!= -1)
2621 return descr
->selected_item
;
2622 return descr
->focus_item
;
2623 /* otherwise, if the user tries to move the selection with the */
2624 /* arrow keys, we will give the application something to choke on */
2625 case LB_GETTOPINDEX
:
2626 return descr
->top_item
;
2628 case LB_GETITEMHEIGHT
:
2629 return LISTBOX_GetItemHeight( descr
, wParam
);
2631 case LB_SETITEMHEIGHT
:
2632 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2634 case LB_ITEMFROMPOINT
:
2641 /* The hiword of the return value is not a client area
2642 hittest as suggested by MSDN, but rather a hittest on
2643 the returned listbox item. */
2645 if(descr
->nb_items
== 0)
2646 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2648 pt
.x
= (short)LOWORD(lParam
);
2649 pt
.y
= (short)HIWORD(lParam
);
2651 SetRect(&rect
, 0, 0, descr
->width
, descr
->height
);
2653 if(!PtInRect(&rect
, pt
))
2655 pt
.x
= min(pt
.x
, rect
.right
- 1);
2656 pt
.x
= max(pt
.x
, 0);
2657 pt
.y
= min(pt
.y
, rect
.bottom
- 1);
2658 pt
.y
= max(pt
.y
, 0);
2662 index
= LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
);
2666 index
= descr
->nb_items
- 1;
2669 return MAKELONG(index
, hit
? 0 : 1);
2672 case LB_SETCARETINDEX
:
2673 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2674 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2681 case LB_GETCARETINDEX
:
2682 return descr
->focus_item
;
2684 case LB_SETTOPINDEX
:
2685 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2687 case LB_SETCOLUMNWIDTH
:
2688 return LISTBOX_SetColumnWidth( descr
, wParam
);
2690 case LB_GETITEMRECT
:
2691 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2694 return LISTBOX_FindString( descr
, wParam
, (const WCHAR
*)lParam
, FALSE
);
2696 case LB_FINDSTRINGEXACT
:
2697 return LISTBOX_FindString( descr
, wParam
, (const WCHAR
*)lParam
, TRUE
);
2699 case LB_SELECTSTRING
:
2701 const WCHAR
*textW
= (const WCHAR
*)lParam
;
2704 if (HAS_STRINGS(descr
))
2705 TRACE("LB_SELECTSTRING: %s\n", debugstr_w(textW
));
2707 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2708 if (index
!= LB_ERR
)
2710 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2711 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2717 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2719 return descr
->items
[wParam
].selected
;
2722 return LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2725 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2726 LISTBOX_SetCaretIndex( descr
, wParam
, TRUE
);
2727 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2728 if (ret
!= LB_ERR
) ret
= descr
->selected_item
;
2731 case LB_GETSELCOUNT
:
2732 return LISTBOX_GetSelCount( descr
);
2734 case LB_GETSELITEMS
:
2735 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2737 case LB_SELITEMRANGE
:
2738 if (LOWORD(lParam
) <= HIWORD(lParam
))
2739 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
2740 HIWORD(lParam
), wParam
);
2742 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
2743 LOWORD(lParam
), wParam
);
2745 case LB_SELITEMRANGEEX
:
2746 if ((INT
)lParam
>= (INT
)wParam
)
2747 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
2749 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
2751 case LB_GETHORIZONTALEXTENT
:
2752 return descr
->horz_extent
;
2754 case LB_SETHORIZONTALEXTENT
:
2755 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
2757 case LB_GETANCHORINDEX
:
2758 return descr
->anchor_item
;
2760 case LB_SETANCHORINDEX
:
2761 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2763 SetLastError(ERROR_INVALID_INDEX
);
2766 descr
->anchor_item
= (INT
)wParam
;
2770 return LISTBOX_Directory( descr
, wParam
, (const WCHAR
*)lParam
, msg
== LB_DIR
);
2773 return descr
->locale
;
2778 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
2780 ret
= descr
->locale
;
2781 descr
->locale
= (LCID
)wParam
;
2785 case LB_INITSTORAGE
:
2786 return LISTBOX_InitStorage( descr
, wParam
);
2789 return LISTBOX_SetCount( descr
, (INT
)wParam
);
2791 case LB_SETTABSTOPS
:
2792 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
);
2795 if (descr
->caret_on
)
2797 descr
->caret_on
= TRUE
;
2798 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2799 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2803 if (!descr
->caret_on
)
2805 descr
->caret_on
= FALSE
;
2806 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2807 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2810 case LB_GETLISTBOXINFO
:
2811 return descr
->page_size
;
2814 return LISTBOX_Destroy( descr
);
2817 InvalidateRect( descr
->self
, NULL
, TRUE
);
2821 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
2825 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2827 case WM_PRINTCLIENT
:
2831 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( descr
->self
, &ps
);
2832 ret
= LISTBOX_Paint( descr
, hdc
);
2833 if( !wParam
) EndPaint( descr
->self
, &ps
);
2837 LISTBOX_UpdateSize( descr
);
2840 return (LRESULT
)descr
->font
;
2842 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
2843 if (lParam
) InvalidateRect( descr
->self
, 0, TRUE
);
2846 descr
->in_focus
= TRUE
;
2847 descr
->caret_on
= TRUE
;
2848 if (descr
->focus_item
!= -1)
2849 LISTBOX_DrawFocusRect( descr
, TRUE
);
2850 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
2853 LISTBOX_HandleLButtonUp( descr
); /* Release capture if we have it */
2854 descr
->in_focus
= FALSE
;
2855 descr
->wheel_remain
= 0;
2856 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2857 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
2858 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
2861 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
2863 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
2865 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2866 return DefWindowProcW( descr
->self
, msg
, wParam
, lParam
);
2867 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
2868 case WM_LBUTTONDOWN
:
2870 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
2871 (INT16
)LOWORD(lParam
),
2872 (INT16
)HIWORD(lParam
) );
2873 return LISTBOX_HandleLButtonDown( descr
, wParam
,
2874 (INT16
)LOWORD(lParam
),
2875 (INT16
)HIWORD(lParam
) );
2876 case WM_LBUTTONDBLCLK
:
2878 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
2879 (INT16
)LOWORD(lParam
),
2880 (INT16
)HIWORD(lParam
) );
2881 if (descr
->style
& LBS_NOTIFY
)
2882 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
2885 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
2887 BOOL captured
= descr
->captured
;
2891 mousePos
.x
= (INT16
)LOWORD(lParam
);
2892 mousePos
.y
= (INT16
)HIWORD(lParam
);
2895 * If we are in a dropdown combobox, we simulate that
2896 * the mouse is captured to show the tracking of the item.
2898 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
2899 descr
->captured
= TRUE
;
2901 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
2903 descr
->captured
= captured
;
2905 else if (GetCapture() == descr
->self
)
2907 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
2908 (INT16
)HIWORD(lParam
) );
2918 * If the mouse button "up" is not in the listbox,
2919 * we make sure there is no selection by re-selecting the
2920 * item that was selected when the listbox was made visible.
2922 mousePos
.x
= (INT16
)LOWORD(lParam
);
2923 mousePos
.y
= (INT16
)HIWORD(lParam
);
2925 GetClientRect(descr
->self
, &clientRect
);
2928 * When the user clicks outside the combobox and the focus
2929 * is lost, the owning combobox will send a fake buttonup with
2930 * 0xFFFFFFF as the mouse location, we must also revert the
2931 * selection to the original selection.
2933 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
2934 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
2936 return LISTBOX_HandleLButtonUp( descr
);
2938 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
2940 /* for some reason Windows makes it possible to
2941 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2943 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
2944 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
2945 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
2947 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
2951 return LISTBOX_HandleKeyDown( descr
, wParam
);
2953 return LISTBOX_HandleChar( descr
, wParam
);
2956 return LISTBOX_HandleSystemTimer( descr
);
2958 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
2961 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
2962 wParam
, (LPARAM
)descr
->self
);
2963 TRACE("hbrush = %p\n", hbrush
);
2965 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
2968 GetClientRect(descr
->self
, &rect
);
2969 FillRect((HDC
)wParam
, &rect
, hbrush
);
2974 if( lphc
) return 0;
2975 return SendMessageW( descr
->owner
, msg
, wParam
, lParam
);
2978 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
2987 if ((msg
>= WM_USER
) && (msg
< 0xc000))
2988 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
2989 hwnd
, msg
, wParam
, lParam
);
2992 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
2995 void LISTBOX_Register(void)
2999 memset(&wndClass
, 0, sizeof(wndClass
));
3000 wndClass
.style
= CS_PARENTDC
| CS_DBLCLKS
| CS_GLOBALCLASS
;
3001 wndClass
.lpfnWndProc
= LISTBOX_WindowProc
;
3002 wndClass
.cbClsExtra
= 0;
3003 wndClass
.cbWndExtra
= sizeof(LB_DESCR
*);
3004 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
3005 wndClass
.hbrBackground
= NULL
;
3006 wndClass
.lpszClassName
= WC_LISTBOXW
;
3007 RegisterClassW(&wndClass
);
3010 void COMBOLBOX_Register(void)
3012 static const WCHAR combolboxW
[] = {'C','o','m','b','o','L','B','o','x',0};
3015 memset(&wndClass
, 0, sizeof(wndClass
));
3016 wndClass
.style
= CS_SAVEBITS
| CS_DBLCLKS
| CS_DROPSHADOW
| CS_GLOBALCLASS
;
3017 wndClass
.lpfnWndProc
= LISTBOX_WindowProc
;
3018 wndClass
.cbClsExtra
= 0;
3019 wndClass
.cbWndExtra
= sizeof(LB_DESCR
*);
3020 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
3021 wndClass
.hbrBackground
= NULL
;
3022 wndClass
.lpszClassName
= combolboxW
;
3023 RegisterClassW(&wndClass
);