4 * Copyright 1996 Alexandre Julliard
12 #include "wine/winuser16.h"
13 #include "wine/winbase16.h"
14 #include "wine/unicode.h"
21 #include "debugtools.h"
23 DEFAULT_DEBUG_CHANNEL(listbox
);
24 DECLARE_DEBUG_CHANNEL(combo
);
30 * Probably needs improvement:
34 /* Items array granularity */
35 #define LB_ARRAY_GRANULARITY 16
37 /* Scrolling timeout in ms */
38 #define LB_SCROLL_TIMEOUT 50
40 /* Listbox system timer id */
43 /* flag listbox changed while setredraw false - internal style */
44 #define LBS_DISPLAYCHANGED 0x80000000
49 LPWSTR str
; /* Item text */
50 BOOL selected
; /* Is item selected? */
51 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
52 DWORD data
; /* User data */
55 /* Listbox structure */
58 HWND owner
; /* Owner window to send notifications to */
59 UINT style
; /* Window style */
60 INT width
; /* Window width */
61 INT height
; /* Window height */
62 LB_ITEMDATA
*items
; /* Array of items */
63 INT nb_items
; /* Number of items */
64 INT top_item
; /* Top visible item */
65 INT selected_item
; /* Selected item */
66 INT focus_item
; /* Item that has the focus */
67 INT anchor_item
; /* Anchor item for extended selection */
68 INT item_height
; /* Default item height */
69 INT page_size
; /* Items per listbox page */
70 INT column_width
; /* Column width for multi-column listboxes */
71 INT horz_extent
; /* Horizontal extent (0 if no hscroll) */
72 INT horz_pos
; /* Horizontal position */
73 INT nb_tabs
; /* Number of tabs in array */
74 INT
*tabs
; /* Array of tabs */
75 BOOL caret_on
; /* Is caret on? */
76 BOOL captured
; /* Is mouse captured? */
78 HFONT font
; /* Current font */
79 LCID locale
; /* Current locale for string comparisons */
80 LPHEADCOMBO lphc
; /* ComboLBox */
84 #define IS_OWNERDRAW(descr) \
85 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
87 #define HAS_STRINGS(descr) \
88 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
91 #define IS_MULTISELECT(descr) \
92 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
94 #define SEND_NOTIFICATION(wnd,descr,code) \
95 (SendMessageW( (descr)->owner, WM_COMMAND, \
96 MAKEWPARAM((wnd)->wIDmenu, (code)), (wnd)->hwndSelf ))
98 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
100 /* Current timer status */
110 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
112 static LRESULT WINAPI
ComboLBWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
113 static LRESULT WINAPI
ComboLBWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
114 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
115 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
117 static LRESULT
LISTBOX_GetItemRect( LB_DESCR
*descr
, INT index
, RECT
*rect
);
119 /*********************************************************************
120 * listbox class descriptor
122 const struct builtin_class_descr LISTBOX_builtin_class
=
124 "ListBox", /* name */
125 CS_GLOBALCLASS
| CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
126 ListBoxWndProcA
, /* procA */
127 ListBoxWndProcW
, /* procW */
128 sizeof(LB_DESCR
*), /* extra */
129 IDC_ARROWA
, /* cursor */
134 /*********************************************************************
135 * combolbox class descriptor
137 const struct builtin_class_descr COMBOLBOX_builtin_class
=
139 "ComboLBox", /* name */
140 CS_GLOBALCLASS
| CS_DBLCLKS
| CS_SAVEBITS
, /* style */
141 ComboLBWndProcA
, /* procA */
142 ComboLBWndProcW
, /* procW */
143 sizeof(LB_DESCR
*), /* extra */
144 IDC_ARROWA
, /* cursor */
149 /***********************************************************************
152 void LISTBOX_Dump( WND
*wnd
)
156 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
158 TRACE( "Listbox:\n" );
159 TRACE( "hwnd=%04x descr=%08x items=%d top=%d\n",
160 wnd
->hwndSelf
, (UINT
)descr
, descr
->nb_items
,
162 for (i
= 0, item
= descr
->items
; i
< descr
->nb_items
; i
++, item
++)
164 TRACE( "%4d: %-40s %d %08lx %3d\n",
165 i
, debugstr_w(item
->str
), item
->selected
, item
->data
, item
->height
);
170 /***********************************************************************
171 * LISTBOX_GetCurrentPageSize
173 * Return the current page size
175 static INT
LISTBOX_GetCurrentPageSize( LB_DESCR
*descr
)
178 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
179 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
181 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
183 if (i
== descr
->top_item
) return 1;
184 else return i
- descr
->top_item
;
188 /***********************************************************************
189 * LISTBOX_GetMaxTopIndex
191 * Return the maximum possible index for the top of the listbox.
193 static INT
LISTBOX_GetMaxTopIndex( LB_DESCR
*descr
)
197 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
199 page
= descr
->height
;
200 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
201 if ((page
-= descr
->items
[max
].height
) < 0) break;
202 if (max
< descr
->nb_items
- 1) max
++;
204 else if (descr
->style
& LBS_MULTICOLUMN
)
206 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
207 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
208 max
= (max
- page
) * descr
->page_size
;
212 max
= descr
->nb_items
- descr
->page_size
;
214 if (max
< 0) max
= 0;
219 /***********************************************************************
220 * LISTBOX_UpdateScroll
222 * Update the scrollbars. Should be called whenever the content
223 * of the listbox changes.
225 static void LISTBOX_UpdateScroll( WND
*wnd
, LB_DESCR
*descr
)
229 /* Check the listbox scroll bar flags individually before we call
230 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
231 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
232 scroll bar when we do not need one.
233 if (!(descr->style & WS_VSCROLL)) return;
236 /* It is important that we check descr->style, and not wnd->dwStyle,
237 for WS_VSCROLL, as the former is exactly the one passed in
238 argument to CreateWindow.
239 In Windows (and from now on in Wine :) a listbox created
240 with such a style (no WS_SCROLL) does not update
241 the scrollbar with listbox-related data, thus letting
242 the programmer use it for his/her own purposes. */
244 if (descr
->style
& LBS_NOREDRAW
) return;
245 info
.cbSize
= sizeof(info
);
247 if (descr
->style
& LBS_MULTICOLUMN
)
250 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
251 info
.nPos
= descr
->top_item
/ descr
->page_size
;
252 info
.nPage
= descr
->width
/ descr
->column_width
;
253 if (info
.nPage
< 1) info
.nPage
= 1;
254 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
255 if (descr
->style
& LBS_DISABLENOSCROLL
)
256 info
.fMask
|= SIF_DISABLENOSCROLL
;
257 if (descr
->style
& WS_HSCROLL
)
258 SetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
260 info
.fMask
= SIF_RANGE
;
261 if (descr
->style
& WS_VSCROLL
)
262 SetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
267 info
.nMax
= descr
->nb_items
- 1;
268 info
.nPos
= descr
->top_item
;
269 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
270 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
271 if (descr
->style
& LBS_DISABLENOSCROLL
)
272 info
.fMask
|= SIF_DISABLENOSCROLL
;
273 if (descr
->style
& WS_VSCROLL
)
274 SetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
276 if (descr
->horz_extent
)
279 info
.nMax
= descr
->horz_extent
- 1;
280 info
.nPos
= descr
->horz_pos
;
281 info
.nPage
= descr
->width
;
282 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
283 if (descr
->style
& LBS_DISABLENOSCROLL
)
284 info
.fMask
|= SIF_DISABLENOSCROLL
;
285 if (descr
->style
& WS_HSCROLL
)
286 SetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
292 /***********************************************************************
295 * Set the top item of the listbox, scrolling up or down if necessary.
297 static LRESULT
LISTBOX_SetTopItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
300 INT max
= LISTBOX_GetMaxTopIndex( descr
);
301 if (index
> max
) index
= max
;
302 if (index
< 0) index
= 0;
303 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
304 if (descr
->top_item
== index
) return LB_OKAY
;
305 if (descr
->style
& LBS_MULTICOLUMN
)
307 INT diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
308 if (scroll
&& (abs(diff
) < descr
->width
))
309 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
310 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
318 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
322 if (index
> descr
->top_item
)
324 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
325 diff
-= descr
->items
[i
].height
;
329 for (i
= index
; i
< descr
->top_item
; i
++)
330 diff
+= descr
->items
[i
].height
;
334 diff
= (descr
->top_item
- index
) * descr
->item_height
;
336 if (abs(diff
) < descr
->height
)
337 ScrollWindowEx( wnd
->hwndSelf
, 0, diff
, NULL
, NULL
, 0, NULL
,
338 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
342 if (!scroll
) InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
343 descr
->top_item
= index
;
344 LISTBOX_UpdateScroll( wnd
, descr
);
349 /***********************************************************************
352 * Update the page size. Should be called when the size of
353 * the client area or the item height changes.
355 static void LISTBOX_UpdatePage( WND
*wnd
, LB_DESCR
*descr
)
359 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
361 if (page_size
== descr
->page_size
) return;
362 descr
->page_size
= page_size
;
363 if (descr
->style
& LBS_MULTICOLUMN
)
364 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
365 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
, FALSE
);
369 /***********************************************************************
372 * Update the size of the listbox. Should be called when the size of
373 * the client area changes.
375 static void LISTBOX_UpdateSize( WND
*wnd
, LB_DESCR
*descr
)
379 GetClientRect( wnd
->hwndSelf
, &rect
);
380 descr
->width
= rect
.right
- rect
.left
;
381 descr
->height
= rect
.bottom
- rect
.top
;
382 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
386 if(descr
->item_height
!= 0)
387 remaining
= descr
->height
% descr
->item_height
;
390 if ((descr
->height
> descr
->item_height
) && remaining
)
392 if (!(wnd
->flags
& WIN_ISWIN32
))
393 { /* give a margin for error to 16 bits programs - if we need
394 less than the height of the nonclient area, round to the
395 *next* number of items */
396 int ncheight
= wnd
->rectWindow
.bottom
- wnd
->rectWindow
.top
- descr
->height
;
397 if ((descr
->item_height
- remaining
) <= ncheight
)
398 remaining
= remaining
- descr
->item_height
;
400 TRACE("[%04x]: changing height %d -> %d\n",
401 wnd
->hwndSelf
, descr
->height
,
402 descr
->height
- remaining
);
403 SetWindowPos( wnd
->hwndSelf
, 0, 0, 0,
404 wnd
->rectWindow
.right
- wnd
->rectWindow
.left
,
405 wnd
->rectWindow
.bottom
- wnd
->rectWindow
.top
- remaining
,
406 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
410 TRACE("[%04x]: new size = %d,%d\n",
411 wnd
->hwndSelf
, descr
->width
, descr
->height
);
412 LISTBOX_UpdatePage( wnd
, descr
);
413 LISTBOX_UpdateScroll( wnd
, descr
);
415 /* Invalidate the focused item so it will be repainted correctly */
416 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
418 InvalidateRect( wnd
->hwndSelf
, &rect
, FALSE
);
423 /***********************************************************************
424 * LISTBOX_GetItemRect
426 * Get the rectangle enclosing an item, in listbox client coordinates.
427 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
429 static LRESULT
LISTBOX_GetItemRect( LB_DESCR
*descr
, INT index
, RECT
*rect
)
431 /* Index <= 0 is legal even on empty listboxes */
432 if (index
&& (index
>= descr
->nb_items
)) return -1;
433 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
434 if (descr
->style
& LBS_MULTICOLUMN
)
436 INT col
= (index
/ descr
->page_size
) -
437 (descr
->top_item
/ descr
->page_size
);
438 rect
->left
+= col
* descr
->column_width
;
439 rect
->right
= rect
->left
+ descr
->column_width
;
440 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
441 rect
->bottom
= rect
->top
+ descr
->item_height
;
443 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
446 rect
->right
+= descr
->horz_pos
;
447 if ((index
>= 0) && (index
< descr
->nb_items
))
449 if (index
< descr
->top_item
)
451 for (i
= descr
->top_item
-1; i
>= index
; i
--)
452 rect
->top
-= descr
->items
[i
].height
;
456 for (i
= descr
->top_item
; i
< index
; i
++)
457 rect
->top
+= descr
->items
[i
].height
;
459 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
465 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
466 rect
->bottom
= rect
->top
+ descr
->item_height
;
467 rect
->right
+= descr
->horz_pos
;
470 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
471 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
475 /***********************************************************************
476 * LISTBOX_GetItemFromPoint
478 * Return the item nearest from point (x,y) (in client coordinates).
480 static INT
LISTBOX_GetItemFromPoint( LB_DESCR
*descr
, INT x
, INT y
)
482 INT index
= descr
->top_item
;
484 if (!descr
->nb_items
) return -1; /* No items */
485 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
490 while (index
< descr
->nb_items
)
492 if ((pos
+= descr
->items
[index
].height
) > y
) break;
501 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
505 else if (descr
->style
& LBS_MULTICOLUMN
)
507 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
508 if (y
>= 0) index
+= y
/ descr
->item_height
;
509 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
510 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
514 index
+= (y
/ descr
->item_height
);
516 if (index
< 0) return 0;
517 if (index
>= descr
->nb_items
) return -1;
522 /***********************************************************************
527 static void LISTBOX_PaintItem( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
,
528 const RECT
*rect
, INT index
, UINT action
, BOOL ignoreFocus
)
530 LB_ITEMDATA
*item
= NULL
;
531 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
533 if (IS_OWNERDRAW(descr
))
541 if (action
== ODA_FOCUS
)
542 DrawFocusRect( hdc
, rect
);
544 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
548 /* some programs mess with the clipping region when
549 drawing the item, *and* restore the previous region
550 after they are done, so a region has better to exist
551 else everything ends clipped */
552 GetClientRect(wnd
->hwndSelf
, &r
);
553 hrgn
= CreateRectRgnIndirect(&r
);
554 SelectClipRgn( hdc
, hrgn
);
555 DeleteObject( hrgn
);
557 dis
.CtlType
= ODT_LISTBOX
;
558 dis
.CtlID
= wnd
->wIDmenu
;
559 dis
.hwndItem
= wnd
->hwndSelf
;
560 dis
.itemAction
= action
;
564 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
565 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
567 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
568 if (wnd
->dwStyle
& WS_DISABLED
) dis
.itemState
|= ODS_DISABLED
;
569 dis
.itemData
= item
? item
->data
: 0;
571 TRACE("[%04x]: drawitem %d (%s) action=%02x "
572 "state=%02x rect=%d,%d-%d,%d\n",
573 wnd
->hwndSelf
, index
, item
? debugstr_w(item
->str
) : "", action
,
574 dis
.itemState
, rect
->left
, rect
->top
,
575 rect
->right
, rect
->bottom
);
576 SendMessageW(descr
->owner
, WM_DRAWITEM
, wnd
->wIDmenu
, (LPARAM
)&dis
);
580 COLORREF oldText
= 0, oldBk
= 0;
582 if (action
== ODA_FOCUS
)
584 DrawFocusRect( hdc
, rect
);
587 if (item
&& item
->selected
)
589 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
590 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
593 TRACE("[%04x]: painting %d (%s) action=%02x "
594 "rect=%d,%d-%d,%d\n",
595 wnd
->hwndSelf
, index
, item
? debugstr_w(item
->str
) : "", action
,
596 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
598 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
599 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
600 else if (!(descr
->style
& LBS_USETABSTOPS
))
601 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
602 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
603 strlenW(item
->str
), NULL
);
606 /* Output empty string to paint background in the full width. */
607 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
608 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
609 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
610 item
->str
, strlenW(item
->str
),
611 descr
->nb_tabs
, descr
->tabs
, 0);
613 if (item
&& item
->selected
)
615 SetBkColor( hdc
, oldBk
);
616 SetTextColor( hdc
, oldText
);
618 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
620 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
625 /***********************************************************************
628 * Change the redraw flag.
630 static void LISTBOX_SetRedraw( WND
*wnd
, LB_DESCR
*descr
, BOOL on
)
634 if (!(descr
->style
& LBS_NOREDRAW
)) return;
635 descr
->style
&= ~LBS_NOREDRAW
;
636 if (descr
->style
& LBS_DISPLAYCHANGED
)
637 { /* page was changed while setredraw false, refresh automatically */
638 InvalidateRect(wnd
->hwndSelf
, NULL
, TRUE
);
639 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
640 { /* reset top of page if less than number of items/page */
641 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
642 if (descr
->top_item
< 0) descr
->top_item
= 0;
644 descr
->style
&= ~LBS_DISPLAYCHANGED
;
646 LISTBOX_UpdateScroll( wnd
, descr
);
648 else descr
->style
|= LBS_NOREDRAW
;
652 /***********************************************************************
653 * LISTBOX_RepaintItem
655 * Repaint a single item synchronously.
657 static void LISTBOX_RepaintItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
663 HBRUSH hbrush
, oldBrush
= 0;
665 /* Do not repaint the item if the item is not visible */
666 if (!IsWindowVisible(wnd
->hwndSelf
)) return;
667 if (descr
->style
& LBS_NOREDRAW
)
669 descr
->style
|= LBS_DISPLAYCHANGED
;
672 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
673 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
))) return;
674 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
675 hbrush
= SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
676 hdc
, (LPARAM
)wnd
->hwndSelf
);
677 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
678 if (wnd
->dwStyle
& WS_DISABLED
)
679 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
680 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
681 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, index
, action
, FALSE
);
682 if (oldFont
) SelectObject( hdc
, oldFont
);
683 if (oldBrush
) SelectObject( hdc
, oldBrush
);
684 ReleaseDC( wnd
->hwndSelf
, hdc
);
688 /***********************************************************************
689 * LISTBOX_InitStorage
691 static LRESULT
LISTBOX_InitStorage( WND
*wnd
, LB_DESCR
*descr
, INT nb_items
)
695 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
696 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
698 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
699 if (!(item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
700 nb_items
* sizeof(LB_ITEMDATA
) )))
702 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
710 /***********************************************************************
711 * LISTBOX_SetTabStops
713 static BOOL
LISTBOX_SetTabStops( WND
*wnd
, LB_DESCR
*descr
, INT count
,
714 LPINT tabs
, BOOL short_ints
)
716 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
717 if (descr
->tabs
) HeapFree( GetProcessHeap(), 0, descr
->tabs
);
718 if (!(descr
->nb_tabs
= count
))
723 /* FIXME: count = 1 */
724 if (!(descr
->tabs
= (INT
*)HeapAlloc( GetProcessHeap(), 0,
725 descr
->nb_tabs
* sizeof(INT
) )))
730 LPINT16 p
= (LPINT16
)tabs
;
732 TRACE("[%04x]: settabstops ", wnd
->hwndSelf
);
733 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
734 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
735 if (TRACE_ON(listbox
)) DPRINTF("%hd ", descr
->tabs
[i
]);
737 if (TRACE_ON(listbox
)) DPRINTF("\n");
739 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
740 /* FIXME: repaint the window? */
745 /***********************************************************************
748 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPARAM lParam
, BOOL unicode
)
750 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
751 if (HAS_STRINGS(descr
))
754 return strlenW(descr
->items
[index
].str
);
756 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
760 LPWSTR buffer
= (LPWSTR
)lParam
;
761 strcpyW( buffer
, descr
->items
[index
].str
);
762 return strlenW(buffer
);
766 LPSTR buffer
= (LPSTR
)lParam
;
767 return WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1, buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
771 *((LPDWORD
)lParam
)=*(LPDWORD
)(&descr
->items
[index
].data
);
772 return sizeof(DWORD
);
777 /***********************************************************************
778 * LISTBOX_FindStringPos
780 * Find the nearest string located before a given string in sort order.
781 * If 'exact' is TRUE, return an error if we don't get an exact match.
783 static INT
LISTBOX_FindStringPos( WND
*wnd
, LB_DESCR
*descr
, LPCWSTR str
,
786 INT index
, min
, max
, res
= -1;
788 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
790 max
= descr
->nb_items
;
793 index
= (min
+ max
) / 2;
794 if (HAS_STRINGS(descr
))
795 res
= lstrcmpiW( descr
->items
[index
].str
, str
);
798 COMPAREITEMSTRUCT cis
;
800 cis
.CtlType
= ODT_LISTBOX
;
801 cis
.CtlID
= wnd
->wIDmenu
;
802 cis
.hwndItem
= wnd
->hwndSelf
;
804 cis
.itemData1
= descr
->items
[index
].data
;
806 cis
.itemData2
= (DWORD
)str
;
807 cis
.dwLocaleId
= descr
->locale
;
808 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
,
809 wnd
->wIDmenu
, (LPARAM
)&cis
);
811 if (!res
) return index
;
812 if (res
> 0) max
= index
;
813 else min
= index
+ 1;
815 return exact
? -1 : max
;
819 /***********************************************************************
820 * LISTBOX_FindFileStrPos
822 * Find the nearest string located before a given string in directory
823 * sort order (i.e. first files, then directories, then drives).
825 static INT
LISTBOX_FindFileStrPos( WND
*wnd
, LB_DESCR
*descr
, LPCWSTR str
)
827 INT min
, max
, res
= -1;
829 if (!HAS_STRINGS(descr
))
830 return LISTBOX_FindStringPos( wnd
, descr
, str
, FALSE
);
832 max
= descr
->nb_items
;
835 INT index
= (min
+ max
) / 2;
836 LPCWSTR p
= descr
->items
[index
].str
;
837 if (*p
== '[') /* drive or directory */
839 if (*str
!= '[') res
= -1;
840 else if (p
[1] == '-') /* drive */
842 if (str
[1] == '-') res
= str
[2] - p
[2];
847 if (str
[1] == '-') res
= 1;
848 else res
= lstrcmpiW( str
, p
);
853 if (*str
== '[') res
= 1;
854 else res
= lstrcmpiW( str
, p
);
856 if (!res
) return index
;
857 if (res
< 0) max
= index
;
858 else min
= index
+ 1;
864 /***********************************************************************
867 * Find the item beginning with a given string.
869 static INT
LISTBOX_FindString( WND
*wnd
, LB_DESCR
*descr
, INT start
,
870 LPCWSTR str
, BOOL exact
)
875 if (start
>= descr
->nb_items
) start
= -1;
876 item
= descr
->items
+ start
+ 1;
877 if (HAS_STRINGS(descr
))
879 if (!str
|| ! str
[0] ) return LB_ERR
;
882 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
883 if (!lstrcmpiW( str
, item
->str
)) return i
;
884 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
885 if (!lstrcmpiW( str
, item
->str
)) return i
;
889 /* Special case for drives and directories: ignore prefix */
890 #define CHECK_DRIVE(item) \
891 if ((item)->str[0] == '[') \
893 if (!strncmpiW( str, (item)->str+1, len )) return i; \
894 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
898 INT len
= strlenW(str
);
899 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
901 if (!strncmpiW( str
, item
->str
, len
)) return i
;
904 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
906 if (!strncmpiW( str
, item
->str
, len
)) return i
;
914 if (exact
&& (descr
->style
& LBS_SORT
))
915 /* If sorted, use a WM_COMPAREITEM binary search */
916 return LISTBOX_FindStringPos( wnd
, descr
, str
, TRUE
);
918 /* Otherwise use a linear search */
919 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
920 if (item
->data
== (DWORD
)str
) return i
;
921 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
922 if (item
->data
== (DWORD
)str
) return i
;
928 /***********************************************************************
929 * LISTBOX_GetSelCount
931 static LRESULT
LISTBOX_GetSelCount( LB_DESCR
*descr
)
934 LB_ITEMDATA
*item
= descr
->items
;
936 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
937 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
938 if (item
->selected
) count
++;
943 /***********************************************************************
944 * LISTBOX_GetSelItems16
946 static LRESULT
LISTBOX_GetSelItems16( LB_DESCR
*descr
, INT16 max
, LPINT16 array
)
949 LB_ITEMDATA
*item
= descr
->items
;
951 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
952 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
953 if (item
->selected
) array
[count
++] = (INT16
)i
;
958 /***********************************************************************
959 * LISTBOX_GetSelItems
961 static LRESULT
LISTBOX_GetSelItems( LB_DESCR
*descr
, INT max
, LPINT array
)
964 LB_ITEMDATA
*item
= descr
->items
;
966 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
967 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
968 if (item
->selected
) array
[count
++] = i
;
973 /***********************************************************************
976 static LRESULT
LISTBOX_Paint( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
)
978 INT i
, col_pos
= descr
->page_size
- 1;
980 RECT focusRect
= {-1, -1, -1, -1};
982 HBRUSH hbrush
, oldBrush
= 0;
984 if (descr
->style
& LBS_NOREDRAW
) return 0;
986 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
987 if (descr
->style
& LBS_MULTICOLUMN
)
988 rect
.right
= rect
.left
+ descr
->column_width
;
989 else if (descr
->horz_pos
)
991 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
992 rect
.right
+= descr
->horz_pos
;
995 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
996 hbrush
= SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
997 hdc
, (LPARAM
)wnd
->hwndSelf
);
998 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
999 if (wnd
->dwStyle
& WS_DISABLED
)
1000 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1002 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1005 /* Special case for empty listbox: paint focus rect */
1006 rect
.bottom
= rect
.top
+ descr
->item_height
;
1007 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, descr
->focus_item
,
1009 rect
.top
= rect
.bottom
;
1012 /* Paint all the item, regarding the selection
1013 Focus state will be painted after */
1015 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1017 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1018 rect
.bottom
= rect
.top
+ descr
->item_height
;
1020 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1022 if (i
== descr
->focus_item
)
1024 /* keep the focus rect, to paint the focus item after */
1025 focusRect
.left
= rect
.left
;
1026 focusRect
.right
= rect
.right
;
1027 focusRect
.top
= rect
.top
;
1028 focusRect
.bottom
= rect
.bottom
;
1030 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1031 rect
.top
= rect
.bottom
;
1033 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1035 if (!IS_OWNERDRAW(descr
))
1037 /* Clear the bottom of the column */
1038 if (rect
.top
< descr
->height
)
1040 rect
.bottom
= descr
->height
;
1041 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1042 &rect
, NULL
, 0, NULL
);
1046 /* Go to the next column */
1047 rect
.left
+= descr
->column_width
;
1048 rect
.right
+= descr
->column_width
;
1050 col_pos
= descr
->page_size
- 1;
1055 if (rect
.top
>= descr
->height
) break;
1059 /* Paint the focus item now */
1060 if (focusRect
.top
!= focusRect
.bottom
&& descr
->caret_on
)
1061 LISTBOX_PaintItem( wnd
, descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1063 if (!IS_OWNERDRAW(descr
))
1065 /* Clear the remainder of the client area */
1066 if (rect
.top
< descr
->height
)
1068 rect
.bottom
= descr
->height
;
1069 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1070 &rect
, NULL
, 0, NULL
);
1072 if (rect
.right
< descr
->width
)
1074 rect
.left
= rect
.right
;
1075 rect
.right
= descr
->width
;
1077 rect
.bottom
= descr
->height
;
1078 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1079 &rect
, NULL
, 0, NULL
);
1082 if (oldFont
) SelectObject( hdc
, oldFont
);
1083 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1088 /***********************************************************************
1089 * LISTBOX_InvalidateItems
1091 * Invalidate all items from a given item. If the specified item is not
1092 * visible, nothing happens.
1094 static void LISTBOX_InvalidateItems( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1098 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1100 if (descr
->style
& LBS_NOREDRAW
)
1102 descr
->style
|= LBS_DISPLAYCHANGED
;
1105 rect
.bottom
= descr
->height
;
1106 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
1107 if (descr
->style
& LBS_MULTICOLUMN
)
1109 /* Repaint the other columns */
1110 rect
.left
= rect
.right
;
1111 rect
.right
= descr
->width
;
1113 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
1119 /***********************************************************************
1120 * LISTBOX_GetItemHeight
1122 static LRESULT
LISTBOX_GetItemHeight( LB_DESCR
*descr
, INT index
)
1124 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1126 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1127 return descr
->items
[index
].height
;
1129 else return descr
->item_height
;
1133 /***********************************************************************
1134 * LISTBOX_SetItemHeight
1136 static LRESULT
LISTBOX_SetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1139 if (!height
) height
= 1;
1141 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1143 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1144 TRACE("[%04x]: item %d height = %d\n",
1145 wnd
->hwndSelf
, index
, height
);
1146 descr
->items
[index
].height
= height
;
1147 LISTBOX_UpdateScroll( wnd
, descr
);
1148 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1150 else if (height
!= descr
->item_height
)
1152 TRACE("[%04x]: new height = %d\n",
1153 wnd
->hwndSelf
, height
);
1154 descr
->item_height
= height
;
1155 LISTBOX_UpdatePage( wnd
, descr
);
1156 LISTBOX_UpdateScroll( wnd
, descr
);
1157 InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
1163 /***********************************************************************
1164 * LISTBOX_SetHorizontalPos
1166 static void LISTBOX_SetHorizontalPos( WND
*wnd
, LB_DESCR
*descr
, INT pos
)
1170 if (pos
> descr
->horz_extent
- descr
->width
)
1171 pos
= descr
->horz_extent
- descr
->width
;
1172 if (pos
< 0) pos
= 0;
1173 if (!(diff
= descr
->horz_pos
- pos
)) return;
1174 TRACE("[%04x]: new horz pos = %d\n",
1175 wnd
->hwndSelf
, pos
);
1176 descr
->horz_pos
= pos
;
1177 LISTBOX_UpdateScroll( wnd
, descr
);
1178 if (abs(diff
) < descr
->width
)
1179 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
1180 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1182 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1186 /***********************************************************************
1187 * LISTBOX_SetHorizontalExtent
1189 static LRESULT
LISTBOX_SetHorizontalExtent( WND
*wnd
, LB_DESCR
*descr
,
1192 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1194 if (extent
<= 0) extent
= 1;
1195 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1196 TRACE("[%04x]: new horz extent = %d\n",
1197 wnd
->hwndSelf
, extent
);
1198 descr
->horz_extent
= extent
;
1199 if (descr
->horz_pos
> extent
- descr
->width
)
1200 LISTBOX_SetHorizontalPos( wnd
, descr
, extent
- descr
->width
);
1202 LISTBOX_UpdateScroll( wnd
, descr
);
1207 /***********************************************************************
1208 * LISTBOX_SetColumnWidth
1210 static LRESULT
LISTBOX_SetColumnWidth( WND
*wnd
, LB_DESCR
*descr
, INT width
)
1212 if (width
== descr
->column_width
) return LB_OKAY
;
1213 TRACE("[%04x]: new column width = %d\n",
1214 wnd
->hwndSelf
, width
);
1215 descr
->column_width
= width
;
1216 LISTBOX_UpdatePage( wnd
, descr
);
1221 /***********************************************************************
1224 * Returns the item height.
1226 static INT
LISTBOX_SetFont( WND
*wnd
, LB_DESCR
*descr
, HFONT font
)
1234 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
)))
1236 ERR("unable to get DC.\n" );
1239 if (font
) oldFont
= SelectObject( hdc
, font
);
1240 GetTextMetricsW( hdc
, &tm
);
1241 if (oldFont
) SelectObject( hdc
, oldFont
);
1242 ReleaseDC( wnd
->hwndSelf
, hdc
);
1243 if (!IS_OWNERDRAW(descr
))
1244 LISTBOX_SetItemHeight( wnd
, descr
, 0, tm
.tmHeight
);
1245 return tm
.tmHeight
;
1249 /***********************************************************************
1250 * LISTBOX_MakeItemVisible
1252 * Make sure that a given item is partially or fully visible.
1254 static void LISTBOX_MakeItemVisible( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1259 if (index
<= descr
->top_item
) top
= index
;
1260 else if (descr
->style
& LBS_MULTICOLUMN
)
1262 INT cols
= descr
->width
;
1263 if (!fully
) cols
+= descr
->column_width
- 1;
1264 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1266 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1267 top
= index
- descr
->page_size
* (cols
- 1);
1269 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1271 INT height
= fully
? descr
->items
[index
].height
: 1;
1272 for (top
= index
; top
> descr
->top_item
; top
--)
1273 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1277 if (index
< descr
->top_item
+ descr
->page_size
) return;
1278 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1279 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1280 top
= index
- descr
->page_size
+ 1;
1282 LISTBOX_SetTopItem( wnd
, descr
, top
, TRUE
);
1285 /***********************************************************************
1286 * LISTBOX_SetCaretIndex
1289 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1292 static LRESULT
LISTBOX_SetCaretIndex( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1293 BOOL fully_visible
)
1295 INT oldfocus
= descr
->focus_item
;
1297 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1298 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1299 if (index
== oldfocus
) return LB_OKAY
;
1300 descr
->focus_item
= index
;
1301 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1302 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1304 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1305 if (descr
->caret_on
&& (descr
->in_focus
))
1306 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1312 /***********************************************************************
1313 * LISTBOX_SelectItemRange
1315 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1317 static LRESULT
LISTBOX_SelectItemRange( WND
*wnd
, LB_DESCR
*descr
, INT first
,
1322 /* A few sanity checks */
1324 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1325 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1326 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1327 if (last
== -1) last
= descr
->nb_items
- 1;
1328 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1329 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1330 /* selected_item reflects last selected/unselected item on multiple sel */
1331 descr
->selected_item
= last
;
1333 if (on
) /* Turn selection on */
1335 for (i
= first
; i
<= last
; i
++)
1337 if (descr
->items
[i
].selected
) continue;
1338 descr
->items
[i
].selected
= TRUE
;
1339 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1341 LISTBOX_SetCaretIndex( wnd
, descr
, last
, TRUE
);
1343 else /* Turn selection off */
1345 for (i
= first
; i
<= last
; i
++)
1347 if (!descr
->items
[i
].selected
) continue;
1348 descr
->items
[i
].selected
= FALSE
;
1349 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1355 /***********************************************************************
1356 * LISTBOX_SetSelection
1358 static LRESULT
LISTBOX_SetSelection( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1359 BOOL on
, BOOL send_notify
)
1361 TRACE( "index=%d notify=%s\n", index
, send_notify
? "YES" : "NO" );
1363 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1364 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1365 if (descr
->style
& LBS_MULTIPLESEL
)
1367 if (index
== -1) /* Select all items */
1368 return LISTBOX_SelectItemRange( wnd
, descr
, 0, -1, on
);
1369 else /* Only one item */
1370 return LISTBOX_SelectItemRange( wnd
, descr
, index
, index
, on
);
1374 INT oldsel
= descr
->selected_item
;
1375 if (index
== oldsel
) return LB_OKAY
;
1376 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1377 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1378 descr
->selected_item
= index
;
1379 if (oldsel
!= -1) LISTBOX_RepaintItem( wnd
, descr
, oldsel
, ODA_SELECT
);
1380 if (index
!= -1) LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_SELECT
);
1381 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( wnd
, descr
,
1382 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1384 if( descr
->lphc
) /* set selection change flag for parent combo */
1385 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1391 /***********************************************************************
1394 * Change the caret position and extend the selection to the new caret.
1396 static void LISTBOX_MoveCaret( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1397 BOOL fully_visible
)
1399 INT oldfocus
= descr
->focus_item
;
1401 if ((index
< 0) || (index
>= descr
->nb_items
))
1404 /* Important, repaint needs to be done in this order if
1405 you want to mimic Windows behavior:
1406 1. Remove the focus and paint the item
1407 2. Remove the selection and paint the item(s)
1408 3. Set the selection and repaint the item(s)
1409 4. Set the focus to 'index' and repaint the item */
1411 /* 1. remove the focus and repaint the item */
1412 descr
->focus_item
= -1;
1413 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1414 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1416 /* 2. then turn off the previous selection */
1417 /* 3. repaint the new selected item */
1418 if (descr
->style
& LBS_EXTENDEDSEL
)
1420 if (descr
->anchor_item
!= -1)
1422 INT first
= min( index
, descr
->anchor_item
);
1423 INT last
= max( index
, descr
->anchor_item
);
1425 LISTBOX_SelectItemRange( wnd
, descr
, 0, first
- 1, FALSE
);
1426 LISTBOX_SelectItemRange( wnd
, descr
, last
+ 1, -1, FALSE
);
1427 LISTBOX_SelectItemRange( wnd
, descr
, first
, last
, TRUE
);
1430 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1432 /* Set selection to new caret item */
1433 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
1436 /* 4. repaint the new item with the focus */
1437 descr
->focus_item
= index
;
1438 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1439 if (descr
->caret_on
&& (descr
->in_focus
))
1440 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1444 /***********************************************************************
1445 * LISTBOX_InsertItem
1447 static LRESULT
LISTBOX_InsertItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1448 LPWSTR str
, DWORD data
)
1452 INT oldfocus
= descr
->focus_item
;
1454 if (index
== -1) index
= descr
->nb_items
;
1455 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1456 if (!descr
->items
) max_items
= 0;
1457 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1458 if (descr
->nb_items
== max_items
)
1460 /* We need to grow the array */
1461 max_items
+= LB_ARRAY_GRANULARITY
;
1462 if (!(item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1463 max_items
* sizeof(LB_ITEMDATA
) )))
1465 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1468 descr
->items
= item
;
1471 /* Insert the item structure */
1473 item
= &descr
->items
[index
];
1474 if (index
< descr
->nb_items
)
1475 RtlMoveMemory( item
+ 1, item
,
1476 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1480 item
->selected
= FALSE
;
1483 /* Get item height */
1485 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1487 MEASUREITEMSTRUCT mis
;
1489 mis
.CtlType
= ODT_LISTBOX
;
1490 mis
.CtlID
= wnd
->wIDmenu
;
1492 mis
.itemData
= descr
->items
[index
].data
;
1493 mis
.itemHeight
= descr
->item_height
;
1494 SendMessageW( descr
->owner
, WM_MEASUREITEM
, wnd
->wIDmenu
, (LPARAM
)&mis
);
1495 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1496 TRACE("[%04x]: measure item %d (%s) = %d\n",
1497 wnd
->hwndSelf
, index
, str
? debugstr_w(str
) : "", item
->height
);
1500 /* Repaint the items */
1502 LISTBOX_UpdateScroll( wnd
, descr
);
1503 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1505 /* Move selection and focused item */
1506 /* If listbox was empty, set focus to the first item */
1507 if (descr
->nb_items
== 1)
1508 LISTBOX_SetCaretIndex( wnd
, descr
, 0, FALSE
);
1509 /* single select don't change selection index in win31 */
1510 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1512 descr
->selected_item
++;
1513 LISTBOX_SetSelection( wnd
, descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1517 if (index
<= descr
->selected_item
)
1519 descr
->selected_item
++;
1520 descr
->focus_item
= oldfocus
; /* focus not changed */
1527 /***********************************************************************
1528 * LISTBOX_InsertString
1530 static LRESULT
LISTBOX_InsertString( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1533 LPWSTR new_str
= NULL
;
1537 if (HAS_STRINGS(descr
))
1539 static const WCHAR empty_stringW
[] = { 0 };
1540 if (!str
) str
= empty_stringW
;
1541 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1543 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1546 strcpyW(new_str
, str
);
1548 else data
= (DWORD
)str
;
1550 if (index
== -1) index
= descr
->nb_items
;
1551 if ((ret
= LISTBOX_InsertItem( wnd
, descr
, index
, new_str
, data
)) != 0)
1553 if (new_str
) HeapFree( GetProcessHeap(), 0, new_str
);
1557 TRACE("[%04x]: added item %d '%s'\n",
1558 wnd
->hwndSelf
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1563 /***********************************************************************
1564 * LISTBOX_DeleteItem
1566 * Delete the content of an item. 'index' must be a valid index.
1568 static void LISTBOX_DeleteItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1570 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1571 * while Win95 sends it for all items with user data.
1572 * It's probably better to send it too often than not
1573 * often enough, so this is what we do here.
1575 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1577 DELETEITEMSTRUCT dis
;
1579 dis
.CtlType
= ODT_LISTBOX
;
1580 dis
.CtlID
= wnd
->wIDmenu
;
1582 dis
.hwndItem
= wnd
->hwndSelf
;
1583 dis
.itemData
= descr
->items
[index
].data
;
1584 SendMessageW( descr
->owner
, WM_DELETEITEM
, wnd
->wIDmenu
, (LPARAM
)&dis
);
1586 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1587 HeapFree( GetProcessHeap(), 0, descr
->items
[index
].str
);
1591 /***********************************************************************
1592 * LISTBOX_RemoveItem
1594 * Remove an item from the listbox and delete its content.
1596 static LRESULT
LISTBOX_RemoveItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1601 if ((index
== -1) && (descr
->nb_items
> 0)) index
= descr
->nb_items
- 1;
1602 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1604 /* We need to invalidate the original rect instead of the updated one. */
1605 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1607 LISTBOX_DeleteItem( wnd
, descr
, index
);
1609 /* Remove the item */
1611 item
= &descr
->items
[index
];
1612 if (index
< descr
->nb_items
-1)
1613 RtlMoveMemory( item
, item
+ 1,
1614 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1616 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1618 /* Shrink the item array if possible */
1620 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1621 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1623 max_items
-= LB_ARRAY_GRANULARITY
;
1624 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1625 max_items
* sizeof(LB_ITEMDATA
) );
1626 if (item
) descr
->items
= item
;
1628 /* Repaint the items */
1630 LISTBOX_UpdateScroll( wnd
, descr
);
1631 /* if we removed the scrollbar, reset the top of the list
1632 (correct for owner-drawn ???) */
1633 if (descr
->nb_items
== descr
->page_size
)
1634 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1636 /* Move selection and focused item */
1637 if (!IS_MULTISELECT(descr
))
1639 if (index
== descr
->selected_item
)
1640 descr
->selected_item
= -1;
1641 else if (index
< descr
->selected_item
)
1643 descr
->selected_item
--;
1644 if (ISWIN31
) /* win 31 do not change the selected item number */
1645 LISTBOX_SetSelection( wnd
, descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1649 if (descr
->focus_item
>= descr
->nb_items
)
1651 descr
->focus_item
= descr
->nb_items
- 1;
1652 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1658 /***********************************************************************
1659 * LISTBOX_ResetContent
1661 static void LISTBOX_ResetContent( WND
*wnd
, LB_DESCR
*descr
)
1665 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( wnd
, descr
, i
);
1666 if (descr
->items
) HeapFree( GetProcessHeap(), 0, descr
->items
);
1667 descr
->nb_items
= 0;
1668 descr
->top_item
= 0;
1669 descr
->selected_item
= -1;
1670 descr
->focus_item
= 0;
1671 descr
->anchor_item
= -1;
1672 descr
->items
= NULL
;
1676 /***********************************************************************
1679 static LRESULT
LISTBOX_SetCount( WND
*wnd
, LB_DESCR
*descr
, INT count
)
1683 if (HAS_STRINGS(descr
)) return LB_ERR
;
1684 /* FIXME: this is far from optimal... */
1685 if (count
> descr
->nb_items
)
1687 while (count
> descr
->nb_items
)
1688 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, 0 )) < 0)
1691 else if (count
< descr
->nb_items
)
1693 while (count
< descr
->nb_items
)
1694 if ((ret
= LISTBOX_RemoveItem( wnd
, descr
, -1 )) < 0)
1701 /***********************************************************************
1704 static LRESULT
LISTBOX_Directory( WND
*wnd
, LB_DESCR
*descr
, UINT attrib
,
1705 LPCWSTR filespec
, BOOL long_names
)
1708 LRESULT ret
= LB_OKAY
;
1709 WIN32_FIND_DATAW entry
;
1712 /* don't scan directory if we just want drives exclusively */
1713 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1714 /* scan directory */
1715 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1717 if (GetLastError() != ERROR_NO_MORE_FILES
) return LB_ERR
;
1724 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1726 static const WCHAR bracketW
[] = { ']',0 };
1727 static const WCHAR dotW
[] = { '.',0 };
1728 if (!(attrib
& DDL_DIRECTORY
) ||
1729 !strcmpW( entry
.cAlternateFileName
, dotW
)) continue;
1731 if (long_names
) strcpyW( buffer
+ 1, entry
.cFileName
);
1732 else strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1733 strcatW(buffer
, bracketW
);
1735 else /* not a directory */
1737 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1738 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1740 if ((attrib
& DDL_EXCLUSIVE
) &&
1741 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1744 if (long_names
) strcpyW( buffer
, entry
.cFileName
);
1745 else strcpyW( buffer
, entry
.cAlternateFileName
);
1747 if (!long_names
) CharLowerW( buffer
);
1748 pos
= LISTBOX_FindFileStrPos( wnd
, descr
, buffer
);
1749 if ((ret
= LISTBOX_InsertString( wnd
, descr
, pos
, buffer
)) < 0)
1751 } while (FindNextFileW( handle
, &entry
));
1752 FindClose( handle
);
1757 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1759 WCHAR buffer
[] = {'[','-','a','-',']',0};
1760 WCHAR root
[] = {'A',':','\\',0};
1762 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1764 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1765 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, buffer
)) < 0)
1773 /***********************************************************************
1774 * LISTBOX_HandleVScroll
1776 static LRESULT
LISTBOX_HandleVScroll( WND
*wnd
, LB_DESCR
*descr
, WPARAM wParam
)
1780 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1781 switch(LOWORD(wParam
))
1784 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
- 1, TRUE
);
1787 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ 1, TRUE
);
1790 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-
1791 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1794 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+
1795 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1797 case SB_THUMBPOSITION
:
1798 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
), TRUE
);
1801 info
.cbSize
= sizeof(info
);
1802 info
.fMask
= SIF_TRACKPOS
;
1803 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1804 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
, TRUE
);
1807 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1810 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1817 /***********************************************************************
1818 * LISTBOX_HandleHScroll
1820 static LRESULT
LISTBOX_HandleHScroll( WND
*wnd
, LB_DESCR
*descr
, WPARAM wParam
)
1825 if (descr
->style
& LBS_MULTICOLUMN
)
1827 switch(LOWORD(wParam
))
1830 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-descr
->page_size
,
1834 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+descr
->page_size
,
1838 page
= descr
->width
/ descr
->column_width
;
1839 if (page
< 1) page
= 1;
1840 LISTBOX_SetTopItem( wnd
, descr
,
1841 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1844 page
= descr
->width
/ descr
->column_width
;
1845 if (page
< 1) page
= 1;
1846 LISTBOX_SetTopItem( wnd
, descr
,
1847 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1849 case SB_THUMBPOSITION
:
1850 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1854 info
.cbSize
= sizeof(info
);
1855 info
.fMask
= SIF_TRACKPOS
;
1856 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1857 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1861 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1864 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1868 else if (descr
->horz_extent
)
1870 switch(LOWORD(wParam
))
1873 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
- 1 );
1876 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
+ 1 );
1879 LISTBOX_SetHorizontalPos( wnd
, descr
,
1880 descr
->horz_pos
- descr
->width
);
1883 LISTBOX_SetHorizontalPos( wnd
, descr
,
1884 descr
->horz_pos
+ descr
->width
);
1886 case SB_THUMBPOSITION
:
1887 LISTBOX_SetHorizontalPos( wnd
, descr
, HIWORD(wParam
) );
1890 info
.cbSize
= sizeof(info
);
1891 info
.fMask
= SIF_TRACKPOS
;
1892 GetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
);
1893 LISTBOX_SetHorizontalPos( wnd
, descr
, info
.nTrackPos
);
1896 LISTBOX_SetHorizontalPos( wnd
, descr
, 0 );
1899 LISTBOX_SetHorizontalPos( wnd
, descr
,
1900 descr
->horz_extent
- descr
->width
);
1907 static LRESULT
LISTBOX_HandleMouseWheel(WND
*wnd
, LB_DESCR
*descr
, WPARAM wParam
)
1909 short gcWheelDelta
= 0;
1910 UINT pulScrollLines
= 3;
1912 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1914 gcWheelDelta
-= (short) HIWORD(wParam
);
1916 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
1918 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
1919 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
1920 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ cLineScroll
, TRUE
);
1925 /***********************************************************************
1926 * LISTBOX_HandleLButtonDown
1928 static LRESULT
LISTBOX_HandleLButtonDown( WND
*wnd
, LB_DESCR
*descr
,
1929 WPARAM wParam
, INT x
, INT y
)
1931 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
1932 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1933 wnd
->hwndSelf
, x
, y
, index
);
1934 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
1936 if (!descr
->in_focus
)
1938 if( !descr
->lphc
) SetFocus( wnd
->hwndSelf
);
1939 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
1940 : descr
->lphc
->self
->hwndSelf
);
1943 if (index
== -1) return 0;
1945 if (descr
->style
& LBS_EXTENDEDSEL
)
1947 /* we should perhaps make sure that all items are deselected
1948 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1949 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1950 LISTBOX_SetSelection( wnd, descr, -1, FALSE, FALSE);
1953 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1954 if (wParam
& MK_CONTROL
)
1956 LISTBOX_SetCaretIndex( wnd
, descr
, index
, FALSE
);
1957 LISTBOX_SetSelection( wnd
, descr
, index
,
1958 !descr
->items
[index
].selected
,
1959 (descr
->style
& LBS_NOTIFY
) != 0);
1961 else LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1965 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1966 LISTBOX_SetSelection( wnd
, descr
, index
,
1967 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1968 !descr
->items
[index
].selected
),
1969 (descr
->style
& LBS_NOTIFY
) != 0 );
1972 descr
->captured
= TRUE
;
1973 SetCapture( wnd
->hwndSelf
);
1977 if (descr
->style
& LBS_NOTIFY
)
1978 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
1979 MAKELPARAM( x
, y
) );
1980 if (wnd
->dwExStyle
& WS_EX_DRAGDETECT
)
1987 if (DragDetect( wnd
->hwndSelf
, pt
))
1988 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
1995 /*************************************************************************
1996 * LISTBOX_HandleLButtonDownCombo [Internal]
1998 * Process LButtonDown message for the ComboListBox
2001 * pWnd [I] The windows internal structure
2002 * pDescr [I] The ListBox internal structure
2003 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2004 * x [I] X Mouse Coordinate
2005 * y [I] Y Mouse Coordinate
2008 * 0 since we are processing the WM_LBUTTONDOWN Message
2011 * This function is only to be used when a ListBox is a ComboListBox
2014 static LRESULT
LISTBOX_HandleLButtonDownCombo( WND
*pWnd
, LB_DESCR
*pDescr
,
2015 UINT msg
, WPARAM wParam
, INT x
, INT y
)
2017 RECT clientRect
, screenRect
;
2023 GetClientRect(pWnd
->hwndSelf
, &clientRect
);
2025 if(PtInRect(&clientRect
, mousePos
))
2027 /* MousePos is in client, resume normal processing */
2028 if (msg
== WM_LBUTTONDOWN
)
2030 pDescr
->lphc
->droppedIndex
= pDescr
->nb_items
? pDescr
->selected_item
: -1;
2031 return LISTBOX_HandleLButtonDown( pWnd
, pDescr
, wParam
, x
, y
);
2033 else if (pDescr
->style
& LBS_NOTIFY
)
2034 SEND_NOTIFICATION( pWnd
, pDescr
, LBN_DBLCLK
);
2039 POINT screenMousePos
;
2040 HWND hWndOldCapture
;
2042 /* Check the Non-Client Area */
2043 screenMousePos
= mousePos
;
2044 hWndOldCapture
= GetCapture();
2046 GetWindowRect(pWnd
->hwndSelf
, &screenRect
);
2047 ClientToScreen(pWnd
->hwndSelf
, &screenMousePos
);
2049 if(!PtInRect(&screenRect
, screenMousePos
))
2051 LISTBOX_SetCaretIndex( pWnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
);
2052 LISTBOX_SetSelection( pWnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2053 COMBO_FlipListbox( pDescr
->lphc
, FALSE
, FALSE
);
2058 /* Check to see the NC is a scrollbar */
2060 /* Check Vertical scroll bar */
2061 if (pWnd
->dwStyle
& WS_VSCROLL
)
2063 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2064 if (PtInRect( &clientRect
, mousePos
))
2066 nHitTestType
= HTVSCROLL
;
2069 /* Check horizontal scroll bar */
2070 if (pWnd
->dwStyle
& WS_HSCROLL
)
2072 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2073 if (PtInRect( &clientRect
, mousePos
))
2075 nHitTestType
= HTHSCROLL
;
2078 /* Windows sends this message when a scrollbar is clicked
2081 if(nHitTestType
!= 0)
2083 SendMessageW(pWnd
->hwndSelf
, WM_NCLBUTTONDOWN
, nHitTestType
,
2084 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2086 /* Resume the Capture after scrolling is complete
2088 if(hWndOldCapture
!= 0)
2090 SetCapture(hWndOldCapture
);
2097 /***********************************************************************
2098 * LISTBOX_HandleLButtonUp
2100 static LRESULT
LISTBOX_HandleLButtonUp( WND
*wnd
, LB_DESCR
*descr
)
2102 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2103 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2104 LISTBOX_Timer
= LB_TIMER_NONE
;
2105 if (descr
->captured
)
2107 descr
->captured
= FALSE
;
2108 if (GetCapture() == wnd
->hwndSelf
) ReleaseCapture();
2109 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2110 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2116 /***********************************************************************
2117 * LISTBOX_HandleTimer
2119 * Handle scrolling upon a timer event.
2120 * Return TRUE if scrolling should continue.
2122 static LRESULT
LISTBOX_HandleTimer( WND
*wnd
, LB_DESCR
*descr
,
2123 INT index
, TIMER_DIRECTION dir
)
2128 if (descr
->top_item
) index
= descr
->top_item
- 1;
2132 if (descr
->top_item
) index
-= descr
->page_size
;
2135 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2136 if (index
== descr
->focus_item
) index
++;
2137 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2139 case LB_TIMER_RIGHT
:
2140 if (index
+ descr
->page_size
< descr
->nb_items
)
2141 index
+= descr
->page_size
;
2146 if (index
== descr
->focus_item
) return FALSE
;
2147 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
2152 /***********************************************************************
2153 * LISTBOX_HandleSystemTimer
2155 * WM_SYSTIMER handler.
2157 static LRESULT
LISTBOX_HandleSystemTimer( WND
*wnd
, LB_DESCR
*descr
)
2159 if (!LISTBOX_HandleTimer( wnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
2161 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2162 LISTBOX_Timer
= LB_TIMER_NONE
;
2168 /***********************************************************************
2169 * LISTBOX_HandleMouseMove
2171 * WM_MOUSEMOVE handler.
2173 static void LISTBOX_HandleMouseMove( WND
*wnd
, LB_DESCR
*descr
,
2177 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2179 if (!descr
->captured
) return;
2181 if (descr
->style
& LBS_MULTICOLUMN
)
2184 else if (y
>= descr
->item_height
* descr
->page_size
)
2185 y
= descr
->item_height
* descr
->page_size
- 1;
2189 dir
= LB_TIMER_LEFT
;
2192 else if (x
>= descr
->width
)
2194 dir
= LB_TIMER_RIGHT
;
2195 x
= descr
->width
- 1;
2200 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2201 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2204 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2205 if (index
== -1) index
= descr
->focus_item
;
2206 if (!LISTBOX_HandleTimer( wnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2208 /* Start/stop the system timer */
2210 if (dir
!= LB_TIMER_NONE
)
2211 SetSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2212 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2213 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2214 LISTBOX_Timer
= dir
;
2218 /***********************************************************************
2219 * LISTBOX_HandleKeyDown
2221 static LRESULT
LISTBOX_HandleKeyDown( WND
*wnd
, LB_DESCR
*descr
, WPARAM wParam
)
2224 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2225 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2226 bForceSelection
= FALSE
; /* only for single select list */
2228 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2230 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2231 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2233 if (caret
== -2) return 0;
2235 if (caret
== -1) switch(wParam
)
2238 if (descr
->style
& LBS_MULTICOLUMN
)
2240 bForceSelection
= FALSE
;
2241 if (descr
->focus_item
>= descr
->page_size
)
2242 caret
= descr
->focus_item
- descr
->page_size
;
2247 caret
= descr
->focus_item
- 1;
2248 if (caret
< 0) caret
= 0;
2251 if (descr
->style
& LBS_MULTICOLUMN
)
2253 bForceSelection
= FALSE
;
2254 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2255 caret
= descr
->focus_item
+ descr
->page_size
;
2260 caret
= descr
->focus_item
+ 1;
2261 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2265 if (descr
->style
& LBS_MULTICOLUMN
)
2267 INT page
= descr
->width
/ descr
->column_width
;
2268 if (page
< 1) page
= 1;
2269 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2271 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2272 if (caret
< 0) caret
= 0;
2275 if (descr
->style
& LBS_MULTICOLUMN
)
2277 INT page
= descr
->width
/ descr
->column_width
;
2278 if (page
< 1) page
= 1;
2279 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2281 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2282 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2288 caret
= descr
->nb_items
- 1;
2291 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2292 else if (descr
->style
& LBS_MULTIPLESEL
)
2294 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
,
2295 !descr
->items
[descr
->focus_item
].selected
,
2296 (descr
->style
& LBS_NOTIFY
) != 0 );
2300 bForceSelection
= FALSE
;
2302 if (bForceSelection
) /* focused item is used instead of key */
2303 caret
= descr
->focus_item
;
2306 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
2307 !(GetKeyState( VK_SHIFT
) & 0x8000))
2308 descr
->anchor_item
= caret
;
2309 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2310 LISTBOX_SetSelection( wnd
, descr
, caret
, TRUE
, FALSE
);
2311 if (descr
->style
& LBS_NOTIFY
)
2315 /* make sure that combo parent doesn't hide us */
2316 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2318 if (descr
->nb_items
) SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2325 /***********************************************************************
2326 * LISTBOX_HandleChar
2328 static LRESULT
LISTBOX_HandleChar( WND
*wnd
, LB_DESCR
*descr
, WCHAR charW
)
2336 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2338 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2339 MAKEWPARAM(charW
, descr
->focus_item
),
2341 if (caret
== -2) return 0;
2344 caret
= LISTBOX_FindString( wnd
, descr
, descr
->focus_item
, str
, FALSE
);
2347 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2348 LISTBOX_SetSelection( wnd
, descr
, caret
, TRUE
, FALSE
);
2349 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2350 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2351 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2357 /***********************************************************************
2360 static BOOL
LISTBOX_Create( WND
*wnd
, LPHEADCOMBO lphc
)
2363 MEASUREITEMSTRUCT mis
;
2366 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2369 GetClientRect( wnd
->hwndSelf
, &rect
);
2370 descr
->owner
= GetParent( wnd
->hwndSelf
);
2371 descr
->style
= wnd
->dwStyle
;
2372 descr
->width
= rect
.right
- rect
.left
;
2373 descr
->height
= rect
.bottom
- rect
.top
;
2374 descr
->items
= NULL
;
2375 descr
->nb_items
= 0;
2376 descr
->top_item
= 0;
2377 descr
->selected_item
= -1;
2378 descr
->focus_item
= 0;
2379 descr
->anchor_item
= -1;
2380 descr
->item_height
= 1;
2381 descr
->page_size
= 1;
2382 descr
->column_width
= 150;
2383 descr
->horz_extent
= (wnd
->dwStyle
& WS_HSCROLL
) ? 1 : 0;
2384 descr
->horz_pos
= 0;
2387 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2388 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2389 descr
->in_focus
= FALSE
;
2390 descr
->captured
= FALSE
;
2392 descr
->locale
= 0; /* FIXME */
2395 if( ( GetExpWinVer16( wnd
->hInstance
) & 0xFF00 ) == 0x0300
2396 && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2398 /* Win95 document "List Box Differences" from MSDN:
2399 If a list box in a version 3.x application has either the
2400 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2401 horizontal and vertical scroll bars.
2403 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2408 TRACE_(combo
)("[%04x]: resetting owner %04x -> %04x\n",
2409 wnd
->hwndSelf
, descr
->owner
, lphc
->self
->hwndSelf
);
2410 descr
->owner
= lphc
->self
->hwndSelf
;
2413 *(LB_DESCR
**)wnd
->wExtra
= descr
;
2415 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2417 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2418 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2419 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2420 descr
->item_height
= LISTBOX_SetFont( wnd
, descr
, 0 );
2422 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2424 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2426 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2427 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2431 mis
.CtlType
= ODT_LISTBOX
;
2432 mis
.CtlID
= wnd
->wIDmenu
;
2436 mis
.itemHeight
= descr
->item_height
;
2437 SendMessageW( descr
->owner
, WM_MEASUREITEM
, wnd
->wIDmenu
, (LPARAM
)&mis
);
2438 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2442 TRACE("owner: %04x, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2447 /***********************************************************************
2450 static BOOL
LISTBOX_Destroy( WND
*wnd
, LB_DESCR
*descr
)
2452 LISTBOX_ResetContent( wnd
, descr
);
2453 HeapFree( GetProcessHeap(), 0, descr
);
2459 /***********************************************************************
2462 static LRESULT WINAPI
ListBoxWndProc_locked( WND
* wnd
, UINT msg
,
2463 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2467 HWND hwnd
= wnd
->hwndSelf
;
2470 if (!(descr
= *(LB_DESCR
**)wnd
->wExtra
))
2476 if (!LISTBOX_Create( wnd
, NULL
))
2478 TRACE("creating wnd=%04x descr=%p\n",
2479 hwnd
, *(LB_DESCR
**)wnd
->wExtra
);
2485 * When a listbox is not in a combobox and the look
2486 * is win95, the WS_BORDER style is replaced with
2487 * the WS_EX_CLIENTEDGE style.
2489 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
2490 (wnd
->dwStyle
& WS_BORDER
) )
2492 wnd
->dwExStyle
|= WS_EX_CLIENTEDGE
;
2493 wnd
->dwStyle
&= ~ WS_BORDER
;
2498 /* Ignore all other messages before we get a WM_CREATE */
2499 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2500 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2503 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2504 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2507 case LB_RESETCONTENT16
:
2508 case LB_RESETCONTENT
:
2509 LISTBOX_ResetContent( wnd
, descr
);
2510 LISTBOX_UpdateScroll( wnd
, descr
);
2511 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
2514 case LB_ADDSTRING16
:
2515 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2521 if(unicode
|| !HAS_STRINGS(descr
))
2522 textW
= (LPWSTR
)lParam
;
2525 LPSTR textA
= (LPSTR
)lParam
;
2526 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2527 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2528 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2530 wParam
= LISTBOX_FindStringPos( wnd
, descr
, textW
, FALSE
);
2531 ret
= LISTBOX_InsertString( wnd
, descr
, wParam
, textW
);
2532 if (!unicode
&& HAS_STRINGS(descr
))
2533 HeapFree(GetProcessHeap(), 0, textW
);
2537 case LB_INSERTSTRING16
:
2538 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2539 wParam
= (INT
)(INT16
)wParam
;
2541 case LB_INSERTSTRING
:
2545 if(unicode
|| !HAS_STRINGS(descr
))
2546 textW
= (LPWSTR
)lParam
;
2549 LPSTR textA
= (LPSTR
)lParam
;
2550 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2551 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2552 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2554 ret
= LISTBOX_InsertString( wnd
, descr
, wParam
, textW
);
2555 if(!unicode
&& HAS_STRINGS(descr
))
2556 HeapFree(GetProcessHeap(), 0, textW
);
2561 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2567 if(unicode
|| !HAS_STRINGS(descr
))
2568 textW
= (LPWSTR
)lParam
;
2571 LPSTR textA
= (LPSTR
)lParam
;
2572 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2573 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2574 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2576 wParam
= LISTBOX_FindFileStrPos( wnd
, descr
, textW
);
2577 ret
= LISTBOX_InsertString( wnd
, descr
, wParam
, textW
);
2578 if(!unicode
&& HAS_STRINGS(descr
))
2579 HeapFree(GetProcessHeap(), 0, textW
);
2583 case LB_DELETESTRING16
:
2584 case LB_DELETESTRING
:
2585 if (LISTBOX_RemoveItem( wnd
, descr
, wParam
) != LB_ERR
)
2586 return descr
->nb_items
;
2590 case LB_GETITEMDATA16
:
2591 case LB_GETITEMDATA
:
2592 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2594 return descr
->items
[wParam
].data
;
2596 case LB_SETITEMDATA16
:
2597 case LB_SETITEMDATA
:
2598 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2600 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2605 return descr
->nb_items
;
2608 lParam
= (LPARAM
)MapSL(lParam
);
2611 return LISTBOX_GetText( descr
, wParam
, lParam
, unicode
);
2613 case LB_GETTEXTLEN16
:
2616 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2618 return (HAS_STRINGS(descr
) ? strlenW(descr
->items
[wParam
].str
)
2621 case LB_GETCURSEL16
:
2623 if (descr
->nb_items
==0)
2625 if (!IS_MULTISELECT(descr
))
2626 return descr
->selected_item
;
2628 if (descr
->selected_item
!=-1)
2629 return descr
->selected_item
;
2631 return descr
->focus_item
;
2632 /* otherwise, if the user tries to move the selection with the */
2633 /* arrow keys, we will give the application something to choke on */
2634 case LB_GETTOPINDEX16
:
2635 case LB_GETTOPINDEX
:
2636 return descr
->top_item
;
2638 case LB_GETITEMHEIGHT16
:
2639 case LB_GETITEMHEIGHT
:
2640 return LISTBOX_GetItemHeight( descr
, wParam
);
2642 case LB_SETITEMHEIGHT16
:
2643 lParam
= LOWORD(lParam
);
2645 case LB_SETITEMHEIGHT
:
2646 return LISTBOX_SetItemHeight( wnd
, descr
, wParam
, lParam
);
2648 case LB_ITEMFROMPOINT
:
2653 pt
.x
= LOWORD(lParam
);
2654 pt
.y
= HIWORD(lParam
);
2657 rect
.right
= descr
->width
;
2658 rect
.bottom
= descr
->height
;
2660 return MAKELONG( LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
),
2661 !PtInRect( &rect
, pt
) );
2664 case LB_SETCARETINDEX16
:
2665 case LB_SETCARETINDEX
:
2666 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2667 if (LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, !lParam
) == LB_ERR
)
2674 case LB_GETCARETINDEX16
:
2675 case LB_GETCARETINDEX
:
2676 return descr
->focus_item
;
2678 case LB_SETTOPINDEX16
:
2679 case LB_SETTOPINDEX
:
2680 return LISTBOX_SetTopItem( wnd
, descr
, wParam
, TRUE
);
2682 case LB_SETCOLUMNWIDTH16
:
2683 case LB_SETCOLUMNWIDTH
:
2684 return LISTBOX_SetColumnWidth( wnd
, descr
, wParam
);
2686 case LB_GETITEMRECT16
:
2689 ret
= LISTBOX_GetItemRect( descr
, (INT16
)wParam
, &rect
);
2690 CONV_RECT32TO16( &rect
, MapSL(lParam
) );
2694 case LB_GETITEMRECT
:
2695 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2697 case LB_FINDSTRING16
:
2698 wParam
= (INT
)(INT16
)wParam
;
2699 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2705 if(unicode
|| !HAS_STRINGS(descr
))
2706 textW
= (LPWSTR
)lParam
;
2709 LPSTR textA
= (LPSTR
)lParam
;
2710 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2711 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2712 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2714 ret
= LISTBOX_FindString( wnd
, descr
, wParam
, textW
, FALSE
);
2715 if(!unicode
&& HAS_STRINGS(descr
))
2716 HeapFree(GetProcessHeap(), 0, textW
);
2720 case LB_FINDSTRINGEXACT16
:
2721 wParam
= (INT
)(INT16
)wParam
;
2722 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2724 case LB_FINDSTRINGEXACT
:
2728 if(unicode
|| !HAS_STRINGS(descr
))
2729 textW
= (LPWSTR
)lParam
;
2732 LPSTR textA
= (LPSTR
)lParam
;
2733 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2734 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2735 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2737 ret
= LISTBOX_FindString( wnd
, descr
, wParam
, textW
, TRUE
);
2738 if(!unicode
&& HAS_STRINGS(descr
))
2739 HeapFree(GetProcessHeap(), 0, textW
);
2743 case LB_SELECTSTRING16
:
2744 wParam
= (INT
)(INT16
)wParam
;
2745 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2747 case LB_SELECTSTRING
:
2752 if(HAS_STRINGS(descr
))
2753 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2754 debugstr_a((LPSTR
)lParam
));
2755 if(unicode
|| !HAS_STRINGS(descr
))
2756 textW
= (LPWSTR
)lParam
;
2759 LPSTR textA
= (LPSTR
)lParam
;
2760 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2761 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2762 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2764 index
= LISTBOX_FindString( wnd
, descr
, wParam
, textW
, FALSE
);
2765 if(!unicode
&& HAS_STRINGS(descr
))
2766 HeapFree(GetProcessHeap(), 0, textW
);
2767 if (index
!= LB_ERR
)
2769 LISTBOX_SetCaretIndex( wnd
, descr
, index
, TRUE
);
2770 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
2776 wParam
= (INT
)(INT16
)wParam
;
2779 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2781 return descr
->items
[wParam
].selected
;
2784 lParam
= (INT
)(INT16
)lParam
;
2787 return LISTBOX_SetSelection( wnd
, descr
, lParam
, wParam
, FALSE
);
2789 case LB_SETCURSEL16
:
2790 wParam
= (INT
)(INT16
)wParam
;
2793 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2794 LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, TRUE
);
2795 return LISTBOX_SetSelection( wnd
, descr
, wParam
, TRUE
, FALSE
);
2797 case LB_GETSELCOUNT16
:
2798 case LB_GETSELCOUNT
:
2799 return LISTBOX_GetSelCount( descr
);
2801 case LB_GETSELITEMS16
:
2802 return LISTBOX_GetSelItems16( descr
, wParam
, (LPINT16
)MapSL(lParam
) );
2804 case LB_GETSELITEMS
:
2805 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2807 case LB_SELITEMRANGE16
:
2808 case LB_SELITEMRANGE
:
2809 if (LOWORD(lParam
) <= HIWORD(lParam
))
2810 return LISTBOX_SelectItemRange( wnd
, descr
, LOWORD(lParam
),
2811 HIWORD(lParam
), wParam
);
2813 return LISTBOX_SelectItemRange( wnd
, descr
, HIWORD(lParam
),
2814 LOWORD(lParam
), wParam
);
2816 case LB_SELITEMRANGEEX16
:
2817 case LB_SELITEMRANGEEX
:
2818 if ((INT
)lParam
>= (INT
)wParam
)
2819 return LISTBOX_SelectItemRange( wnd
, descr
, wParam
, lParam
, TRUE
);
2821 return LISTBOX_SelectItemRange( wnd
, descr
, lParam
, wParam
, FALSE
);
2823 case LB_GETHORIZONTALEXTENT16
:
2824 case LB_GETHORIZONTALEXTENT
:
2825 return descr
->horz_extent
;
2827 case LB_SETHORIZONTALEXTENT16
:
2828 case LB_SETHORIZONTALEXTENT
:
2829 return LISTBOX_SetHorizontalExtent( wnd
, descr
, wParam
);
2831 case LB_GETANCHORINDEX16
:
2832 case LB_GETANCHORINDEX
:
2833 return descr
->anchor_item
;
2835 case LB_SETANCHORINDEX16
:
2836 wParam
= (INT
)(INT16
)wParam
;
2838 case LB_SETANCHORINDEX
:
2839 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2841 descr
->anchor_item
= (INT
)wParam
;
2845 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2846 * be set automatically (this is different in Win32) */
2847 if (wParam
& DDL_DRIVES
) wParam
|= DDL_EXCLUSIVE
;
2848 lParam
= (LPARAM
)MapSL(lParam
);
2855 textW
= (LPWSTR
)lParam
;
2858 LPSTR textA
= (LPSTR
)lParam
;
2859 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2860 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2861 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2863 ret
= LISTBOX_Directory( wnd
, descr
, wParam
, textW
, msg
== LB_DIR
);
2865 HeapFree(GetProcessHeap(), 0, textW
);
2870 return descr
->locale
;
2873 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2876 case LB_INITSTORAGE
:
2877 return LISTBOX_InitStorage( wnd
, descr
, wParam
);
2880 return LISTBOX_SetCount( wnd
, descr
, (INT
)wParam
);
2882 case LB_SETTABSTOPS16
:
2883 return LISTBOX_SetTabStops( wnd
, descr
, (INT
)(INT16
)wParam
, MapSL(lParam
), TRUE
);
2885 case LB_SETTABSTOPS
:
2886 return LISTBOX_SetTabStops( wnd
, descr
, wParam
, (LPINT
)lParam
, FALSE
);
2890 if (descr
->caret_on
)
2892 descr
->caret_on
= TRUE
;
2893 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2894 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2899 if (!descr
->caret_on
)
2901 descr
->caret_on
= FALSE
;
2902 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2903 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2907 return LISTBOX_Destroy( wnd
, descr
);
2910 InvalidateRect( hwnd
, NULL
, TRUE
);
2914 LISTBOX_SetRedraw( wnd
, descr
, wParam
!= 0 );
2918 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2923 HDC hdc
= ( wParam
) ? ((HDC
)wParam
)
2924 : BeginPaint( hwnd
, &ps
);
2925 ret
= LISTBOX_Paint( wnd
, descr
, hdc
);
2926 if( !wParam
) EndPaint( hwnd
, &ps
);
2930 LISTBOX_UpdateSize( wnd
, descr
);
2935 LISTBOX_SetFont( wnd
, descr
, (HFONT
)wParam
);
2936 if (lParam
) InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
2939 descr
->in_focus
= TRUE
;
2940 descr
->caret_on
= TRUE
;
2941 if (descr
->focus_item
!= -1)
2942 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2943 SEND_NOTIFICATION( wnd
, descr
, LBN_SETFOCUS
);
2946 descr
->in_focus
= FALSE
;
2947 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2948 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2949 SEND_NOTIFICATION( wnd
, descr
, LBN_KILLFOCUS
);
2952 return LISTBOX_HandleHScroll( wnd
, descr
, wParam
);
2954 return LISTBOX_HandleVScroll( wnd
, descr
, wParam
);
2955 case WM_MOUSEACTIVATE
:
2956 return MA_NOACTIVATE
;
2958 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2959 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2960 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2961 return LISTBOX_HandleMouseWheel( wnd
, descr
, wParam
);
2962 case WM_LBUTTONDOWN
:
2963 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2964 (INT16
)LOWORD(lParam
),
2965 (INT16
)HIWORD(lParam
) );
2966 case WM_LBUTTONDBLCLK
:
2967 if (descr
->style
& LBS_NOTIFY
)
2968 SEND_NOTIFICATION( wnd
, descr
, LBN_DBLCLK
);
2971 if (GetCapture() == hwnd
)
2972 LISTBOX_HandleMouseMove( wnd
, descr
, (INT16
)LOWORD(lParam
),
2973 (INT16
)HIWORD(lParam
) );
2976 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2978 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2983 charW
= (WCHAR
)wParam
;
2986 CHAR charA
= (CHAR
)wParam
;
2987 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
2989 return LISTBOX_HandleChar( wnd
, descr
, charW
);
2992 return LISTBOX_HandleSystemTimer( wnd
, descr
);
2994 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
2997 HBRUSH hbrush
= SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
2998 wParam
, (LPARAM
)wnd
->hwndSelf
);
2999 TRACE("hbrush = %04x\n", hbrush
);
3001 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3004 GetClientRect(hwnd
, &rect
);
3005 FillRect((HDC
)wParam
, &rect
, hbrush
);
3011 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3012 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3016 case WM_QUERYDROPOBJECT
:
3021 LPDRAGINFO16 dragInfo
= MapSL( lParam
);
3022 dragInfo
->l
= LISTBOX_GetItemFromPoint( descr
, dragInfo
->pt
.x
,
3024 return SendMessage16( descr
->owner
, msg
, wParam
, lParam
);
3029 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3030 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
3031 hwnd
, msg
, wParam
, lParam
);
3032 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3033 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3038 /***********************************************************************
3041 * This is just a wrapper for the real wndproc, it only does window locking
3044 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3046 WND
* wndPtr
= WIN_FindWndPtr( hwnd
);
3047 LRESULT res
= ListBoxWndProc_locked(wndPtr
, msg
, wParam
, lParam
, FALSE
);
3049 WIN_ReleaseWndPtr(wndPtr
);
3053 /***********************************************************************
3056 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3058 WND
* wndPtr
= WIN_FindWndPtr( hwnd
);
3059 LRESULT res
= ListBoxWndProc_locked(wndPtr
, msg
, wParam
, lParam
, TRUE
);
3061 WIN_ReleaseWndPtr(wndPtr
);
3065 /***********************************************************************
3066 * ComboLBWndProc_locked
3068 * The real combo listbox wndproc, but called with locked WND struct.
3070 static LRESULT WINAPI
ComboLBWndProc_locked( WND
* wnd
, UINT msg
,
3071 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
3078 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
3080 TRACE_(combo
)("[%04x]: msg %s wp %08x lp %08lx\n",
3081 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
3083 hwnd
= wnd
->hwndSelf
;
3085 if( descr
|| msg
== WM_CREATE
)
3087 LPHEADCOMBO lphc
= (descr
) ? descr
->lphc
: NULL
;
3092 #define lpcs ((LPCREATESTRUCTA)lParam)
3093 TRACE_(combo
)("\tpassed parent handle = 0x%08x\n",
3094 (UINT
)lpcs
->lpCreateParams
);
3096 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
3098 return LISTBOX_Create( wnd
, lphc
);
3100 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
3101 (CB_GETTYPE(lphc
) != CBS_SIMPLE
) )
3107 mousePos
.x
= (INT16
)LOWORD(lParam
);
3108 mousePos
.y
= (INT16
)HIWORD(lParam
);
3111 * If we are in a dropdown combobox, we simulate that
3112 * the mouse is captured to show the tracking of the item.
3114 GetClientRect(hwnd
, &clientRect
);
3116 if (PtInRect( &clientRect
, mousePos
))
3118 captured
= descr
->captured
;
3119 descr
->captured
= TRUE
;
3121 LISTBOX_HandleMouseMove( wnd
, descr
,
3122 mousePos
.x
, mousePos
.y
);
3124 descr
->captured
= captured
;
3129 LISTBOX_HandleMouseMove( wnd
, descr
,
3130 mousePos
.x
, mousePos
.y
);
3139 * If we are in Win3.1 look, go with the default behavior.
3141 return unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3142 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3145 if (TWEAK_WineLook
> WIN31_LOOK
)
3151 * If the mouse button "up" is not in the listbox,
3152 * we make sure there is no selection by re-selecting the
3153 * item that was selected when the listbox was made visible.
3155 mousePos
.x
= (INT16
)LOWORD(lParam
);
3156 mousePos
.y
= (INT16
)HIWORD(lParam
);
3158 GetClientRect(hwnd
, &clientRect
);
3161 * When the user clicks outside the combobox and the focus
3162 * is lost, the owning combobox will send a fake buttonup with
3163 * 0xFFFFFFF as the mouse location, we must also revert the
3164 * selection to the original selection.
3166 if ( (lParam
== (LPARAM
)-1) ||
3167 (!PtInRect( &clientRect
, mousePos
)) )
3169 LISTBOX_MoveCaret( wnd
,
3175 return LISTBOX_HandleLButtonUp( wnd
, descr
);
3176 case WM_LBUTTONDBLCLK
:
3177 case WM_LBUTTONDOWN
:
3178 return LISTBOX_HandleLButtonDownCombo(wnd
, descr
, msg
, wParam
,
3179 (INT16
)LOWORD(lParam
),
3180 (INT16
)HIWORD(lParam
) );
3181 case WM_MOUSEACTIVATE
:
3182 return MA_NOACTIVATE
;
3186 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3188 /* for some reason(?) Windows makes it possible to
3189 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3191 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3192 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3193 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3195 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3199 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
3201 case LB_SETCURSEL16
:
3203 lRet
= unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3204 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3205 lRet
=(lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
3208 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3213 return unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3214 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3217 lRet
= unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3218 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3220 TRACE_(combo
)("\t default on msg [%04x]\n", (UINT16
)msg
);
3225 /***********************************************************************
3228 * NOTE: in Windows, winproc address of the ComboLBox is the same
3229 * as that of the Listbox.
3231 * This is just a wrapper for the real wndproc, it only does window locking
3234 LRESULT WINAPI
ComboLBWndProcA( HWND hwnd
, UINT msg
,
3235 WPARAM wParam
, LPARAM lParam
)
3237 WND
*wnd
= WIN_FindWndPtr( hwnd
);
3238 LRESULT res
= ComboLBWndProc_locked(wnd
, msg
, wParam
, lParam
, FALSE
);
3240 WIN_ReleaseWndPtr(wnd
);
3244 /***********************************************************************
3247 LRESULT WINAPI
ComboLBWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3249 WND
*wnd
= WIN_FindWndPtr( hwnd
);
3250 LRESULT res
= ComboLBWndProc_locked(wnd
, msg
, wParam
, lParam
, TRUE
);
3252 WIN_ReleaseWndPtr(wnd
);