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
);
758 LPWSTR buffer
= (LPWSTR
)lParam
;
759 strcpyW( buffer
, descr
->items
[index
].str
);
760 return strlenW(buffer
);
764 LPSTR buffer
= (LPSTR
)lParam
;
765 return WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1, buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
769 *((LPDWORD
)lParam
)=*(LPDWORD
)(&descr
->items
[index
].data
);
770 return sizeof(DWORD
);
775 /***********************************************************************
776 * LISTBOX_FindStringPos
778 * Find the nearest string located before a given string in sort order.
779 * If 'exact' is TRUE, return an error if we don't get an exact match.
781 static INT
LISTBOX_FindStringPos( WND
*wnd
, LB_DESCR
*descr
, LPCWSTR str
,
784 INT index
, min
, max
, res
= -1;
786 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
788 max
= descr
->nb_items
;
791 index
= (min
+ max
) / 2;
792 if (HAS_STRINGS(descr
))
793 res
= lstrcmpiW( descr
->items
[index
].str
, str
);
796 COMPAREITEMSTRUCT cis
;
798 cis
.CtlType
= ODT_LISTBOX
;
799 cis
.CtlID
= wnd
->wIDmenu
;
800 cis
.hwndItem
= wnd
->hwndSelf
;
802 cis
.itemData1
= descr
->items
[index
].data
;
804 cis
.itemData2
= (DWORD
)str
;
805 cis
.dwLocaleId
= descr
->locale
;
806 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
,
807 wnd
->wIDmenu
, (LPARAM
)&cis
);
809 if (!res
) return index
;
810 if (res
> 0) max
= index
;
811 else min
= index
+ 1;
813 return exact
? -1 : max
;
817 /***********************************************************************
818 * LISTBOX_FindFileStrPos
820 * Find the nearest string located before a given string in directory
821 * sort order (i.e. first files, then directories, then drives).
823 static INT
LISTBOX_FindFileStrPos( WND
*wnd
, LB_DESCR
*descr
, LPCWSTR str
)
825 INT min
, max
, res
= -1;
827 if (!HAS_STRINGS(descr
))
828 return LISTBOX_FindStringPos( wnd
, descr
, str
, FALSE
);
830 max
= descr
->nb_items
;
833 INT index
= (min
+ max
) / 2;
834 LPCWSTR p
= descr
->items
[index
].str
;
835 if (*p
== '[') /* drive or directory */
837 if (*str
!= '[') res
= -1;
838 else if (p
[1] == '-') /* drive */
840 if (str
[1] == '-') res
= str
[2] - p
[2];
845 if (str
[1] == '-') res
= 1;
846 else res
= lstrcmpiW( str
, p
);
851 if (*str
== '[') res
= 1;
852 else res
= lstrcmpiW( str
, p
);
854 if (!res
) return index
;
855 if (res
< 0) max
= index
;
856 else min
= index
+ 1;
862 /***********************************************************************
865 * Find the item beginning with a given string.
867 static INT
LISTBOX_FindString( WND
*wnd
, LB_DESCR
*descr
, INT start
,
868 LPCWSTR str
, BOOL exact
)
873 if (start
>= descr
->nb_items
) start
= -1;
874 item
= descr
->items
+ start
+ 1;
875 if (HAS_STRINGS(descr
))
877 if (!str
|| ! str
[0] ) return LB_ERR
;
880 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
881 if (!lstrcmpiW( str
, item
->str
)) return i
;
882 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
883 if (!lstrcmpiW( str
, item
->str
)) return i
;
887 /* Special case for drives and directories: ignore prefix */
888 #define CHECK_DRIVE(item) \
889 if ((item)->str[0] == '[') \
891 if (!strncmpiW( str, (item)->str+1, len )) return i; \
892 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
896 INT len
= strlenW(str
);
897 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
899 if (!strncmpiW( str
, item
->str
, len
)) return i
;
902 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
904 if (!strncmpiW( str
, item
->str
, len
)) return i
;
912 if (exact
&& (descr
->style
& LBS_SORT
))
913 /* If sorted, use a WM_COMPAREITEM binary search */
914 return LISTBOX_FindStringPos( wnd
, descr
, str
, TRUE
);
916 /* Otherwise use a linear search */
917 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
918 if (item
->data
== (DWORD
)str
) return i
;
919 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
920 if (item
->data
== (DWORD
)str
) return i
;
926 /***********************************************************************
927 * LISTBOX_GetSelCount
929 static LRESULT
LISTBOX_GetSelCount( LB_DESCR
*descr
)
932 LB_ITEMDATA
*item
= descr
->items
;
934 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
935 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
936 if (item
->selected
) count
++;
941 /***********************************************************************
942 * LISTBOX_GetSelItems16
944 static LRESULT
LISTBOX_GetSelItems16( LB_DESCR
*descr
, INT16 max
, LPINT16 array
)
947 LB_ITEMDATA
*item
= descr
->items
;
949 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
950 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
951 if (item
->selected
) array
[count
++] = (INT16
)i
;
956 /***********************************************************************
957 * LISTBOX_GetSelItems
959 static LRESULT
LISTBOX_GetSelItems( LB_DESCR
*descr
, INT max
, LPINT array
)
962 LB_ITEMDATA
*item
= descr
->items
;
964 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
965 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
966 if (item
->selected
) array
[count
++] = i
;
971 /***********************************************************************
974 static LRESULT
LISTBOX_Paint( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
)
976 INT i
, col_pos
= descr
->page_size
- 1;
978 RECT focusRect
= {-1, -1, -1, -1};
980 HBRUSH hbrush
, oldBrush
= 0;
982 if (descr
->style
& LBS_NOREDRAW
) return 0;
984 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
985 if (descr
->style
& LBS_MULTICOLUMN
)
986 rect
.right
= rect
.left
+ descr
->column_width
;
987 else if (descr
->horz_pos
)
989 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
990 rect
.right
+= descr
->horz_pos
;
993 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
994 hbrush
= SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
995 hdc
, (LPARAM
)wnd
->hwndSelf
);
996 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
997 if (wnd
->dwStyle
& WS_DISABLED
)
998 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1000 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1003 /* Special case for empty listbox: paint focus rect */
1004 rect
.bottom
= rect
.top
+ descr
->item_height
;
1005 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, descr
->focus_item
,
1007 rect
.top
= rect
.bottom
;
1010 /* Paint all the item, regarding the selection
1011 Focus state will be painted after */
1013 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1015 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1016 rect
.bottom
= rect
.top
+ descr
->item_height
;
1018 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1020 if (i
== descr
->focus_item
)
1022 /* keep the focus rect, to paint the focus item after */
1023 focusRect
.left
= rect
.left
;
1024 focusRect
.right
= rect
.right
;
1025 focusRect
.top
= rect
.top
;
1026 focusRect
.bottom
= rect
.bottom
;
1028 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1029 rect
.top
= rect
.bottom
;
1031 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1033 if (!IS_OWNERDRAW(descr
))
1035 /* Clear the bottom of the column */
1036 if (rect
.top
< descr
->height
)
1038 rect
.bottom
= descr
->height
;
1039 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1040 &rect
, NULL
, 0, NULL
);
1044 /* Go to the next column */
1045 rect
.left
+= descr
->column_width
;
1046 rect
.right
+= descr
->column_width
;
1048 col_pos
= descr
->page_size
- 1;
1053 if (rect
.top
>= descr
->height
) break;
1057 /* Paint the focus item now */
1058 if (focusRect
.top
!= focusRect
.bottom
&& descr
->caret_on
)
1059 LISTBOX_PaintItem( wnd
, descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1061 if (!IS_OWNERDRAW(descr
))
1063 /* Clear the remainder of the client area */
1064 if (rect
.top
< descr
->height
)
1066 rect
.bottom
= descr
->height
;
1067 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1068 &rect
, NULL
, 0, NULL
);
1070 if (rect
.right
< descr
->width
)
1072 rect
.left
= rect
.right
;
1073 rect
.right
= descr
->width
;
1075 rect
.bottom
= descr
->height
;
1076 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1077 &rect
, NULL
, 0, NULL
);
1080 if (oldFont
) SelectObject( hdc
, oldFont
);
1081 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1086 /***********************************************************************
1087 * LISTBOX_InvalidateItems
1089 * Invalidate all items from a given item. If the specified item is not
1090 * visible, nothing happens.
1092 static void LISTBOX_InvalidateItems( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1096 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1098 if (descr
->style
& LBS_NOREDRAW
)
1100 descr
->style
|= LBS_DISPLAYCHANGED
;
1103 rect
.bottom
= descr
->height
;
1104 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
1105 if (descr
->style
& LBS_MULTICOLUMN
)
1107 /* Repaint the other columns */
1108 rect
.left
= rect
.right
;
1109 rect
.right
= descr
->width
;
1111 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
1117 /***********************************************************************
1118 * LISTBOX_GetItemHeight
1120 static LRESULT
LISTBOX_GetItemHeight( LB_DESCR
*descr
, INT index
)
1122 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1124 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1125 return descr
->items
[index
].height
;
1127 else return descr
->item_height
;
1131 /***********************************************************************
1132 * LISTBOX_SetItemHeight
1134 static LRESULT
LISTBOX_SetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1137 if (!height
) height
= 1;
1139 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1141 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1142 TRACE("[%04x]: item %d height = %d\n",
1143 wnd
->hwndSelf
, index
, height
);
1144 descr
->items
[index
].height
= height
;
1145 LISTBOX_UpdateScroll( wnd
, descr
);
1146 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1148 else if (height
!= descr
->item_height
)
1150 TRACE("[%04x]: new height = %d\n",
1151 wnd
->hwndSelf
, height
);
1152 descr
->item_height
= height
;
1153 LISTBOX_UpdatePage( wnd
, descr
);
1154 LISTBOX_UpdateScroll( wnd
, descr
);
1155 InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
1161 /***********************************************************************
1162 * LISTBOX_SetHorizontalPos
1164 static void LISTBOX_SetHorizontalPos( WND
*wnd
, LB_DESCR
*descr
, INT pos
)
1168 if (pos
> descr
->horz_extent
- descr
->width
)
1169 pos
= descr
->horz_extent
- descr
->width
;
1170 if (pos
< 0) pos
= 0;
1171 if (!(diff
= descr
->horz_pos
- pos
)) return;
1172 TRACE("[%04x]: new horz pos = %d\n",
1173 wnd
->hwndSelf
, pos
);
1174 descr
->horz_pos
= pos
;
1175 LISTBOX_UpdateScroll( wnd
, descr
);
1176 if (abs(diff
) < descr
->width
)
1177 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
1178 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1180 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1184 /***********************************************************************
1185 * LISTBOX_SetHorizontalExtent
1187 static LRESULT
LISTBOX_SetHorizontalExtent( WND
*wnd
, LB_DESCR
*descr
,
1190 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1192 if (extent
<= 0) extent
= 1;
1193 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1194 TRACE("[%04x]: new horz extent = %d\n",
1195 wnd
->hwndSelf
, extent
);
1196 descr
->horz_extent
= extent
;
1197 if (descr
->horz_pos
> extent
- descr
->width
)
1198 LISTBOX_SetHorizontalPos( wnd
, descr
, extent
- descr
->width
);
1200 LISTBOX_UpdateScroll( wnd
, descr
);
1205 /***********************************************************************
1206 * LISTBOX_SetColumnWidth
1208 static LRESULT
LISTBOX_SetColumnWidth( WND
*wnd
, LB_DESCR
*descr
, INT width
)
1210 if (width
== descr
->column_width
) return LB_OKAY
;
1211 TRACE("[%04x]: new column width = %d\n",
1212 wnd
->hwndSelf
, width
);
1213 descr
->column_width
= width
;
1214 LISTBOX_UpdatePage( wnd
, descr
);
1219 /***********************************************************************
1222 * Returns the item height.
1224 static INT
LISTBOX_SetFont( WND
*wnd
, LB_DESCR
*descr
, HFONT font
)
1232 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
)))
1234 ERR("unable to get DC.\n" );
1237 if (font
) oldFont
= SelectObject( hdc
, font
);
1238 GetTextMetricsW( hdc
, &tm
);
1239 if (oldFont
) SelectObject( hdc
, oldFont
);
1240 ReleaseDC( wnd
->hwndSelf
, hdc
);
1241 if (!IS_OWNERDRAW(descr
))
1242 LISTBOX_SetItemHeight( wnd
, descr
, 0, tm
.tmHeight
);
1243 return tm
.tmHeight
;
1247 /***********************************************************************
1248 * LISTBOX_MakeItemVisible
1250 * Make sure that a given item is partially or fully visible.
1252 static void LISTBOX_MakeItemVisible( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1257 if (index
<= descr
->top_item
) top
= index
;
1258 else if (descr
->style
& LBS_MULTICOLUMN
)
1260 INT cols
= descr
->width
;
1261 if (!fully
) cols
+= descr
->column_width
- 1;
1262 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1264 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1265 top
= index
- descr
->page_size
* (cols
- 1);
1267 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1269 INT height
= fully
? descr
->items
[index
].height
: 1;
1270 for (top
= index
; top
> descr
->top_item
; top
--)
1271 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1275 if (index
< descr
->top_item
+ descr
->page_size
) return;
1276 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1277 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1278 top
= index
- descr
->page_size
+ 1;
1280 LISTBOX_SetTopItem( wnd
, descr
, top
, TRUE
);
1283 /***********************************************************************
1284 * LISTBOX_SetCaretIndex
1287 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1290 static LRESULT
LISTBOX_SetCaretIndex( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1291 BOOL fully_visible
)
1293 INT oldfocus
= descr
->focus_item
;
1295 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1296 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1297 if (index
== oldfocus
) return LB_OKAY
;
1298 descr
->focus_item
= index
;
1299 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1300 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1302 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1303 if (descr
->caret_on
&& (descr
->in_focus
))
1304 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1310 /***********************************************************************
1311 * LISTBOX_SelectItemRange
1313 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1315 static LRESULT
LISTBOX_SelectItemRange( WND
*wnd
, LB_DESCR
*descr
, INT first
,
1320 /* A few sanity checks */
1322 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1323 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1324 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1325 if (last
== -1) last
= descr
->nb_items
- 1;
1326 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1327 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1328 /* selected_item reflects last selected/unselected item on multiple sel */
1329 descr
->selected_item
= last
;
1331 if (on
) /* Turn selection on */
1333 for (i
= first
; i
<= last
; i
++)
1335 if (descr
->items
[i
].selected
) continue;
1336 descr
->items
[i
].selected
= TRUE
;
1337 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1339 LISTBOX_SetCaretIndex( wnd
, descr
, last
, TRUE
);
1341 else /* Turn selection off */
1343 for (i
= first
; i
<= last
; i
++)
1345 if (!descr
->items
[i
].selected
) continue;
1346 descr
->items
[i
].selected
= FALSE
;
1347 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1353 /***********************************************************************
1354 * LISTBOX_SetSelection
1356 static LRESULT
LISTBOX_SetSelection( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1357 BOOL on
, BOOL send_notify
)
1359 TRACE( "index=%d notify=%s\n", index
, send_notify
? "YES" : "NO" );
1361 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1362 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1363 if (descr
->style
& LBS_MULTIPLESEL
)
1365 if (index
== -1) /* Select all items */
1366 return LISTBOX_SelectItemRange( wnd
, descr
, 0, -1, on
);
1367 else /* Only one item */
1368 return LISTBOX_SelectItemRange( wnd
, descr
, index
, index
, on
);
1372 INT oldsel
= descr
->selected_item
;
1373 if (index
== oldsel
) return LB_OKAY
;
1374 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1375 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1376 descr
->selected_item
= index
;
1377 if (oldsel
!= -1) LISTBOX_RepaintItem( wnd
, descr
, oldsel
, ODA_SELECT
);
1378 if (index
!= -1) LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_SELECT
);
1379 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( wnd
, descr
,
1380 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1382 if( descr
->lphc
) /* set selection change flag for parent combo */
1383 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1389 /***********************************************************************
1392 * Change the caret position and extend the selection to the new caret.
1394 static void LISTBOX_MoveCaret( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1395 BOOL fully_visible
)
1397 INT oldfocus
= descr
->focus_item
;
1399 if ((index
< 0) || (index
>= descr
->nb_items
))
1402 /* Important, repaint needs to be done in this order if
1403 you want to mimic Windows behavior:
1404 1. Remove the focus and paint the item
1405 2. Remove the selection and paint the item(s)
1406 3. Set the selection and repaint the item(s)
1407 4. Set the focus to 'index' and repaint the item */
1409 /* 1. remove the focus and repaint the item */
1410 descr
->focus_item
= -1;
1411 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1412 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1414 /* 2. then turn off the previous selection */
1415 /* 3. repaint the new selected item */
1416 if (descr
->style
& LBS_EXTENDEDSEL
)
1418 if (descr
->anchor_item
!= -1)
1420 INT first
= min( index
, descr
->anchor_item
);
1421 INT last
= max( index
, descr
->anchor_item
);
1423 LISTBOX_SelectItemRange( wnd
, descr
, 0, first
- 1, FALSE
);
1424 LISTBOX_SelectItemRange( wnd
, descr
, last
+ 1, -1, FALSE
);
1425 LISTBOX_SelectItemRange( wnd
, descr
, first
, last
, TRUE
);
1428 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1430 /* Set selection to new caret item */
1431 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
1434 /* 4. repaint the new item with the focus */
1435 descr
->focus_item
= index
;
1436 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1437 if (descr
->caret_on
&& (descr
->in_focus
))
1438 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1442 /***********************************************************************
1443 * LISTBOX_InsertItem
1445 static LRESULT
LISTBOX_InsertItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1446 LPWSTR str
, DWORD data
)
1450 INT oldfocus
= descr
->focus_item
;
1452 if (index
== -1) index
= descr
->nb_items
;
1453 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1454 if (!descr
->items
) max_items
= 0;
1455 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1456 if (descr
->nb_items
== max_items
)
1458 /* We need to grow the array */
1459 max_items
+= LB_ARRAY_GRANULARITY
;
1460 if (!(item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1461 max_items
* sizeof(LB_ITEMDATA
) )))
1463 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1466 descr
->items
= item
;
1469 /* Insert the item structure */
1471 item
= &descr
->items
[index
];
1472 if (index
< descr
->nb_items
)
1473 RtlMoveMemory( item
+ 1, item
,
1474 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1478 item
->selected
= FALSE
;
1481 /* Get item height */
1483 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1485 MEASUREITEMSTRUCT mis
;
1487 mis
.CtlType
= ODT_LISTBOX
;
1488 mis
.CtlID
= wnd
->wIDmenu
;
1490 mis
.itemData
= descr
->items
[index
].data
;
1491 mis
.itemHeight
= descr
->item_height
;
1492 SendMessageW( descr
->owner
, WM_MEASUREITEM
, wnd
->wIDmenu
, (LPARAM
)&mis
);
1493 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1494 TRACE("[%04x]: measure item %d (%s) = %d\n",
1495 wnd
->hwndSelf
, index
, str
? debugstr_w(str
) : "", item
->height
);
1498 /* Repaint the items */
1500 LISTBOX_UpdateScroll( wnd
, descr
);
1501 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1503 /* Move selection and focused item */
1504 /* If listbox was empty, set focus to the first item */
1505 if (descr
->nb_items
== 1)
1506 LISTBOX_SetCaretIndex( wnd
, descr
, 0, FALSE
);
1507 /* single select don't change selection index in win31 */
1508 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1510 descr
->selected_item
++;
1511 LISTBOX_SetSelection( wnd
, descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1515 if (index
<= descr
->selected_item
)
1517 descr
->selected_item
++;
1518 descr
->focus_item
= oldfocus
; /* focus not changed */
1525 /***********************************************************************
1526 * LISTBOX_InsertString
1528 static LRESULT
LISTBOX_InsertString( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1531 LPWSTR new_str
= NULL
;
1535 if (HAS_STRINGS(descr
))
1537 static const WCHAR empty_stringW
[] = { 0 };
1538 if (!str
) str
= empty_stringW
;
1539 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1541 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1544 strcpyW(new_str
, str
);
1546 else data
= (DWORD
)str
;
1548 if (index
== -1) index
= descr
->nb_items
;
1549 if ((ret
= LISTBOX_InsertItem( wnd
, descr
, index
, new_str
, data
)) != 0)
1551 if (new_str
) HeapFree( GetProcessHeap(), 0, new_str
);
1555 TRACE("[%04x]: added item %d '%s'\n",
1556 wnd
->hwndSelf
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1561 /***********************************************************************
1562 * LISTBOX_DeleteItem
1564 * Delete the content of an item. 'index' must be a valid index.
1566 static void LISTBOX_DeleteItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1568 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1569 * while Win95 sends it for all items with user data.
1570 * It's probably better to send it too often than not
1571 * often enough, so this is what we do here.
1573 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1575 DELETEITEMSTRUCT dis
;
1577 dis
.CtlType
= ODT_LISTBOX
;
1578 dis
.CtlID
= wnd
->wIDmenu
;
1580 dis
.hwndItem
= wnd
->hwndSelf
;
1581 dis
.itemData
= descr
->items
[index
].data
;
1582 SendMessageW( descr
->owner
, WM_DELETEITEM
, wnd
->wIDmenu
, (LPARAM
)&dis
);
1584 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1585 HeapFree( GetProcessHeap(), 0, descr
->items
[index
].str
);
1589 /***********************************************************************
1590 * LISTBOX_RemoveItem
1592 * Remove an item from the listbox and delete its content.
1594 static LRESULT
LISTBOX_RemoveItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1599 if ((index
== -1) && (descr
->nb_items
> 0)) index
= descr
->nb_items
- 1;
1600 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1602 /* We need to invalidate the original rect instead of the updated one. */
1603 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1605 LISTBOX_DeleteItem( wnd
, descr
, index
);
1607 /* Remove the item */
1609 item
= &descr
->items
[index
];
1610 if (index
< descr
->nb_items
-1)
1611 RtlMoveMemory( item
, item
+ 1,
1612 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1614 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1616 /* Shrink the item array if possible */
1618 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1619 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1621 max_items
-= LB_ARRAY_GRANULARITY
;
1622 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1623 max_items
* sizeof(LB_ITEMDATA
) );
1624 if (item
) descr
->items
= item
;
1626 /* Repaint the items */
1628 LISTBOX_UpdateScroll( wnd
, descr
);
1629 /* if we removed the scrollbar, reset the top of the list
1630 (correct for owner-drawn ???) */
1631 if (descr
->nb_items
== descr
->page_size
)
1632 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1634 /* Move selection and focused item */
1635 if (!IS_MULTISELECT(descr
))
1637 if (index
== descr
->selected_item
)
1638 descr
->selected_item
= -1;
1639 else if (index
< descr
->selected_item
)
1641 descr
->selected_item
--;
1642 if (ISWIN31
) /* win 31 do not change the selected item number */
1643 LISTBOX_SetSelection( wnd
, descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1647 if (descr
->focus_item
>= descr
->nb_items
)
1649 descr
->focus_item
= descr
->nb_items
- 1;
1650 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1656 /***********************************************************************
1657 * LISTBOX_ResetContent
1659 static void LISTBOX_ResetContent( WND
*wnd
, LB_DESCR
*descr
)
1663 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( wnd
, descr
, i
);
1664 if (descr
->items
) HeapFree( GetProcessHeap(), 0, descr
->items
);
1665 descr
->nb_items
= 0;
1666 descr
->top_item
= 0;
1667 descr
->selected_item
= -1;
1668 descr
->focus_item
= 0;
1669 descr
->anchor_item
= -1;
1670 descr
->items
= NULL
;
1674 /***********************************************************************
1677 static LRESULT
LISTBOX_SetCount( WND
*wnd
, LB_DESCR
*descr
, INT count
)
1681 if (HAS_STRINGS(descr
)) return LB_ERR
;
1682 /* FIXME: this is far from optimal... */
1683 if (count
> descr
->nb_items
)
1685 while (count
> descr
->nb_items
)
1686 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, 0 )) < 0)
1689 else if (count
< descr
->nb_items
)
1691 while (count
< descr
->nb_items
)
1692 if ((ret
= LISTBOX_RemoveItem( wnd
, descr
, -1 )) < 0)
1699 /***********************************************************************
1702 static LRESULT
LISTBOX_Directory( WND
*wnd
, LB_DESCR
*descr
, UINT attrib
,
1703 LPCWSTR filespec
, BOOL long_names
)
1706 LRESULT ret
= LB_OKAY
;
1707 WIN32_FIND_DATAW entry
;
1710 /* don't scan directory if we just want drives exclusively */
1711 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1712 /* scan directory */
1713 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1715 if (GetLastError() != ERROR_NO_MORE_FILES
) return LB_ERR
;
1722 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1724 static const WCHAR bracketW
[] = { ']',0 };
1725 static const WCHAR dotW
[] = { '.',0 };
1726 if (!(attrib
& DDL_DIRECTORY
) ||
1727 !strcmpW( entry
.cAlternateFileName
, dotW
)) continue;
1729 if (long_names
) strcpyW( buffer
+ 1, entry
.cFileName
);
1730 else strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1731 strcatW(buffer
, bracketW
);
1733 else /* not a directory */
1735 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1736 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1738 if ((attrib
& DDL_EXCLUSIVE
) &&
1739 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1742 if (long_names
) strcpyW( buffer
, entry
.cFileName
);
1743 else strcpyW( buffer
, entry
.cAlternateFileName
);
1745 if (!long_names
) CharLowerW( buffer
);
1746 pos
= LISTBOX_FindFileStrPos( wnd
, descr
, buffer
);
1747 if ((ret
= LISTBOX_InsertString( wnd
, descr
, pos
, buffer
)) < 0)
1749 } while (FindNextFileW( handle
, &entry
));
1750 FindClose( handle
);
1755 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1757 WCHAR buffer
[] = {'[','-','a','-',']',0};
1758 WCHAR root
[] = {'A',':','\\',0};
1760 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1762 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1763 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, buffer
)) < 0)
1771 /***********************************************************************
1772 * LISTBOX_HandleVScroll
1774 static LRESULT
LISTBOX_HandleVScroll( WND
*wnd
, LB_DESCR
*descr
, WPARAM wParam
)
1778 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1779 switch(LOWORD(wParam
))
1782 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
- 1, TRUE
);
1785 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ 1, TRUE
);
1788 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-
1789 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1792 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+
1793 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1795 case SB_THUMBPOSITION
:
1796 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
), TRUE
);
1799 info
.cbSize
= sizeof(info
);
1800 info
.fMask
= SIF_TRACKPOS
;
1801 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1802 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
, TRUE
);
1805 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1808 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1815 /***********************************************************************
1816 * LISTBOX_HandleHScroll
1818 static LRESULT
LISTBOX_HandleHScroll( WND
*wnd
, LB_DESCR
*descr
, WPARAM wParam
)
1823 if (descr
->style
& LBS_MULTICOLUMN
)
1825 switch(LOWORD(wParam
))
1828 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-descr
->page_size
,
1832 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+descr
->page_size
,
1836 page
= descr
->width
/ descr
->column_width
;
1837 if (page
< 1) page
= 1;
1838 LISTBOX_SetTopItem( wnd
, descr
,
1839 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1842 page
= descr
->width
/ descr
->column_width
;
1843 if (page
< 1) page
= 1;
1844 LISTBOX_SetTopItem( wnd
, descr
,
1845 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1847 case SB_THUMBPOSITION
:
1848 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1852 info
.cbSize
= sizeof(info
);
1853 info
.fMask
= SIF_TRACKPOS
;
1854 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1855 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1859 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1862 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1866 else if (descr
->horz_extent
)
1868 switch(LOWORD(wParam
))
1871 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
- 1 );
1874 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
+ 1 );
1877 LISTBOX_SetHorizontalPos( wnd
, descr
,
1878 descr
->horz_pos
- descr
->width
);
1881 LISTBOX_SetHorizontalPos( wnd
, descr
,
1882 descr
->horz_pos
+ descr
->width
);
1884 case SB_THUMBPOSITION
:
1885 LISTBOX_SetHorizontalPos( wnd
, descr
, HIWORD(wParam
) );
1888 info
.cbSize
= sizeof(info
);
1889 info
.fMask
= SIF_TRACKPOS
;
1890 GetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
);
1891 LISTBOX_SetHorizontalPos( wnd
, descr
, info
.nTrackPos
);
1894 LISTBOX_SetHorizontalPos( wnd
, descr
, 0 );
1897 LISTBOX_SetHorizontalPos( wnd
, descr
,
1898 descr
->horz_extent
- descr
->width
);
1905 static LRESULT
LISTBOX_HandleMouseWheel(WND
*wnd
, LB_DESCR
*descr
, WPARAM wParam
)
1907 short gcWheelDelta
= 0;
1908 UINT pulScrollLines
= 3;
1910 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1912 gcWheelDelta
-= (short) HIWORD(wParam
);
1914 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
1916 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
1917 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
1918 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ cLineScroll
, TRUE
);
1923 /***********************************************************************
1924 * LISTBOX_HandleLButtonDown
1926 static LRESULT
LISTBOX_HandleLButtonDown( WND
*wnd
, LB_DESCR
*descr
,
1927 WPARAM wParam
, INT x
, INT y
)
1929 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
1930 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1931 wnd
->hwndSelf
, x
, y
, index
);
1932 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
1934 if (!descr
->in_focus
)
1936 if( !descr
->lphc
) SetFocus( wnd
->hwndSelf
);
1937 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
1938 : descr
->lphc
->self
->hwndSelf
);
1941 if (index
== -1) return 0;
1943 if (descr
->style
& LBS_EXTENDEDSEL
)
1945 /* we should perhaps make sure that all items are deselected
1946 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1947 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1948 LISTBOX_SetSelection( wnd, descr, -1, FALSE, FALSE);
1951 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1952 if (wParam
& MK_CONTROL
)
1954 LISTBOX_SetCaretIndex( wnd
, descr
, index
, FALSE
);
1955 LISTBOX_SetSelection( wnd
, descr
, index
,
1956 !descr
->items
[index
].selected
,
1957 (descr
->style
& LBS_NOTIFY
) != 0);
1959 else LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1963 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1964 LISTBOX_SetSelection( wnd
, descr
, index
,
1965 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1966 !descr
->items
[index
].selected
),
1967 (descr
->style
& LBS_NOTIFY
) != 0 );
1970 descr
->captured
= TRUE
;
1971 SetCapture( wnd
->hwndSelf
);
1975 if (descr
->style
& LBS_NOTIFY
)
1976 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
1977 MAKELPARAM( x
, y
) );
1978 if (wnd
->dwExStyle
& WS_EX_DRAGDETECT
)
1985 if (DragDetect( wnd
->hwndSelf
, pt
))
1986 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
1993 /*************************************************************************
1994 * LISTBOX_HandleLButtonDownCombo [Internal]
1996 * Process LButtonDown message for the ComboListBox
1999 * pWnd [I] The windows internal structure
2000 * pDescr [I] The ListBox internal structure
2001 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2002 * x [I] X Mouse Coordinate
2003 * y [I] Y Mouse Coordinate
2006 * 0 since we are processing the WM_LBUTTONDOWN Message
2009 * This function is only to be used when a ListBox is a ComboListBox
2012 static LRESULT
LISTBOX_HandleLButtonDownCombo( WND
*pWnd
, LB_DESCR
*pDescr
,
2013 UINT msg
, WPARAM wParam
, INT x
, INT y
)
2015 RECT clientRect
, screenRect
;
2021 GetClientRect(pWnd
->hwndSelf
, &clientRect
);
2023 if(PtInRect(&clientRect
, mousePos
))
2025 /* MousePos is in client, resume normal processing */
2026 if (msg
== WM_LBUTTONDOWN
)
2028 pDescr
->lphc
->droppedIndex
= pDescr
->nb_items
? pDescr
->selected_item
: -1;
2029 return LISTBOX_HandleLButtonDown( pWnd
, pDescr
, wParam
, x
, y
);
2031 else if (pDescr
->style
& LBS_NOTIFY
)
2032 SEND_NOTIFICATION( pWnd
, pDescr
, LBN_DBLCLK
);
2037 POINT screenMousePos
;
2038 HWND hWndOldCapture
;
2040 /* Check the Non-Client Area */
2041 screenMousePos
= mousePos
;
2042 hWndOldCapture
= GetCapture();
2044 GetWindowRect(pWnd
->hwndSelf
, &screenRect
);
2045 ClientToScreen(pWnd
->hwndSelf
, &screenMousePos
);
2047 if(!PtInRect(&screenRect
, screenMousePos
))
2049 LISTBOX_SetSelection( pWnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2050 COMBO_FlipListbox( pDescr
->lphc
, FALSE
, FALSE
);
2055 /* Check to see the NC is a scrollbar */
2057 /* Check Vertical scroll bar */
2058 if (pWnd
->dwStyle
& WS_VSCROLL
)
2060 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2061 if (PtInRect( &clientRect
, mousePos
))
2063 nHitTestType
= HTVSCROLL
;
2066 /* Check horizontal scroll bar */
2067 if (pWnd
->dwStyle
& WS_HSCROLL
)
2069 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2070 if (PtInRect( &clientRect
, mousePos
))
2072 nHitTestType
= HTHSCROLL
;
2075 /* Windows sends this message when a scrollbar is clicked
2078 if(nHitTestType
!= 0)
2080 SendMessageW(pWnd
->hwndSelf
, WM_NCLBUTTONDOWN
, nHitTestType
,
2081 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2083 /* Resume the Capture after scrolling is complete
2085 if(hWndOldCapture
!= 0)
2087 SetCapture(hWndOldCapture
);
2094 /***********************************************************************
2095 * LISTBOX_HandleLButtonUp
2097 static LRESULT
LISTBOX_HandleLButtonUp( WND
*wnd
, LB_DESCR
*descr
)
2099 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2100 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2101 LISTBOX_Timer
= LB_TIMER_NONE
;
2102 if (descr
->captured
)
2104 descr
->captured
= FALSE
;
2105 if (GetCapture() == wnd
->hwndSelf
) ReleaseCapture();
2106 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2107 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2113 /***********************************************************************
2114 * LISTBOX_HandleTimer
2116 * Handle scrolling upon a timer event.
2117 * Return TRUE if scrolling should continue.
2119 static LRESULT
LISTBOX_HandleTimer( WND
*wnd
, LB_DESCR
*descr
,
2120 INT index
, TIMER_DIRECTION dir
)
2125 if (descr
->top_item
) index
= descr
->top_item
- 1;
2129 if (descr
->top_item
) index
-= descr
->page_size
;
2132 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2133 if (index
== descr
->focus_item
) index
++;
2134 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2136 case LB_TIMER_RIGHT
:
2137 if (index
+ descr
->page_size
< descr
->nb_items
)
2138 index
+= descr
->page_size
;
2143 if (index
== descr
->focus_item
) return FALSE
;
2144 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
2149 /***********************************************************************
2150 * LISTBOX_HandleSystemTimer
2152 * WM_SYSTIMER handler.
2154 static LRESULT
LISTBOX_HandleSystemTimer( WND
*wnd
, LB_DESCR
*descr
)
2156 if (!LISTBOX_HandleTimer( wnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
2158 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2159 LISTBOX_Timer
= LB_TIMER_NONE
;
2165 /***********************************************************************
2166 * LISTBOX_HandleMouseMove
2168 * WM_MOUSEMOVE handler.
2170 static void LISTBOX_HandleMouseMove( WND
*wnd
, LB_DESCR
*descr
,
2174 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2176 if (!descr
->captured
) return;
2178 if (descr
->style
& LBS_MULTICOLUMN
)
2181 else if (y
>= descr
->item_height
* descr
->page_size
)
2182 y
= descr
->item_height
* descr
->page_size
- 1;
2186 dir
= LB_TIMER_LEFT
;
2189 else if (x
>= descr
->width
)
2191 dir
= LB_TIMER_RIGHT
;
2192 x
= descr
->width
- 1;
2197 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2198 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2201 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2202 if (index
== -1) index
= descr
->focus_item
;
2203 if (!LISTBOX_HandleTimer( wnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2205 /* Start/stop the system timer */
2207 if (dir
!= LB_TIMER_NONE
)
2208 SetSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2209 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2210 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2211 LISTBOX_Timer
= dir
;
2215 /***********************************************************************
2216 * LISTBOX_HandleKeyDown
2218 static LRESULT
LISTBOX_HandleKeyDown( WND
*wnd
, LB_DESCR
*descr
, WPARAM wParam
)
2221 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2222 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2223 bForceSelection
= FALSE
; /* only for single select list */
2225 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2227 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2228 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2230 if (caret
== -2) return 0;
2232 if (caret
== -1) switch(wParam
)
2235 if (descr
->style
& LBS_MULTICOLUMN
)
2237 bForceSelection
= FALSE
;
2238 if (descr
->focus_item
>= descr
->page_size
)
2239 caret
= descr
->focus_item
- descr
->page_size
;
2244 caret
= descr
->focus_item
- 1;
2245 if (caret
< 0) caret
= 0;
2248 if (descr
->style
& LBS_MULTICOLUMN
)
2250 bForceSelection
= FALSE
;
2251 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2252 caret
= descr
->focus_item
+ descr
->page_size
;
2257 caret
= descr
->focus_item
+ 1;
2258 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2262 if (descr
->style
& LBS_MULTICOLUMN
)
2264 INT page
= descr
->width
/ descr
->column_width
;
2265 if (page
< 1) page
= 1;
2266 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2268 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2269 if (caret
< 0) caret
= 0;
2272 if (descr
->style
& LBS_MULTICOLUMN
)
2274 INT page
= descr
->width
/ descr
->column_width
;
2275 if (page
< 1) page
= 1;
2276 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2278 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2279 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2285 caret
= descr
->nb_items
- 1;
2288 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2289 else if (descr
->style
& LBS_MULTIPLESEL
)
2291 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
,
2292 !descr
->items
[descr
->focus_item
].selected
,
2293 (descr
->style
& LBS_NOTIFY
) != 0 );
2297 bForceSelection
= FALSE
;
2299 if (bForceSelection
) /* focused item is used instead of key */
2300 caret
= descr
->focus_item
;
2303 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
2304 !(GetKeyState( VK_SHIFT
) & 0x8000))
2305 descr
->anchor_item
= caret
;
2306 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2307 LISTBOX_SetSelection( wnd
, descr
, caret
, TRUE
, FALSE
);
2308 if (descr
->style
& LBS_NOTIFY
)
2310 if( descr
->lphc
&& CB_GETTYPE(descr
->lphc
) != CBS_SIMPLE
)
2312 /* make sure that combo parent doesn't hide us */
2313 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2315 if (descr
->nb_items
) SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2322 /***********************************************************************
2323 * LISTBOX_HandleChar
2325 static LRESULT
LISTBOX_HandleChar( WND
*wnd
, LB_DESCR
*descr
, WCHAR charW
)
2333 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2335 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2336 MAKEWPARAM(charW
, descr
->focus_item
),
2338 if (caret
== -2) return 0;
2341 caret
= LISTBOX_FindString( wnd
, descr
, descr
->focus_item
, str
, FALSE
);
2344 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2345 LISTBOX_SetSelection( wnd
, descr
, caret
, TRUE
, FALSE
);
2346 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2347 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2348 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2354 /***********************************************************************
2357 static BOOL
LISTBOX_Create( WND
*wnd
, LPHEADCOMBO lphc
)
2360 MEASUREITEMSTRUCT mis
;
2363 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2366 GetClientRect( wnd
->hwndSelf
, &rect
);
2367 descr
->owner
= GetParent( wnd
->hwndSelf
);
2368 descr
->style
= wnd
->dwStyle
;
2369 descr
->width
= rect
.right
- rect
.left
;
2370 descr
->height
= rect
.bottom
- rect
.top
;
2371 descr
->items
= NULL
;
2372 descr
->nb_items
= 0;
2373 descr
->top_item
= 0;
2374 descr
->selected_item
= -1;
2375 descr
->focus_item
= 0;
2376 descr
->anchor_item
= -1;
2377 descr
->item_height
= 1;
2378 descr
->page_size
= 1;
2379 descr
->column_width
= 150;
2380 descr
->horz_extent
= (wnd
->dwStyle
& WS_HSCROLL
) ? 1 : 0;
2381 descr
->horz_pos
= 0;
2384 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2385 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2386 descr
->in_focus
= FALSE
;
2387 descr
->captured
= FALSE
;
2389 descr
->locale
= 0; /* FIXME */
2392 if( ( GetExpWinVer16( wnd
->hInstance
) & 0xFF00 ) == 0x0300
2393 && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2395 /* Win95 document "List Box Differences" from MSDN:
2396 If a list box in a version 3.x application has either the
2397 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2398 horizontal and vertical scroll bars.
2400 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2405 TRACE_(combo
)("[%04x]: resetting owner %04x -> %04x\n",
2406 wnd
->hwndSelf
, descr
->owner
, lphc
->self
->hwndSelf
);
2407 descr
->owner
= lphc
->self
->hwndSelf
;
2410 *(LB_DESCR
**)wnd
->wExtra
= descr
;
2412 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2414 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2415 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2416 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2417 descr
->item_height
= LISTBOX_SetFont( wnd
, descr
, 0 );
2419 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2421 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2423 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2424 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2428 mis
.CtlType
= ODT_LISTBOX
;
2429 mis
.CtlID
= wnd
->wIDmenu
;
2433 mis
.itemHeight
= descr
->item_height
;
2434 SendMessageW( descr
->owner
, WM_MEASUREITEM
, wnd
->wIDmenu
, (LPARAM
)&mis
);
2435 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2439 TRACE("owner: %04x, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2444 /***********************************************************************
2447 static BOOL
LISTBOX_Destroy( WND
*wnd
, LB_DESCR
*descr
)
2449 LISTBOX_ResetContent( wnd
, descr
);
2450 HeapFree( GetProcessHeap(), 0, descr
);
2456 /***********************************************************************
2459 static LRESULT WINAPI
ListBoxWndProc_locked( WND
* wnd
, UINT msg
,
2460 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2464 HWND hwnd
= wnd
->hwndSelf
;
2467 if (!(descr
= *(LB_DESCR
**)wnd
->wExtra
))
2473 if (!LISTBOX_Create( wnd
, NULL
))
2475 TRACE("creating wnd=%04x descr=%p\n",
2476 hwnd
, *(LB_DESCR
**)wnd
->wExtra
);
2482 * When a listbox is not in a combobox and the look
2483 * is win95, the WS_BORDER style is replaced with
2484 * the WS_EX_CLIENTEDGE style.
2486 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
2487 (wnd
->dwStyle
& WS_BORDER
) )
2489 wnd
->dwExStyle
|= WS_EX_CLIENTEDGE
;
2490 wnd
->dwStyle
&= ~ WS_BORDER
;
2495 /* Ignore all other messages before we get a WM_CREATE */
2496 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2497 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2500 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2501 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2504 case LB_RESETCONTENT16
:
2505 case LB_RESETCONTENT
:
2506 LISTBOX_ResetContent( wnd
, descr
);
2507 LISTBOX_UpdateScroll( wnd
, descr
);
2508 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
2511 case LB_ADDSTRING16
:
2512 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2518 if(unicode
|| !HAS_STRINGS(descr
))
2519 textW
= (LPWSTR
)lParam
;
2522 LPSTR textA
= (LPSTR
)lParam
;
2523 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2524 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2525 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2527 wParam
= LISTBOX_FindStringPos( wnd
, descr
, textW
, FALSE
);
2528 ret
= LISTBOX_InsertString( wnd
, descr
, wParam
, textW
);
2529 if (!unicode
&& HAS_STRINGS(descr
))
2530 HeapFree(GetProcessHeap(), 0, textW
);
2534 case LB_INSERTSTRING16
:
2535 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2536 wParam
= (INT
)(INT16
)wParam
;
2538 case LB_INSERTSTRING
:
2542 if(unicode
|| !HAS_STRINGS(descr
))
2543 textW
= (LPWSTR
)lParam
;
2546 LPSTR textA
= (LPSTR
)lParam
;
2547 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2548 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2549 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2551 ret
= LISTBOX_InsertString( wnd
, descr
, wParam
, textW
);
2552 if(!unicode
&& HAS_STRINGS(descr
))
2553 HeapFree(GetProcessHeap(), 0, textW
);
2558 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2564 if(unicode
|| !HAS_STRINGS(descr
))
2565 textW
= (LPWSTR
)lParam
;
2568 LPSTR textA
= (LPSTR
)lParam
;
2569 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2570 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2571 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2573 wParam
= LISTBOX_FindFileStrPos( wnd
, descr
, textW
);
2574 ret
= LISTBOX_InsertString( wnd
, descr
, wParam
, textW
);
2575 if(!unicode
&& HAS_STRINGS(descr
))
2576 HeapFree(GetProcessHeap(), 0, textW
);
2580 case LB_DELETESTRING16
:
2581 case LB_DELETESTRING
:
2582 if (LISTBOX_RemoveItem( wnd
, descr
, wParam
) != LB_ERR
)
2583 return descr
->nb_items
;
2587 case LB_GETITEMDATA16
:
2588 case LB_GETITEMDATA
:
2589 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2591 return descr
->items
[wParam
].data
;
2593 case LB_SETITEMDATA16
:
2594 case LB_SETITEMDATA
:
2595 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2597 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2602 return descr
->nb_items
;
2605 lParam
= (LPARAM
)MapSL(lParam
);
2608 return LISTBOX_GetText( descr
, wParam
, lParam
, unicode
);
2610 case LB_GETTEXTLEN16
:
2613 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2615 return (HAS_STRINGS(descr
) ? strlenW(descr
->items
[wParam
].str
)
2618 case LB_GETCURSEL16
:
2620 if (descr
->nb_items
==0)
2622 if (!IS_MULTISELECT(descr
))
2623 return descr
->selected_item
;
2625 if (descr
->selected_item
!=-1)
2626 return descr
->selected_item
;
2628 return descr
->focus_item
;
2629 /* otherwise, if the user tries to move the selection with the */
2630 /* arrow keys, we will give the application something to choke on */
2631 case LB_GETTOPINDEX16
:
2632 case LB_GETTOPINDEX
:
2633 return descr
->top_item
;
2635 case LB_GETITEMHEIGHT16
:
2636 case LB_GETITEMHEIGHT
:
2637 return LISTBOX_GetItemHeight( descr
, wParam
);
2639 case LB_SETITEMHEIGHT16
:
2640 lParam
= LOWORD(lParam
);
2642 case LB_SETITEMHEIGHT
:
2643 return LISTBOX_SetItemHeight( wnd
, descr
, wParam
, lParam
);
2645 case LB_ITEMFROMPOINT
:
2650 pt
.x
= LOWORD(lParam
);
2651 pt
.y
= HIWORD(lParam
);
2654 rect
.right
= descr
->width
;
2655 rect
.bottom
= descr
->height
;
2657 return MAKELONG( LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
),
2658 !PtInRect( &rect
, pt
) );
2661 case LB_SETCARETINDEX16
:
2662 case LB_SETCARETINDEX
:
2663 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2664 if (LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, !lParam
) == LB_ERR
)
2671 case LB_GETCARETINDEX16
:
2672 case LB_GETCARETINDEX
:
2673 return descr
->focus_item
;
2675 case LB_SETTOPINDEX16
:
2676 case LB_SETTOPINDEX
:
2677 return LISTBOX_SetTopItem( wnd
, descr
, wParam
, TRUE
);
2679 case LB_SETCOLUMNWIDTH16
:
2680 case LB_SETCOLUMNWIDTH
:
2681 return LISTBOX_SetColumnWidth( wnd
, descr
, wParam
);
2683 case LB_GETITEMRECT16
:
2686 ret
= LISTBOX_GetItemRect( descr
, (INT16
)wParam
, &rect
);
2687 CONV_RECT32TO16( &rect
, MapSL(lParam
) );
2691 case LB_GETITEMRECT
:
2692 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2694 case LB_FINDSTRING16
:
2695 wParam
= (INT
)(INT16
)wParam
;
2696 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2702 if(unicode
|| !HAS_STRINGS(descr
))
2703 textW
= (LPWSTR
)lParam
;
2706 LPSTR textA
= (LPSTR
)lParam
;
2707 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2708 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2709 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2711 ret
= LISTBOX_FindString( wnd
, descr
, wParam
, textW
, FALSE
);
2712 if(!unicode
&& HAS_STRINGS(descr
))
2713 HeapFree(GetProcessHeap(), 0, textW
);
2717 case LB_FINDSTRINGEXACT16
:
2718 wParam
= (INT
)(INT16
)wParam
;
2719 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2721 case LB_FINDSTRINGEXACT
:
2725 if(unicode
|| !HAS_STRINGS(descr
))
2726 textW
= (LPWSTR
)lParam
;
2729 LPSTR textA
= (LPSTR
)lParam
;
2730 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2731 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2732 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2734 ret
= LISTBOX_FindString( wnd
, descr
, wParam
, textW
, TRUE
);
2735 if(!unicode
&& HAS_STRINGS(descr
))
2736 HeapFree(GetProcessHeap(), 0, textW
);
2740 case LB_SELECTSTRING16
:
2741 wParam
= (INT
)(INT16
)wParam
;
2742 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2744 case LB_SELECTSTRING
:
2748 if(unicode
|| !HAS_STRINGS(descr
))
2749 textW
= (LPWSTR
)lParam
;
2752 LPSTR textA
= (LPSTR
)lParam
;
2753 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2754 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2755 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2757 index
= LISTBOX_FindString( wnd
, descr
, wParam
, textW
, FALSE
);
2758 if(!unicode
&& HAS_STRINGS(descr
))
2759 HeapFree(GetProcessHeap(), 0, textW
);
2760 if (index
!= LB_ERR
)
2761 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
2766 wParam
= (INT
)(INT16
)wParam
;
2769 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2771 return descr
->items
[wParam
].selected
;
2774 lParam
= (INT
)(INT16
)lParam
;
2777 return LISTBOX_SetSelection( wnd
, descr
, lParam
, wParam
, FALSE
);
2779 case LB_SETCURSEL16
:
2780 wParam
= (INT
)(INT16
)wParam
;
2783 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2784 LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, TRUE
);
2785 return LISTBOX_SetSelection( wnd
, descr
, wParam
, TRUE
, FALSE
);
2787 case LB_GETSELCOUNT16
:
2788 case LB_GETSELCOUNT
:
2789 return LISTBOX_GetSelCount( descr
);
2791 case LB_GETSELITEMS16
:
2792 return LISTBOX_GetSelItems16( descr
, wParam
, (LPINT16
)MapSL(lParam
) );
2794 case LB_GETSELITEMS
:
2795 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2797 case LB_SELITEMRANGE16
:
2798 case LB_SELITEMRANGE
:
2799 if (LOWORD(lParam
) <= HIWORD(lParam
))
2800 return LISTBOX_SelectItemRange( wnd
, descr
, LOWORD(lParam
),
2801 HIWORD(lParam
), wParam
);
2803 return LISTBOX_SelectItemRange( wnd
, descr
, HIWORD(lParam
),
2804 LOWORD(lParam
), wParam
);
2806 case LB_SELITEMRANGEEX16
:
2807 case LB_SELITEMRANGEEX
:
2808 if ((INT
)lParam
>= (INT
)wParam
)
2809 return LISTBOX_SelectItemRange( wnd
, descr
, wParam
, lParam
, TRUE
);
2811 return LISTBOX_SelectItemRange( wnd
, descr
, lParam
, wParam
, FALSE
);
2813 case LB_GETHORIZONTALEXTENT16
:
2814 case LB_GETHORIZONTALEXTENT
:
2815 return descr
->horz_extent
;
2817 case LB_SETHORIZONTALEXTENT16
:
2818 case LB_SETHORIZONTALEXTENT
:
2819 return LISTBOX_SetHorizontalExtent( wnd
, descr
, wParam
);
2821 case LB_GETANCHORINDEX16
:
2822 case LB_GETANCHORINDEX
:
2823 return descr
->anchor_item
;
2825 case LB_SETANCHORINDEX16
:
2826 wParam
= (INT
)(INT16
)wParam
;
2828 case LB_SETANCHORINDEX
:
2829 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2831 descr
->anchor_item
= (INT
)wParam
;
2835 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2836 * be set automatically (this is different in Win32) */
2837 if (wParam
& DDL_DRIVES
) wParam
|= DDL_EXCLUSIVE
;
2838 lParam
= (LPARAM
)MapSL(lParam
);
2845 textW
= (LPWSTR
)lParam
;
2848 LPSTR textA
= (LPSTR
)lParam
;
2849 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2850 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2851 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2853 ret
= LISTBOX_Directory( wnd
, descr
, wParam
, textW
, msg
== LB_DIR
);
2855 HeapFree(GetProcessHeap(), 0, textW
);
2860 return descr
->locale
;
2863 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2866 case LB_INITSTORAGE
:
2867 return LISTBOX_InitStorage( wnd
, descr
, wParam
);
2870 return LISTBOX_SetCount( wnd
, descr
, (INT
)wParam
);
2872 case LB_SETTABSTOPS16
:
2873 return LISTBOX_SetTabStops( wnd
, descr
, (INT
)(INT16
)wParam
, MapSL(lParam
), TRUE
);
2875 case LB_SETTABSTOPS
:
2876 return LISTBOX_SetTabStops( wnd
, descr
, wParam
, (LPINT
)lParam
, FALSE
);
2880 if (descr
->caret_on
)
2882 descr
->caret_on
= TRUE
;
2883 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2884 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2889 if (!descr
->caret_on
)
2891 descr
->caret_on
= FALSE
;
2892 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2893 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2897 return LISTBOX_Destroy( wnd
, descr
);
2900 InvalidateRect( hwnd
, NULL
, TRUE
);
2904 LISTBOX_SetRedraw( wnd
, descr
, wParam
!= 0 );
2908 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2913 HDC hdc
= ( wParam
) ? ((HDC
)wParam
)
2914 : BeginPaint( hwnd
, &ps
);
2915 ret
= LISTBOX_Paint( wnd
, descr
, hdc
);
2916 if( !wParam
) EndPaint( hwnd
, &ps
);
2920 LISTBOX_UpdateSize( wnd
, descr
);
2925 LISTBOX_SetFont( wnd
, descr
, (HFONT
)wParam
);
2926 if (lParam
) InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
2929 descr
->in_focus
= TRUE
;
2930 descr
->caret_on
= TRUE
;
2931 if (descr
->focus_item
!= -1)
2932 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2933 SEND_NOTIFICATION( wnd
, descr
, LBN_SETFOCUS
);
2936 descr
->in_focus
= FALSE
;
2937 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2938 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2939 SEND_NOTIFICATION( wnd
, descr
, LBN_KILLFOCUS
);
2942 return LISTBOX_HandleHScroll( wnd
, descr
, wParam
);
2944 return LISTBOX_HandleVScroll( wnd
, descr
, wParam
);
2945 case WM_MOUSEACTIVATE
:
2946 return MA_NOACTIVATE
;
2948 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2949 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2950 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2951 return LISTBOX_HandleMouseWheel( wnd
, descr
, wParam
);
2952 case WM_LBUTTONDOWN
:
2953 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2954 (INT16
)LOWORD(lParam
),
2955 (INT16
)HIWORD(lParam
) );
2956 case WM_LBUTTONDBLCLK
:
2957 if (descr
->style
& LBS_NOTIFY
)
2958 SEND_NOTIFICATION( wnd
, descr
, LBN_DBLCLK
);
2961 if (GetCapture() == hwnd
)
2962 LISTBOX_HandleMouseMove( wnd
, descr
, (INT16
)LOWORD(lParam
),
2963 (INT16
)HIWORD(lParam
) );
2966 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2968 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2973 charW
= (WCHAR
)wParam
;
2976 CHAR charA
= (CHAR
)wParam
;
2977 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
2979 return LISTBOX_HandleChar( wnd
, descr
, charW
);
2982 return LISTBOX_HandleSystemTimer( wnd
, descr
);
2984 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
2987 HBRUSH hbrush
= SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
2988 wParam
, (LPARAM
)wnd
->hwndSelf
);
2989 GetClientRect(hwnd
, &rect
);
2990 if (hbrush
) FillRect( (HDC
)wParam
, &rect
, hbrush
);
2995 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
2996 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3000 case WM_QUERYDROPOBJECT
:
3005 LPDRAGINFO16 dragInfo
= MapSL( lParam
);
3006 dragInfo
->l
= LISTBOX_GetItemFromPoint( descr
, dragInfo
->pt
.x
,
3008 return SendMessage16( descr
->owner
, msg
, wParam
, lParam
);
3013 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3014 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
3015 hwnd
, msg
, wParam
, lParam
);
3016 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3017 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3022 /***********************************************************************
3025 * This is just a wrapper for the real wndproc, it only does window locking
3028 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3030 WND
* wndPtr
= WIN_FindWndPtr( hwnd
);
3031 LRESULT res
= ListBoxWndProc_locked(wndPtr
, msg
, wParam
, lParam
, FALSE
);
3033 WIN_ReleaseWndPtr(wndPtr
);
3037 /***********************************************************************
3040 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3042 WND
* wndPtr
= WIN_FindWndPtr( hwnd
);
3043 LRESULT res
= ListBoxWndProc_locked(wndPtr
, msg
, wParam
, lParam
, TRUE
);
3045 WIN_ReleaseWndPtr(wndPtr
);
3049 /***********************************************************************
3050 * ComboLBWndProc_locked
3052 * The real combo listbox wndproc, but called with locked WND struct.
3054 static LRESULT WINAPI
ComboLBWndProc_locked( WND
* wnd
, UINT msg
,
3055 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
3058 HWND hwnd
= wnd
->hwndSelf
;
3062 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
3064 TRACE_(combo
)("[%04x]: msg %s wp %08x lp %08lx\n",
3065 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
3067 if( descr
|| msg
== WM_CREATE
)
3069 LPHEADCOMBO lphc
= (descr
) ? descr
->lphc
: NULL
;
3074 #define lpcs ((LPCREATESTRUCTA)lParam)
3075 TRACE_(combo
)("\tpassed parent handle = 0x%08x\n",
3076 (UINT
)lpcs
->lpCreateParams
);
3078 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
3080 return LISTBOX_Create( wnd
, lphc
);
3082 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
3083 (CB_GETTYPE(lphc
) != CBS_SIMPLE
) )
3089 mousePos
.x
= (INT16
)LOWORD(lParam
);
3090 mousePos
.y
= (INT16
)HIWORD(lParam
);
3093 * If we are in a dropdown combobox, we simulate that
3094 * the mouse is captured to show the tracking of the item.
3096 GetClientRect(hwnd
, &clientRect
);
3098 if (PtInRect( &clientRect
, mousePos
))
3100 captured
= descr
->captured
;
3101 descr
->captured
= TRUE
;
3103 LISTBOX_HandleMouseMove( wnd
, descr
,
3104 mousePos
.x
, mousePos
.y
);
3106 descr
->captured
= captured
;
3111 LISTBOX_HandleMouseMove( wnd
, descr
,
3112 mousePos
.x
, mousePos
.y
);
3121 * If we are in Win3.1 look, go with the default behavior.
3123 return unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3124 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3127 if (TWEAK_WineLook
> WIN31_LOOK
)
3133 * If the mouse button "up" is not in the listbox,
3134 * we make sure there is no selection by re-selecting the
3135 * item that was selected when the listbox was made visible.
3137 mousePos
.x
= (INT16
)LOWORD(lParam
);
3138 mousePos
.y
= (INT16
)HIWORD(lParam
);
3140 GetClientRect(hwnd
, &clientRect
);
3143 * When the user clicks outside the combobox and the focus
3144 * is lost, the owning combobox will send a fake buttonup with
3145 * 0xFFFFFFF as the mouse location, we must also revert the
3146 * selection to the original selection.
3148 if ( (lParam
== (LPARAM
)-1) ||
3149 (!PtInRect( &clientRect
, mousePos
)) )
3151 LISTBOX_MoveCaret( wnd
,
3157 return LISTBOX_HandleLButtonUp( wnd
, descr
);
3158 case WM_LBUTTONDBLCLK
:
3159 case WM_LBUTTONDOWN
:
3160 return LISTBOX_HandleLButtonDownCombo(wnd
, descr
, msg
, wParam
,
3161 (INT16
)LOWORD(lParam
),
3162 (INT16
)HIWORD(lParam
) );
3163 case WM_MOUSEACTIVATE
:
3164 return MA_NOACTIVATE
;
3168 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3170 /* for some reason(?) Windows makes it possible to
3171 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3173 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3174 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3175 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3177 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3181 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
3183 case LB_SETCURSEL16
:
3185 lRet
= unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3186 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3187 lRet
=(lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
3190 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3195 return unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3196 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3199 lRet
= unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3200 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3202 TRACE_(combo
)("\t default on msg [%04x]\n", (UINT16
)msg
);
3207 /***********************************************************************
3210 * NOTE: in Windows, winproc address of the ComboLBox is the same
3211 * as that of the Listbox.
3213 * This is just a wrapper for the real wndproc, it only does window locking
3216 LRESULT WINAPI
ComboLBWndProcA( HWND hwnd
, UINT msg
,
3217 WPARAM wParam
, LPARAM lParam
)
3219 WND
*wnd
= WIN_FindWndPtr( hwnd
);
3220 LRESULT res
= ComboLBWndProc_locked(wnd
, msg
, wParam
, lParam
, FALSE
);
3222 WIN_ReleaseWndPtr(wnd
);
3226 /***********************************************************************
3229 LRESULT WINAPI
ComboLBWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3231 WND
*wnd
= WIN_FindWndPtr( hwnd
);
3232 LRESULT res
= ComboLBWndProc_locked(wnd
, msg
, wParam
, lParam
, TRUE
);
3234 WIN_ReleaseWndPtr(wnd
);