4 * Copyright 1996 Alexandre Julliard
12 #include "wine/winuser16.h"
13 #include "wine/winbase16.h"
20 #include "debugtools.h"
23 DEFAULT_DEBUG_CHANNEL(listbox
);
24 DECLARE_DEBUG_CHANNEL(combo
);
33 /* Items array granularity */
34 #define LB_ARRAY_GRANULARITY 16
36 /* Scrolling timeout in ms */
37 #define LB_SCROLL_TIMEOUT 50
39 /* Listbox system timer id */
42 /* flag listbox changed while setredraw false - internal style */
43 #define LBS_DISPLAYCHANGED 0x80000000
48 LPSTR str
; /* Item text */
49 BOOL selected
; /* Is item selected? */
50 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
51 DWORD data
; /* User data */
54 /* Listbox structure */
57 HANDLE heap
; /* Heap for this listbox */
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 (SendMessageA( (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
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
116 /*********************************************************************
117 * listbox class descriptor
119 const struct builtin_class_descr LISTBOX_builtin_class
=
121 "ListBox", /* name */
122 CS_GLOBALCLASS
| CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
123 ListBoxWndProcA
, /* procA */
124 NULL
, /* procW (FIXME) */
125 sizeof(LB_DESCR
*), /* extra */
126 IDC_ARROWA
, /* cursor */
131 /*********************************************************************
132 * combolbox class descriptor
134 const struct builtin_class_descr COMBOLBOX_builtin_class
=
136 "ComboLBox", /* name */
137 CS_GLOBALCLASS
| CS_DBLCLKS
| CS_SAVEBITS
, /* style */
138 ComboLBWndProcA
, /* procA */
139 NULL
, /* procW (FIXME) */
140 sizeof(LB_DESCR
*), /* extra */
141 IDC_ARROWA
, /* cursor */
146 /***********************************************************************
149 void LISTBOX_Dump( WND
*wnd
)
153 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
155 TRACE( "Listbox:\n" );
156 TRACE( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
157 wnd
->hwndSelf
, (UINT
)descr
, descr
->heap
, descr
->nb_items
,
159 for (i
= 0, item
= descr
->items
; i
< descr
->nb_items
; i
++, item
++)
161 TRACE( "%4d: %-40s %d %08lx %3d\n",
162 i
, item
->str
, item
->selected
, item
->data
, item
->height
);
167 /***********************************************************************
168 * LISTBOX_GetCurrentPageSize
170 * Return the current page size
172 static INT
LISTBOX_GetCurrentPageSize( WND
*wnd
, LB_DESCR
*descr
)
175 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
176 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
178 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
180 if (i
== descr
->top_item
) return 1;
181 else return i
- descr
->top_item
;
185 /***********************************************************************
186 * LISTBOX_GetMaxTopIndex
188 * Return the maximum possible index for the top of the listbox.
190 static INT
LISTBOX_GetMaxTopIndex( WND
*wnd
, LB_DESCR
*descr
)
194 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
196 page
= descr
->height
;
197 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
198 if ((page
-= descr
->items
[max
].height
) < 0) break;
199 if (max
< descr
->nb_items
- 1) max
++;
201 else if (descr
->style
& LBS_MULTICOLUMN
)
203 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
204 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
205 max
= (max
- page
) * descr
->page_size
;
209 max
= descr
->nb_items
- descr
->page_size
;
211 if (max
< 0) max
= 0;
216 /***********************************************************************
217 * LISTBOX_UpdateScroll
219 * Update the scrollbars. Should be called whenever the content
220 * of the listbox changes.
222 static void LISTBOX_UpdateScroll( WND
*wnd
, LB_DESCR
*descr
)
226 /* Check the listbox scroll bar flags individually before we call
227 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
228 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
229 scroll bar when we do not need one.
230 if (!(descr->style & WS_VSCROLL)) return;
233 /* It is important that we check descr->style, and not wnd->dwStyle,
234 for WS_VSCROLL, as the former is exactly the one passed in
235 argument to CreateWindow.
236 In Windows (and from now on in Wine :) a listbox created
237 with such a style (no WS_SCROLL) does not update
238 the scrollbar with listbox-related data, thus letting
239 the programmer use it for his/her own purposes. */
241 if (descr
->style
& LBS_NOREDRAW
) return;
242 info
.cbSize
= sizeof(info
);
244 if (descr
->style
& LBS_MULTICOLUMN
)
247 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
248 info
.nPos
= descr
->top_item
/ descr
->page_size
;
249 info
.nPage
= descr
->width
/ descr
->column_width
;
250 if (info
.nPage
< 1) info
.nPage
= 1;
251 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
252 if (descr
->style
& LBS_DISABLENOSCROLL
)
253 info
.fMask
|= SIF_DISABLENOSCROLL
;
254 if (descr
->style
& WS_HSCROLL
)
255 SetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
257 info
.fMask
= SIF_RANGE
;
258 if (descr
->style
& WS_VSCROLL
)
259 SetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
264 info
.nMax
= descr
->nb_items
- 1;
265 info
.nPos
= descr
->top_item
;
266 info
.nPage
= LISTBOX_GetCurrentPageSize( wnd
, descr
);
267 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
268 if (descr
->style
& LBS_DISABLENOSCROLL
)
269 info
.fMask
|= SIF_DISABLENOSCROLL
;
270 if (descr
->style
& WS_VSCROLL
)
271 SetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
273 if (descr
->horz_extent
)
276 info
.nMax
= descr
->horz_extent
- 1;
277 info
.nPos
= descr
->horz_pos
;
278 info
.nPage
= descr
->width
;
279 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
280 if (descr
->style
& LBS_DISABLENOSCROLL
)
281 info
.fMask
|= SIF_DISABLENOSCROLL
;
282 if (descr
->style
& WS_HSCROLL
)
283 SetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
290 /***********************************************************************
293 * Set the top item of the listbox, scrolling up or down if necessary.
295 static LRESULT
LISTBOX_SetTopItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
298 INT max
= LISTBOX_GetMaxTopIndex( wnd
, descr
);
299 if (index
> max
) index
= max
;
300 if (index
< 0) index
= 0;
301 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
302 if (descr
->top_item
== index
) return LB_OKAY
;
303 if (descr
->style
& LBS_MULTICOLUMN
)
305 INT diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
306 if (scroll
&& (abs(diff
) < descr
->width
))
307 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
308 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
316 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
320 if (index
> descr
->top_item
)
322 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
323 diff
-= descr
->items
[i
].height
;
327 for (i
= index
; i
< descr
->top_item
; i
++)
328 diff
+= descr
->items
[i
].height
;
332 diff
= (descr
->top_item
- index
) * descr
->item_height
;
334 if (abs(diff
) < descr
->height
)
335 ScrollWindowEx( wnd
->hwndSelf
, 0, diff
, NULL
, NULL
, 0, NULL
,
336 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
340 if (!scroll
) InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
341 descr
->top_item
= index
;
342 LISTBOX_UpdateScroll( wnd
, descr
);
347 /***********************************************************************
350 * Update the page size. Should be called when the size of
351 * the client area or the item height changes.
353 static void LISTBOX_UpdatePage( WND
*wnd
, LB_DESCR
*descr
)
357 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
359 if (page_size
== descr
->page_size
) return;
360 descr
->page_size
= page_size
;
361 if (descr
->style
& LBS_MULTICOLUMN
)
362 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
363 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
, FALSE
);
367 /***********************************************************************
370 * Update the size of the listbox. Should be called when the size of
371 * the client area changes.
373 static void LISTBOX_UpdateSize( WND
*wnd
, LB_DESCR
*descr
)
377 GetClientRect( wnd
->hwndSelf
, &rect
);
378 descr
->width
= rect
.right
- rect
.left
;
379 descr
->height
= rect
.bottom
- rect
.top
;
380 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
384 if(descr
->item_height
!= 0)
385 remaining
= descr
->height
% descr
->item_height
;
388 if ((descr
->height
> descr
->item_height
) && remaining
)
390 if (!(wnd
->flags
& WIN_ISWIN32
))
391 { /* give a margin for error to 16 bits programs - if we need
392 less than the height of the nonclient area, round to the
393 *next* number of items */
394 int ncheight
= wnd
->rectWindow
.bottom
- wnd
->rectWindow
.top
- descr
->height
;
395 if ((descr
->item_height
- remaining
) <= ncheight
)
396 remaining
= remaining
- descr
->item_height
;
398 TRACE("[%04x]: changing height %d -> %d\n",
399 wnd
->hwndSelf
, descr
->height
,
400 descr
->height
- remaining
);
401 SetWindowPos( wnd
->hwndSelf
, 0, 0, 0,
402 wnd
->rectWindow
.right
- wnd
->rectWindow
.left
,
403 wnd
->rectWindow
.bottom
- wnd
->rectWindow
.top
- remaining
,
404 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
408 TRACE("[%04x]: new size = %d,%d\n",
409 wnd
->hwndSelf
, descr
->width
, descr
->height
);
410 LISTBOX_UpdatePage( wnd
, descr
);
411 LISTBOX_UpdateScroll( wnd
, descr
);
415 /***********************************************************************
416 * LISTBOX_GetItemRect
418 * Get the rectangle enclosing an item, in listbox client coordinates.
419 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
421 static LRESULT
LISTBOX_GetItemRect( WND
*wnd
, LB_DESCR
*descr
, INT index
,
424 /* Index <= 0 is legal even on empty listboxes */
425 if (index
&& (index
>= descr
->nb_items
)) return -1;
426 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
427 if (descr
->style
& LBS_MULTICOLUMN
)
429 INT col
= (index
/ descr
->page_size
) -
430 (descr
->top_item
/ descr
->page_size
);
431 rect
->left
+= col
* descr
->column_width
;
432 rect
->right
= rect
->left
+ descr
->column_width
;
433 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
434 rect
->bottom
= rect
->top
+ descr
->item_height
;
436 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
439 rect
->right
+= descr
->horz_pos
;
440 if ((index
>= 0) && (index
< descr
->nb_items
))
442 if (index
< descr
->top_item
)
444 for (i
= descr
->top_item
-1; i
>= index
; i
--)
445 rect
->top
-= descr
->items
[i
].height
;
449 for (i
= descr
->top_item
; i
< index
; i
++)
450 rect
->top
+= descr
->items
[i
].height
;
452 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
458 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
459 rect
->bottom
= rect
->top
+ descr
->item_height
;
460 rect
->right
+= descr
->horz_pos
;
463 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
464 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
468 /***********************************************************************
469 * LISTBOX_GetItemFromPoint
471 * Return the item nearest from point (x,y) (in client coordinates).
473 static INT
LISTBOX_GetItemFromPoint( WND
*wnd
, LB_DESCR
*descr
,
476 INT index
= descr
->top_item
;
478 if (!descr
->nb_items
) return -1; /* No items */
479 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
484 while (index
< descr
->nb_items
)
486 if ((pos
+= descr
->items
[index
].height
) > y
) break;
495 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
499 else if (descr
->style
& LBS_MULTICOLUMN
)
501 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
502 if (y
>= 0) index
+= y
/ descr
->item_height
;
503 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
504 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
508 index
+= (y
/ descr
->item_height
);
510 if (index
< 0) return 0;
511 if (index
>= descr
->nb_items
) return -1;
516 /***********************************************************************
521 static void LISTBOX_PaintItem( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
,
522 const RECT
*rect
, INT index
, UINT action
, BOOL ignoreFocus
)
524 LB_ITEMDATA
*item
= NULL
;
525 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
527 if (IS_OWNERDRAW(descr
))
535 if (action
== ODA_FOCUS
)
536 DrawFocusRect( hdc
, rect
);
538 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
542 /* some programs mess with the clipping region when
543 drawing the item, *and* restore the previous region
544 after they are done, so a region has better to exist
545 else everything ends clipped */
546 GetClientRect(wnd
->hwndSelf
, &r
);
547 hrgn
= CreateRectRgnIndirect(&r
);
548 SelectClipRgn( hdc
, hrgn
);
549 DeleteObject( hrgn
);
551 dis
.CtlType
= ODT_LISTBOX
;
552 dis
.CtlID
= wnd
->wIDmenu
;
553 dis
.hwndItem
= wnd
->hwndSelf
;
554 dis
.itemAction
= action
;
558 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
559 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
561 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
562 if (wnd
->dwStyle
& WS_DISABLED
) dis
.itemState
|= ODS_DISABLED
;
563 dis
.itemData
= item
? item
->data
: 0;
565 TRACE("[%04x]: drawitem %d (%s) action=%02x "
566 "state=%02x rect=%d,%d-%d,%d\n",
567 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
568 dis
.itemState
, rect
->left
, rect
->top
,
569 rect
->right
, rect
->bottom
);
570 SendMessageA(descr
->owner
, WM_DRAWITEM
, wnd
->wIDmenu
, (LPARAM
)&dis
);
574 COLORREF oldText
= 0, oldBk
= 0;
576 if (action
== ODA_FOCUS
)
578 DrawFocusRect( hdc
, rect
);
581 if (item
&& item
->selected
)
583 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
584 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
587 TRACE("[%04x]: painting %d (%s) action=%02x "
588 "rect=%d,%d-%d,%d\n",
589 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
590 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
592 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
,
593 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
594 else if (!(descr
->style
& LBS_USETABSTOPS
))
595 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
,
596 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
597 strlen(item
->str
), NULL
);
600 /* Output empty string to paint background in the full width. */
601 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
,
602 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
603 TabbedTextOutA( hdc
, rect
->left
+ 1 , rect
->top
,
604 item
->str
, strlen(item
->str
),
605 descr
->nb_tabs
, descr
->tabs
, 0);
607 if (item
&& item
->selected
)
609 SetBkColor( hdc
, oldBk
);
610 SetTextColor( hdc
, oldText
);
612 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
614 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
619 /***********************************************************************
622 * Change the redraw flag.
624 static void LISTBOX_SetRedraw( WND
*wnd
, LB_DESCR
*descr
, BOOL on
)
628 if (!(descr
->style
& LBS_NOREDRAW
)) return;
629 descr
->style
&= ~LBS_NOREDRAW
;
630 if (descr
->style
& LBS_DISPLAYCHANGED
)
631 { /* page was changed while setredraw false, refresh automatically */
632 InvalidateRect(wnd
->hwndSelf
, NULL
, TRUE
);
633 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
634 { /* reset top of page if less than number of items/page */
635 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
636 if (descr
->top_item
< 0) descr
->top_item
= 0;
638 descr
->style
&= ~LBS_DISPLAYCHANGED
;
640 LISTBOX_UpdateScroll( wnd
, descr
);
642 else descr
->style
|= LBS_NOREDRAW
;
646 /***********************************************************************
647 * LISTBOX_RepaintItem
649 * Repaint a single item synchronously.
651 static void LISTBOX_RepaintItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
657 HBRUSH hbrush
, oldBrush
= 0;
659 /* Do not repaint the item if the item is not visible */
660 if (!IsWindowVisible(wnd
->hwndSelf
)) return;
661 if (descr
->style
& LBS_NOREDRAW
)
663 descr
->style
|= LBS_DISPLAYCHANGED
;
666 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) != 1) return;
667 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
))) return;
668 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
669 hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
670 hdc
, (LPARAM
)wnd
->hwndSelf
);
671 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
672 if (wnd
->dwStyle
& WS_DISABLED
)
673 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
674 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
675 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, index
, action
, FALSE
);
676 if (oldFont
) SelectObject( hdc
, oldFont
);
677 if (oldBrush
) SelectObject( hdc
, oldBrush
);
678 ReleaseDC( wnd
->hwndSelf
, hdc
);
682 /***********************************************************************
683 * LISTBOX_InitStorage
685 static LRESULT
LISTBOX_InitStorage( WND
*wnd
, LB_DESCR
*descr
, INT nb_items
,
690 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
691 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
693 nb_items
+= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
694 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
695 nb_items
* sizeof(LB_ITEMDATA
) )))
697 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
705 /***********************************************************************
706 * LISTBOX_SetTabStops
708 static BOOL
LISTBOX_SetTabStops( WND
*wnd
, LB_DESCR
*descr
, INT count
,
709 LPINT tabs
, BOOL short_ints
)
711 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
712 if (descr
->tabs
) HeapFree( descr
->heap
, 0, descr
->tabs
);
713 if (!(descr
->nb_tabs
= count
))
718 /* FIXME: count = 1 */
719 if (!(descr
->tabs
= (INT
*)HeapAlloc( descr
->heap
, 0,
720 descr
->nb_tabs
* sizeof(INT
) )))
725 LPINT16 p
= (LPINT16
)tabs
;
727 TRACE("[%04x]: settabstops ", wnd
->hwndSelf
);
728 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
729 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
730 if (TRACE_ON(listbox
)) DPRINTF("%hd ", descr
->tabs
[i
]);
732 if (TRACE_ON(listbox
)) DPRINTF("\n");
734 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
735 /* FIXME: repaint the window? */
740 /***********************************************************************
743 static LRESULT
LISTBOX_GetText( WND
*wnd
, LB_DESCR
*descr
, INT index
,
746 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
747 if (HAS_STRINGS(descr
))
750 return strlen(descr
->items
[index
].str
);
751 strcpy( buffer
, descr
->items
[index
].str
);
752 return strlen(buffer
);
755 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
756 return sizeof(DWORD
);
761 /***********************************************************************
762 * LISTBOX_FindStringPos
764 * Find the nearest string located before a given string in sort order.
765 * If 'exact' is TRUE, return an error if we don't get an exact match.
767 static INT
LISTBOX_FindStringPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
,
770 INT index
, min
, max
, res
= -1;
772 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
774 max
= descr
->nb_items
;
777 index
= (min
+ max
) / 2;
778 if (HAS_STRINGS(descr
))
779 res
= lstrcmpiA( descr
->items
[index
].str
, str
);
782 COMPAREITEMSTRUCT cis
;
784 cis
.CtlType
= ODT_LISTBOX
;
785 cis
.CtlID
= wnd
->wIDmenu
;
786 cis
.hwndItem
= wnd
->hwndSelf
;
788 cis
.itemData1
= descr
->items
[index
].data
;
790 cis
.itemData2
= (DWORD
)str
;
791 cis
.dwLocaleId
= descr
->locale
;
792 res
= SendMessageA( descr
->owner
, WM_COMPAREITEM
,
793 wnd
->wIDmenu
, (LPARAM
)&cis
);
795 if (!res
) return index
;
796 if (res
> 0) max
= index
;
797 else min
= index
+ 1;
799 return exact
? -1 : max
;
803 /***********************************************************************
804 * LISTBOX_FindFileStrPos
806 * Find the nearest string located before a given string in directory
807 * sort order (i.e. first files, then directories, then drives).
809 static INT
LISTBOX_FindFileStrPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
)
811 INT min
, max
, res
= -1;
813 if (!HAS_STRINGS(descr
))
814 return LISTBOX_FindStringPos( wnd
, descr
, str
, FALSE
);
816 max
= descr
->nb_items
;
819 INT index
= (min
+ max
) / 2;
820 const char *p
= descr
->items
[index
].str
;
821 if (*p
== '[') /* drive or directory */
823 if (*str
!= '[') res
= -1;
824 else if (p
[1] == '-') /* drive */
826 if (str
[1] == '-') res
= str
[2] - p
[2];
831 if (str
[1] == '-') res
= 1;
832 else res
= lstrcmpiA( str
, p
);
837 if (*str
== '[') res
= 1;
838 else res
= lstrcmpiA( str
, p
);
840 if (!res
) return index
;
841 if (res
< 0) max
= index
;
842 else min
= index
+ 1;
848 /***********************************************************************
851 * Find the item beginning with a given string.
853 static INT
LISTBOX_FindString( WND
*wnd
, LB_DESCR
*descr
, INT start
,
854 LPCSTR str
, BOOL exact
)
859 if (start
>= descr
->nb_items
) start
= -1;
860 item
= descr
->items
+ start
+ 1;
861 if (HAS_STRINGS(descr
))
863 if (!str
|| ! str
[0] ) return LB_ERR
;
866 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
867 if (!lstrcmpiA( str
, item
->str
)) return i
;
868 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
869 if (!lstrcmpiA( str
, item
->str
)) return i
;
873 /* Special case for drives and directories: ignore prefix */
874 #define CHECK_DRIVE(item) \
875 if ((item)->str[0] == '[') \
877 if (!strncasecmp( str, (item)->str+1, len )) return i; \
878 if (((item)->str[1] == '-') && !strncasecmp(str,(item)->str+2,len)) \
882 INT len
= strlen(str
);
883 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
885 if (!strncasecmp( str
, item
->str
, len
)) return i
;
888 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
890 if (!strncasecmp( str
, item
->str
, len
)) return i
;
898 if (exact
&& (descr
->style
& LBS_SORT
))
899 /* If sorted, use a WM_COMPAREITEM binary search */
900 return LISTBOX_FindStringPos( wnd
, descr
, str
, TRUE
);
902 /* Otherwise use a linear search */
903 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
904 if (item
->data
== (DWORD
)str
) return i
;
905 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
906 if (item
->data
== (DWORD
)str
) return i
;
912 /***********************************************************************
913 * LISTBOX_GetSelCount
915 static LRESULT
LISTBOX_GetSelCount( WND
*wnd
, LB_DESCR
*descr
)
918 LB_ITEMDATA
*item
= descr
->items
;
920 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
921 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
922 if (item
->selected
) count
++;
927 /***********************************************************************
928 * LISTBOX_GetSelItems16
930 static LRESULT
LISTBOX_GetSelItems16( WND
*wnd
, LB_DESCR
*descr
, INT16 max
,
934 LB_ITEMDATA
*item
= descr
->items
;
936 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
937 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
938 if (item
->selected
) array
[count
++] = (INT16
)i
;
943 /***********************************************************************
944 * LISTBOX_GetSelItems32
946 static LRESULT
LISTBOX_GetSelItems( WND
*wnd
, LB_DESCR
*descr
, INT max
,
950 LB_ITEMDATA
*item
= descr
->items
;
952 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
953 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
954 if (item
->selected
) array
[count
++] = i
;
959 /***********************************************************************
962 static LRESULT
LISTBOX_Paint( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
)
964 INT i
, col_pos
= descr
->page_size
- 1;
966 RECT focusRect
= {-1, -1, -1, -1};
968 HBRUSH hbrush
, oldBrush
= 0;
970 if (descr
->style
& LBS_NOREDRAW
) return 0;
972 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
973 if (descr
->style
& LBS_MULTICOLUMN
)
974 rect
.right
= rect
.left
+ descr
->column_width
;
975 else if (descr
->horz_pos
)
977 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
978 rect
.right
+= descr
->horz_pos
;
981 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
982 hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
983 hdc
, (LPARAM
)wnd
->hwndSelf
);
984 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
985 if (wnd
->dwStyle
& WS_DISABLED
)
986 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
988 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
991 /* Special case for empty listbox: paint focus rect */
992 rect
.bottom
= rect
.top
+ descr
->item_height
;
993 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, descr
->focus_item
,
995 rect
.top
= rect
.bottom
;
998 /* Paint all the item, regarding the selection
999 Focus state will be painted after */
1001 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1003 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1004 rect
.bottom
= rect
.top
+ descr
->item_height
;
1006 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1008 if (i
== descr
->focus_item
)
1010 /* keep the focus rect, to paint the focus item after */
1011 focusRect
.left
= rect
.left
;
1012 focusRect
.right
= rect
.right
;
1013 focusRect
.top
= rect
.top
;
1014 focusRect
.bottom
= rect
.bottom
;
1016 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1017 rect
.top
= rect
.bottom
;
1019 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1021 if (!IS_OWNERDRAW(descr
))
1023 /* Clear the bottom of the column */
1024 if (rect
.top
< descr
->height
)
1026 rect
.bottom
= descr
->height
;
1027 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1028 &rect
, NULL
, 0, NULL
);
1032 /* Go to the next column */
1033 rect
.left
+= descr
->column_width
;
1034 rect
.right
+= descr
->column_width
;
1036 col_pos
= descr
->page_size
- 1;
1041 if (rect
.top
>= descr
->height
) break;
1045 /* Paint the focus item now */
1046 if (focusRect
.top
!= focusRect
.bottom
&& descr
->caret_on
)
1047 LISTBOX_PaintItem( wnd
, descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1049 if (!IS_OWNERDRAW(descr
))
1051 /* Clear the remainder of the client area */
1052 if (rect
.top
< descr
->height
)
1054 rect
.bottom
= descr
->height
;
1055 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1056 &rect
, NULL
, 0, NULL
);
1058 if (rect
.right
< descr
->width
)
1060 rect
.left
= rect
.right
;
1061 rect
.right
= descr
->width
;
1063 rect
.bottom
= descr
->height
;
1064 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1065 &rect
, NULL
, 0, NULL
);
1068 if (oldFont
) SelectObject( hdc
, oldFont
);
1069 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1074 /***********************************************************************
1075 * LISTBOX_InvalidateItems
1077 * Invalidate all items from a given item. If the specified item is not
1078 * visible, nothing happens.
1080 static void LISTBOX_InvalidateItems( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1084 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) == 1)
1086 if (descr
->style
& LBS_NOREDRAW
)
1088 descr
->style
|= LBS_DISPLAYCHANGED
;
1091 rect
.bottom
= descr
->height
;
1092 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
1093 if (descr
->style
& LBS_MULTICOLUMN
)
1095 /* Repaint the other columns */
1096 rect
.left
= rect
.right
;
1097 rect
.right
= descr
->width
;
1099 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
1105 /***********************************************************************
1106 * LISTBOX_GetItemHeight
1108 static LRESULT
LISTBOX_GetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1110 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1112 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1113 return descr
->items
[index
].height
;
1115 else return descr
->item_height
;
1119 /***********************************************************************
1120 * LISTBOX_SetItemHeight
1122 static LRESULT
LISTBOX_SetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1125 if (!height
) height
= 1;
1127 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1129 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1130 TRACE("[%04x]: item %d height = %d\n",
1131 wnd
->hwndSelf
, index
, height
);
1132 descr
->items
[index
].height
= height
;
1133 LISTBOX_UpdateScroll( wnd
, descr
);
1134 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1136 else if (height
!= descr
->item_height
)
1138 TRACE("[%04x]: new height = %d\n",
1139 wnd
->hwndSelf
, height
);
1140 descr
->item_height
= height
;
1141 LISTBOX_UpdatePage( wnd
, descr
);
1142 LISTBOX_UpdateScroll( wnd
, descr
);
1143 InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
1149 /***********************************************************************
1150 * LISTBOX_SetHorizontalPos
1152 static void LISTBOX_SetHorizontalPos( WND
*wnd
, LB_DESCR
*descr
, INT pos
)
1156 if (pos
> descr
->horz_extent
- descr
->width
)
1157 pos
= descr
->horz_extent
- descr
->width
;
1158 if (pos
< 0) pos
= 0;
1159 if (!(diff
= descr
->horz_pos
- pos
)) return;
1160 TRACE("[%04x]: new horz pos = %d\n",
1161 wnd
->hwndSelf
, pos
);
1162 descr
->horz_pos
= pos
;
1163 LISTBOX_UpdateScroll( wnd
, descr
);
1164 if (abs(diff
) < descr
->width
)
1165 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
1166 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1168 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1172 /***********************************************************************
1173 * LISTBOX_SetHorizontalExtent
1175 static LRESULT
LISTBOX_SetHorizontalExtent( WND
*wnd
, LB_DESCR
*descr
,
1178 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1180 if (extent
<= 0) extent
= 1;
1181 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1182 TRACE("[%04x]: new horz extent = %d\n",
1183 wnd
->hwndSelf
, extent
);
1184 descr
->horz_extent
= extent
;
1185 if (descr
->horz_pos
> extent
- descr
->width
)
1186 LISTBOX_SetHorizontalPos( wnd
, descr
, extent
- descr
->width
);
1188 LISTBOX_UpdateScroll( wnd
, descr
);
1193 /***********************************************************************
1194 * LISTBOX_SetColumnWidth
1196 static LRESULT
LISTBOX_SetColumnWidth( WND
*wnd
, LB_DESCR
*descr
, UINT width
)
1198 if (width
== descr
->column_width
) return LB_OKAY
;
1199 TRACE("[%04x]: new column width = %d\n",
1200 wnd
->hwndSelf
, width
);
1201 descr
->column_width
= width
;
1202 LISTBOX_UpdatePage( wnd
, descr
);
1207 /***********************************************************************
1210 * Returns the item height.
1212 static INT
LISTBOX_SetFont( WND
*wnd
, LB_DESCR
*descr
, HFONT font
)
1220 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
)))
1222 ERR("unable to get DC.\n" );
1225 if (font
) oldFont
= SelectObject( hdc
, font
);
1226 GetTextMetricsA( hdc
, &tm
);
1227 if (oldFont
) SelectObject( hdc
, oldFont
);
1228 ReleaseDC( wnd
->hwndSelf
, hdc
);
1229 if (!IS_OWNERDRAW(descr
))
1230 LISTBOX_SetItemHeight( wnd
, descr
, 0, tm
.tmHeight
);
1231 return tm
.tmHeight
;
1235 /***********************************************************************
1236 * LISTBOX_MakeItemVisible
1238 * Make sure that a given item is partially or fully visible.
1240 static void LISTBOX_MakeItemVisible( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1245 if (index
<= descr
->top_item
) top
= index
;
1246 else if (descr
->style
& LBS_MULTICOLUMN
)
1248 INT cols
= descr
->width
;
1249 if (!fully
) cols
+= descr
->column_width
- 1;
1250 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1252 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1253 top
= index
- descr
->page_size
* (cols
- 1);
1255 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1257 INT height
= fully
? descr
->items
[index
].height
: 1;
1258 for (top
= index
; top
> descr
->top_item
; top
--)
1259 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1263 if (index
< descr
->top_item
+ descr
->page_size
) return;
1264 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1265 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1266 top
= index
- descr
->page_size
+ 1;
1268 LISTBOX_SetTopItem( wnd
, descr
, top
, TRUE
);
1271 /***********************************************************************
1272 * LISTBOX_SetCaretIndex
1275 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1278 static LRESULT
LISTBOX_SetCaretIndex( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1279 BOOL fully_visible
)
1281 INT oldfocus
= descr
->focus_item
;
1283 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1284 if (index
== oldfocus
) return LB_OKAY
;
1285 descr
->focus_item
= index
;
1286 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1287 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1289 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1290 if (descr
->caret_on
&& (descr
->in_focus
))
1291 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1297 /***********************************************************************
1298 * LISTBOX_SelectItemRange
1300 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1302 static LRESULT
LISTBOX_SelectItemRange( WND
*wnd
, LB_DESCR
*descr
, INT first
,
1307 /* A few sanity checks */
1309 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1310 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1311 if (last
== -1) last
= descr
->nb_items
- 1;
1312 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1313 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1314 /* selected_item reflects last selected/unselected item on multiple sel */
1315 descr
->selected_item
= last
;
1317 if (on
) /* Turn selection on */
1319 for (i
= first
; i
<= last
; i
++)
1321 if (descr
->items
[i
].selected
) continue;
1322 descr
->items
[i
].selected
= TRUE
;
1323 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1325 LISTBOX_SetCaretIndex( wnd
, descr
, last
, TRUE
);
1327 else /* Turn selection off */
1329 for (i
= first
; i
<= last
; i
++)
1331 if (!descr
->items
[i
].selected
) continue;
1332 descr
->items
[i
].selected
= FALSE
;
1333 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1339 /***********************************************************************
1340 * LISTBOX_SetSelection
1342 static LRESULT
LISTBOX_SetSelection( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1343 BOOL on
, BOOL send_notify
)
1345 TRACE( "index=%d notify=%s\n", index
, send_notify
? "YES" : "NO" );
1347 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1348 if (descr
->style
& LBS_MULTIPLESEL
)
1350 if (index
== -1) /* Select all items */
1351 return LISTBOX_SelectItemRange( wnd
, descr
, 0, -1, on
);
1352 else /* Only one item */
1353 return LISTBOX_SelectItemRange( wnd
, descr
, index
, index
, on
);
1357 INT oldsel
= descr
->selected_item
;
1358 if (index
== oldsel
) return LB_OKAY
;
1359 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1360 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1361 descr
->selected_item
= index
;
1362 if (oldsel
!= -1) LISTBOX_RepaintItem( wnd
, descr
, oldsel
, ODA_SELECT
);
1363 if (index
!= -1) LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_SELECT
);
1364 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( wnd
, descr
,
1365 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1367 if( descr
->lphc
) /* set selection change flag for parent combo */
1368 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1374 /***********************************************************************
1377 * Change the caret position and extend the selection to the new caret.
1379 static void LISTBOX_MoveCaret( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1380 BOOL fully_visible
)
1382 INT oldfocus
= descr
->focus_item
;
1384 if ((index
< 0) || (index
>= descr
->nb_items
))
1387 /* Important, repaint needs to be done in this order if
1388 you want to mimic Windows behavior:
1389 1. Remove the focus and paint the item
1390 2. Remove the selection and paint the item(s)
1391 3. Set the selection and repaint the item(s)
1392 4. Set the focus to 'index' and repaint the item */
1394 /* 1. remove the focus and repaint the item */
1395 descr
->focus_item
= -1;
1396 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1397 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1399 /* 2. then turn off the previous selection */
1400 /* 3. repaint the new selected item */
1401 if (descr
->style
& LBS_EXTENDEDSEL
)
1403 if (descr
->anchor_item
!= -1)
1405 INT first
= min( index
, descr
->anchor_item
);
1406 INT last
= max( index
, descr
->anchor_item
);
1408 LISTBOX_SelectItemRange( wnd
, descr
, 0, first
- 1, FALSE
);
1409 LISTBOX_SelectItemRange( wnd
, descr
, last
+ 1, -1, FALSE
);
1410 LISTBOX_SelectItemRange( wnd
, descr
, first
, last
, TRUE
);
1413 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1415 /* Set selection to new caret item */
1416 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
1419 /* 4. repaint the new item with the focus */
1420 descr
->focus_item
= index
;
1421 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1422 if (descr
->caret_on
&& (descr
->in_focus
))
1423 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1427 /***********************************************************************
1428 * LISTBOX_InsertItem
1430 static LRESULT
LISTBOX_InsertItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1431 LPSTR str
, DWORD data
)
1435 INT oldfocus
= descr
->focus_item
;
1437 if (index
== -1) index
= descr
->nb_items
;
1438 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1439 if (!descr
->items
) max_items
= 0;
1440 else max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
1441 if (descr
->nb_items
== max_items
)
1443 /* We need to grow the array */
1444 max_items
+= LB_ARRAY_GRANULARITY
;
1445 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1446 max_items
* sizeof(LB_ITEMDATA
) )))
1448 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1451 descr
->items
= item
;
1454 /* Insert the item structure */
1456 item
= &descr
->items
[index
];
1457 if (index
< descr
->nb_items
)
1458 RtlMoveMemory( item
+ 1, item
,
1459 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1463 item
->selected
= FALSE
;
1466 /* Get item height */
1468 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1470 MEASUREITEMSTRUCT mis
;
1472 mis
.CtlType
= ODT_LISTBOX
;
1473 mis
.CtlID
= wnd
->wIDmenu
;
1475 mis
.itemData
= descr
->items
[index
].data
;
1476 mis
.itemHeight
= descr
->item_height
;
1477 SendMessageA( descr
->owner
, WM_MEASUREITEM
, wnd
->wIDmenu
, (LPARAM
)&mis
);
1478 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1479 TRACE("[%04x]: measure item %d (%s) = %d\n",
1480 wnd
->hwndSelf
, index
, str
? str
: "", item
->height
);
1483 /* Repaint the items */
1485 LISTBOX_UpdateScroll( wnd
, descr
);
1486 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1488 /* Move selection and focused item */
1489 /* If listbox was empty, set focus to the first item */
1490 if (descr
->nb_items
== 1)
1491 LISTBOX_SetCaretIndex( wnd
, descr
, 0, FALSE
);
1492 /* single select don't change selection index in win31 */
1493 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1495 descr
->selected_item
++;
1496 LISTBOX_SetSelection( wnd
, descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1500 if (index
<= descr
->selected_item
)
1502 descr
->selected_item
++;
1503 descr
->focus_item
= oldfocus
; /* focus not changed */
1510 /***********************************************************************
1511 * LISTBOX_InsertString
1513 static LRESULT
LISTBOX_InsertString( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1516 LPSTR new_str
= NULL
;
1520 if (HAS_STRINGS(descr
))
1523 if (!(new_str
= HEAP_strdupA( descr
->heap
, 0, str
)))
1525 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1529 else data
= (DWORD
)str
;
1531 if (index
== -1) index
= descr
->nb_items
;
1532 if ((ret
= LISTBOX_InsertItem( wnd
, descr
, index
, new_str
, data
)) != 0)
1534 if (new_str
) HeapFree( descr
->heap
, 0, new_str
);
1538 TRACE("[%04x]: added item %d '%s'\n",
1539 wnd
->hwndSelf
, index
, HAS_STRINGS(descr
) ? new_str
: "" );
1544 /***********************************************************************
1545 * LISTBOX_DeleteItem
1547 * Delete the content of an item. 'index' must be a valid index.
1549 static void LISTBOX_DeleteItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1551 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1552 * while Win95 sends it for all items with user data.
1553 * It's probably better to send it too often than not
1554 * often enough, so this is what we do here.
1556 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1558 DELETEITEMSTRUCT dis
;
1560 dis
.CtlType
= ODT_LISTBOX
;
1561 dis
.CtlID
= wnd
->wIDmenu
;
1563 dis
.hwndItem
= wnd
->hwndSelf
;
1564 dis
.itemData
= descr
->items
[index
].data
;
1565 SendMessageA( descr
->owner
, WM_DELETEITEM
, wnd
->wIDmenu
, (LPARAM
)&dis
);
1567 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1568 HeapFree( descr
->heap
, 0, descr
->items
[index
].str
);
1572 /***********************************************************************
1573 * LISTBOX_RemoveItem
1575 * Remove an item from the listbox and delete its content.
1577 static LRESULT
LISTBOX_RemoveItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1582 if (index
== -1) index
= descr
->nb_items
- 1;
1583 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1585 /* We need to invalidate the original rect instead of the updated one. */
1586 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1588 LISTBOX_DeleteItem( wnd
, descr
, index
);
1590 /* Remove the item */
1592 item
= &descr
->items
[index
];
1593 if (index
< descr
->nb_items
-1)
1594 RtlMoveMemory( item
, item
+ 1,
1595 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1597 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1599 /* Shrink the item array if possible */
1601 max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1602 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1604 max_items
-= LB_ARRAY_GRANULARITY
;
1605 item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1606 max_items
* sizeof(LB_ITEMDATA
) );
1607 if (item
) descr
->items
= item
;
1609 /* Repaint the items */
1611 LISTBOX_UpdateScroll( wnd
, descr
);
1612 /* if we removed the scrollbar, reset the top of the list
1613 (correct for owner-drawn ???) */
1614 if (descr
->nb_items
== descr
->page_size
)
1615 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1617 /* Move selection and focused item */
1618 if (!IS_MULTISELECT(descr
))
1620 if (index
== descr
->selected_item
)
1621 descr
->selected_item
= -1;
1622 else if (index
< descr
->selected_item
)
1624 descr
->selected_item
--;
1625 if (ISWIN31
) /* win 31 do not change the selected item number */
1626 LISTBOX_SetSelection( wnd
, descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1630 if (descr
->focus_item
>= descr
->nb_items
)
1632 descr
->focus_item
= descr
->nb_items
- 1;
1633 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1639 /***********************************************************************
1640 * LISTBOX_ResetContent
1642 static void LISTBOX_ResetContent( WND
*wnd
, LB_DESCR
*descr
)
1646 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( wnd
, descr
, i
);
1647 if (descr
->items
) HeapFree( descr
->heap
, 0, descr
->items
);
1648 descr
->nb_items
= 0;
1649 descr
->top_item
= 0;
1650 descr
->selected_item
= -1;
1651 descr
->focus_item
= 0;
1652 descr
->anchor_item
= -1;
1653 descr
->items
= NULL
;
1657 /***********************************************************************
1660 static LRESULT
LISTBOX_SetCount( WND
*wnd
, LB_DESCR
*descr
, INT count
)
1664 if (HAS_STRINGS(descr
)) return LB_ERR
;
1665 /* FIXME: this is far from optimal... */
1666 if (count
> descr
->nb_items
)
1668 while (count
> descr
->nb_items
)
1669 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, 0 )) < 0)
1672 else if (count
< descr
->nb_items
)
1674 while (count
< descr
->nb_items
)
1675 if ((ret
= LISTBOX_RemoveItem( wnd
, descr
, -1 )) < 0)
1682 /***********************************************************************
1685 static LRESULT
LISTBOX_Directory( WND
*wnd
, LB_DESCR
*descr
, UINT attrib
,
1686 LPCSTR filespec
, BOOL long_names
)
1689 LRESULT ret
= LB_OKAY
;
1690 WIN32_FIND_DATAA entry
;
1693 /* don't scan directory if we just want drives exclusively */
1694 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1695 /* scan directory */
1696 if ((handle
= FindFirstFileA(filespec
,&entry
)) == INVALID_HANDLE_VALUE
)
1698 if (GetLastError() != ERROR_NO_MORE_FILES
) return LB_ERR
;
1705 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1707 if (!(attrib
& DDL_DIRECTORY
) ||
1708 !strcmp( entry
.cAlternateFileName
, "." )) continue;
1709 if (long_names
) sprintf( buffer
, "[%s]", entry
.cFileName
);
1710 else sprintf( buffer
, "[%s]", entry
.cAlternateFileName
);
1712 else /* not a directory */
1714 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1715 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1717 if ((attrib
& DDL_EXCLUSIVE
) &&
1718 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1721 if (long_names
) strcpy( buffer
, entry
.cFileName
);
1722 else strcpy( buffer
, entry
.cAlternateFileName
);
1724 if (!long_names
) CharLowerA( buffer
);
1725 pos
= LISTBOX_FindFileStrPos( wnd
, descr
, buffer
);
1726 if ((ret
= LISTBOX_InsertString( wnd
, descr
, pos
, buffer
)) < 0)
1728 } while (FindNextFileA( handle
, &entry
));
1729 FindClose( handle
);
1734 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1736 char buffer
[] = "[-a-]";
1737 char root
[] = "A:\\";
1739 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1741 if (GetDriveTypeA(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1742 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, buffer
)) < 0)
1750 /***********************************************************************
1751 * LISTBOX_HandleVScroll
1753 static LRESULT
LISTBOX_HandleVScroll( WND
*wnd
, LB_DESCR
*descr
,
1754 WPARAM wParam
, LPARAM lParam
)
1758 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1759 switch(LOWORD(wParam
))
1762 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
- 1, TRUE
);
1765 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ 1, TRUE
);
1768 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-
1769 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1772 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+
1773 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1775 case SB_THUMBPOSITION
:
1776 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
), TRUE
);
1779 info
.cbSize
= sizeof(info
);
1780 info
.fMask
= SIF_TRACKPOS
;
1781 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1782 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
, TRUE
);
1785 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1788 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1795 /***********************************************************************
1796 * LISTBOX_HandleHScroll
1798 static LRESULT
LISTBOX_HandleHScroll( WND
*wnd
, LB_DESCR
*descr
,
1799 WPARAM wParam
, LPARAM lParam
)
1804 if (descr
->style
& LBS_MULTICOLUMN
)
1806 switch(LOWORD(wParam
))
1809 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-descr
->page_size
,
1813 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+descr
->page_size
,
1817 page
= descr
->width
/ descr
->column_width
;
1818 if (page
< 1) page
= 1;
1819 LISTBOX_SetTopItem( wnd
, descr
,
1820 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1823 page
= descr
->width
/ descr
->column_width
;
1824 if (page
< 1) page
= 1;
1825 LISTBOX_SetTopItem( wnd
, descr
,
1826 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1828 case SB_THUMBPOSITION
:
1829 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1833 info
.cbSize
= sizeof(info
);
1834 info
.fMask
= SIF_TRACKPOS
;
1835 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1836 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1840 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1843 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1847 else if (descr
->horz_extent
)
1849 switch(LOWORD(wParam
))
1852 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
- 1 );
1855 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
+ 1 );
1858 LISTBOX_SetHorizontalPos( wnd
, descr
,
1859 descr
->horz_pos
- descr
->width
);
1862 LISTBOX_SetHorizontalPos( wnd
, descr
,
1863 descr
->horz_pos
+ descr
->width
);
1865 case SB_THUMBPOSITION
:
1866 LISTBOX_SetHorizontalPos( wnd
, descr
, HIWORD(wParam
) );
1869 info
.cbSize
= sizeof(info
);
1870 info
.fMask
= SIF_TRACKPOS
;
1871 GetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
);
1872 LISTBOX_SetHorizontalPos( wnd
, descr
, info
.nTrackPos
);
1875 LISTBOX_SetHorizontalPos( wnd
, descr
, 0 );
1878 LISTBOX_SetHorizontalPos( wnd
, descr
,
1879 descr
->horz_extent
- descr
->width
);
1886 static LRESULT
LISTBOX_HandleMouseWheel(WND
*wnd
, LB_DESCR
*descr
,WPARAM wParam
, LPARAM lParam
)
1888 short gcWheelDelta
= 0;
1889 UINT pulScrollLines
= 3;
1891 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1893 gcWheelDelta
-= (short) HIWORD(wParam
);
1895 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
1897 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
1898 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
1899 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ cLineScroll
, TRUE
);
1904 /***********************************************************************
1905 * LISTBOX_HandleLButtonDown
1907 static LRESULT
LISTBOX_HandleLButtonDown( WND
*wnd
, LB_DESCR
*descr
,
1908 WPARAM wParam
, INT x
, INT y
)
1910 INT index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1911 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1912 wnd
->hwndSelf
, x
, y
, index
);
1913 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
1915 if (!descr
->in_focus
)
1917 if( !descr
->lphc
) SetFocus( wnd
->hwndSelf
);
1918 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
1919 : descr
->lphc
->self
->hwndSelf
);
1924 if (descr
->style
& LBS_EXTENDEDSEL
)
1926 /* we should perhaps make sure that all items are deselected
1927 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1928 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1929 LISTBOX_SetSelection( wnd, descr, -1, FALSE, FALSE);
1932 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1933 if (wParam
& MK_CONTROL
)
1935 LISTBOX_SetCaretIndex( wnd
, descr
, index
, FALSE
);
1936 LISTBOX_SetSelection( wnd
, descr
, index
,
1937 !descr
->items
[index
].selected
,
1938 (descr
->style
& LBS_NOTIFY
) != 0);
1940 else LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1944 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1945 LISTBOX_SetSelection( wnd
, descr
, index
,
1946 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1947 !descr
->items
[index
].selected
),
1948 (descr
->style
& LBS_NOTIFY
) != 0 );
1952 descr
->captured
= TRUE
;
1953 SetCapture( wnd
->hwndSelf
);
1954 if (index
!= -1 && !descr
->lphc
)
1956 if (descr
->style
& LBS_NOTIFY
)
1957 SendMessageA( descr
->owner
, WM_LBTRACKPOINT
, index
,
1958 MAKELPARAM( x
, y
) );
1959 if (wnd
->dwExStyle
& WS_EX_DRAGDETECT
)
1966 if (DragDetect( wnd
->hwndSelf
, pt
))
1967 SendMessageA( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
1974 /*************************************************************************
1975 * LISTBOX_HandleLButtonDownCombo [Internal]
1977 * Process LButtonDown message for the ComboListBox
1980 * pWnd [I] The windows internal structure
1981 * pDescr [I] The ListBox internal structure
1982 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
1983 * x [I] X Mouse Coordinate
1984 * y [I] Y Mouse Coordinate
1987 * 0 since we are processing the WM_LBUTTONDOWN Message
1990 * This function is only to be used when a ListBox is a ComboListBox
1993 static LRESULT
LISTBOX_HandleLButtonDownCombo( WND
*pWnd
, LB_DESCR
*pDescr
,
1994 UINT msg
, WPARAM wParam
, INT x
, INT y
)
1996 RECT clientRect
, screenRect
;
2002 GetClientRect(pWnd
->hwndSelf
, &clientRect
);
2004 if(PtInRect(&clientRect
, mousePos
))
2006 /* MousePos is in client, resume normal processing */
2007 if (msg
== WM_LBUTTONDOWN
)
2009 pDescr
->lphc
->droppedIndex
= pDescr
->nb_items
? pDescr
->selected_item
: -1;
2010 return LISTBOX_HandleLButtonDown( pWnd
, pDescr
, wParam
, x
, y
);
2012 else if (pDescr
->style
& LBS_NOTIFY
)
2013 SEND_NOTIFICATION( pWnd
, pDescr
, LBN_DBLCLK
);
2018 POINT screenMousePos
;
2019 HWND hWndOldCapture
;
2021 /* Check the Non-Client Area */
2022 screenMousePos
= mousePos
;
2023 hWndOldCapture
= GetCapture();
2025 GetWindowRect(pWnd
->hwndSelf
, &screenRect
);
2026 ClientToScreen(pWnd
->hwndSelf
, &screenMousePos
);
2028 if(!PtInRect(&screenRect
, screenMousePos
))
2030 LISTBOX_SetSelection( pWnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2031 COMBO_FlipListbox( pDescr
->lphc
, FALSE
, FALSE
);
2036 /* Check to see the NC is a scrollbar */
2038 /* Check Vertical scroll bar */
2039 if (pWnd
->dwStyle
& WS_VSCROLL
)
2041 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2042 if (PtInRect( &clientRect
, mousePos
))
2044 nHitTestType
= HTVSCROLL
;
2047 /* Check horizontal scroll bar */
2048 if (pWnd
->dwStyle
& WS_HSCROLL
)
2050 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2051 if (PtInRect( &clientRect
, mousePos
))
2053 nHitTestType
= HTHSCROLL
;
2056 /* Windows sends this message when a scrollbar is clicked
2059 if(nHitTestType
!= 0)
2061 SendMessageA(pWnd
->hwndSelf
, WM_NCLBUTTONDOWN
, nHitTestType
,
2062 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2064 /* Resume the Capture after scrolling is complete
2066 if(hWndOldCapture
!= 0)
2068 SetCapture(hWndOldCapture
);
2075 /***********************************************************************
2076 * LISTBOX_HandleLButtonUp
2078 static LRESULT
LISTBOX_HandleLButtonUp( WND
*wnd
, LB_DESCR
*descr
)
2080 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2081 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2082 LISTBOX_Timer
= LB_TIMER_NONE
;
2083 if (descr
->captured
)
2085 descr
->captured
= FALSE
;
2086 if (GetCapture() == wnd
->hwndSelf
) ReleaseCapture();
2087 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2088 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2094 /***********************************************************************
2095 * LISTBOX_HandleTimer
2097 * Handle scrolling upon a timer event.
2098 * Return TRUE if scrolling should continue.
2100 static LRESULT
LISTBOX_HandleTimer( WND
*wnd
, LB_DESCR
*descr
,
2101 INT index
, TIMER_DIRECTION dir
)
2106 if (descr
->top_item
) index
= descr
->top_item
- 1;
2110 if (descr
->top_item
) index
-= descr
->page_size
;
2113 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( wnd
, descr
);
2114 if (index
== descr
->focus_item
) index
++;
2115 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2117 case LB_TIMER_RIGHT
:
2118 if (index
+ descr
->page_size
< descr
->nb_items
)
2119 index
+= descr
->page_size
;
2124 if (index
== descr
->focus_item
) return FALSE
;
2125 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
2130 /***********************************************************************
2131 * LISTBOX_HandleSystemTimer
2133 * WM_SYSTIMER handler.
2135 static LRESULT
LISTBOX_HandleSystemTimer( WND
*wnd
, LB_DESCR
*descr
)
2137 if (!LISTBOX_HandleTimer( wnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
2139 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2140 LISTBOX_Timer
= LB_TIMER_NONE
;
2146 /***********************************************************************
2147 * LISTBOX_HandleMouseMove
2149 * WM_MOUSEMOVE handler.
2151 static void LISTBOX_HandleMouseMove( WND
*wnd
, LB_DESCR
*descr
,
2155 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2157 if (!descr
->captured
) return;
2159 if (descr
->style
& LBS_MULTICOLUMN
)
2162 else if (y
>= descr
->item_height
* descr
->page_size
)
2163 y
= descr
->item_height
* descr
->page_size
- 1;
2167 dir
= LB_TIMER_LEFT
;
2170 else if (x
>= descr
->width
)
2172 dir
= LB_TIMER_RIGHT
;
2173 x
= descr
->width
- 1;
2178 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2179 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2182 index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
2183 if (index
== -1) index
= descr
->focus_item
;
2184 if (!LISTBOX_HandleTimer( wnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2186 /* Start/stop the system timer */
2188 if (dir
!= LB_TIMER_NONE
)
2189 SetSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2190 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2191 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2192 LISTBOX_Timer
= dir
;
2196 /***********************************************************************
2197 * LISTBOX_HandleKeyDown
2199 static LRESULT
LISTBOX_HandleKeyDown( WND
*wnd
, LB_DESCR
*descr
, WPARAM wParam
)
2202 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2203 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2204 bForceSelection
= FALSE
; /* only for single select list */
2206 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2208 caret
= SendMessageA( descr
->owner
, WM_VKEYTOITEM
,
2209 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2211 if (caret
== -2) return 0;
2213 if (caret
== -1) switch(wParam
)
2216 if (descr
->style
& LBS_MULTICOLUMN
)
2218 bForceSelection
= FALSE
;
2219 if (descr
->focus_item
>= descr
->page_size
)
2220 caret
= descr
->focus_item
- descr
->page_size
;
2225 caret
= descr
->focus_item
- 1;
2226 if (caret
< 0) caret
= 0;
2229 if (descr
->style
& LBS_MULTICOLUMN
)
2231 bForceSelection
= FALSE
;
2232 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2233 caret
= descr
->focus_item
+ descr
->page_size
;
2238 caret
= descr
->focus_item
+ 1;
2239 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2243 if (descr
->style
& LBS_MULTICOLUMN
)
2245 INT page
= descr
->width
/ descr
->column_width
;
2246 if (page
< 1) page
= 1;
2247 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2249 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(wnd
,descr
)+1;
2250 if (caret
< 0) caret
= 0;
2253 if (descr
->style
& LBS_MULTICOLUMN
)
2255 INT page
= descr
->width
/ descr
->column_width
;
2256 if (page
< 1) page
= 1;
2257 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2259 else caret
= descr
->focus_item
+LISTBOX_GetCurrentPageSize(wnd
,descr
)-1;
2260 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2266 caret
= descr
->nb_items
- 1;
2269 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2270 else if (descr
->style
& LBS_MULTIPLESEL
)
2272 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
,
2273 !descr
->items
[descr
->focus_item
].selected
,
2274 (descr
->style
& LBS_NOTIFY
) != 0 );
2278 bForceSelection
= FALSE
;
2280 if (bForceSelection
) /* focused item is used instead of key */
2281 caret
= descr
->focus_item
;
2284 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
2285 !(GetKeyState( VK_SHIFT
) & 0x8000))
2286 descr
->anchor_item
= caret
;
2287 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2288 LISTBOX_SetSelection( wnd
, descr
, caret
, TRUE
, FALSE
);
2289 if (descr
->style
& LBS_NOTIFY
)
2291 if( descr
->lphc
&& CB_GETTYPE(descr
->lphc
) != CBS_SIMPLE
)
2293 /* make sure that combo parent doesn't hide us */
2294 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2296 if (descr
->nb_items
) SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2303 /***********************************************************************
2304 * LISTBOX_HandleChar
2306 static LRESULT
LISTBOX_HandleChar( WND
*wnd
, LB_DESCR
*descr
,
2312 str
[0] = wParam
& 0xff;
2315 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2317 caret
= SendMessageA( descr
->owner
, WM_CHARTOITEM
,
2318 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2320 if (caret
== -2) return 0;
2323 caret
= LISTBOX_FindString( wnd
, descr
, descr
->focus_item
, str
, FALSE
);
2326 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2327 LISTBOX_SetSelection( wnd
, descr
, caret
, TRUE
, FALSE
);
2328 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2329 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2330 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2336 /***********************************************************************
2339 static BOOL
LISTBOX_Create( WND
*wnd
, LPHEADCOMBO lphc
)
2342 MEASUREITEMSTRUCT mis
;
2345 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2347 if (!(descr
->heap
= HeapCreate( 0, 0x10000, 0 )))
2349 HeapFree( GetProcessHeap(), 0, descr
);
2352 GetClientRect( wnd
->hwndSelf
, &rect
);
2353 descr
->owner
= GetParent( wnd
->hwndSelf
);
2354 descr
->style
= wnd
->dwStyle
;
2355 descr
->width
= rect
.right
- rect
.left
;
2356 descr
->height
= rect
.bottom
- rect
.top
;
2357 descr
->items
= NULL
;
2358 descr
->nb_items
= 0;
2359 descr
->top_item
= 0;
2360 descr
->selected_item
= -1;
2361 descr
->focus_item
= 0;
2362 descr
->anchor_item
= -1;
2363 descr
->item_height
= 1;
2364 descr
->page_size
= 1;
2365 descr
->column_width
= 150;
2366 descr
->horz_extent
= (wnd
->dwStyle
& WS_HSCROLL
) ? 1 : 0;
2367 descr
->horz_pos
= 0;
2370 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2371 descr
->in_focus
= FALSE
;
2372 descr
->captured
= FALSE
;
2374 descr
->locale
= 0; /* FIXME */
2377 if( ( GetExpWinVer16( wnd
->hInstance
) & 0xFF00 ) == 0x0300
2378 && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2380 /* Win95 document "List Box Differences" from MSDN:
2381 If a list box in a version 3.x application has either the
2382 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2383 horizontal and vertical scroll bars.
2385 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2390 TRACE_(combo
)("[%04x]: resetting owner %04x -> %04x\n",
2391 wnd
->hwndSelf
, descr
->owner
, lphc
->self
->hwndSelf
);
2392 descr
->owner
= lphc
->self
->hwndSelf
;
2395 *(LB_DESCR
**)wnd
->wExtra
= descr
;
2397 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2399 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2400 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2401 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2402 descr
->item_height
= LISTBOX_SetFont( wnd
, descr
, 0 );
2404 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2406 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2408 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2409 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2413 mis
.CtlType
= ODT_LISTBOX
;
2414 mis
.CtlID
= wnd
->wIDmenu
;
2418 mis
.itemHeight
= descr
->item_height
;
2419 SendMessageA( descr
->owner
, WM_MEASUREITEM
, wnd
->wIDmenu
, (LPARAM
)&mis
);
2420 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2428 /***********************************************************************
2431 static BOOL
LISTBOX_Destroy( WND
*wnd
, LB_DESCR
*descr
)
2433 LISTBOX_ResetContent( wnd
, descr
);
2434 HeapDestroy( descr
->heap
);
2435 HeapFree( GetProcessHeap(), 0, descr
);
2441 /***********************************************************************
2444 static inline LRESULT WINAPI
ListBoxWndProc_locked( WND
* wnd
, UINT msg
,
2445 WPARAM wParam
, LPARAM lParam
)
2449 HWND hwnd
= wnd
->hwndSelf
;
2452 if (!(descr
= *(LB_DESCR
**)wnd
->wExtra
))
2458 if (!LISTBOX_Create( wnd
, NULL
))
2460 TRACE("creating wnd=%04x descr=%p\n",
2461 hwnd
, *(LB_DESCR
**)wnd
->wExtra
);
2467 * When a listbox is not in a combobox and the look
2468 * is win95, the WS_BORDER style is replaced with
2469 * the WS_EX_CLIENTEDGE style.
2471 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
2472 (wnd
->dwStyle
& WS_BORDER
) )
2474 wnd
->dwExStyle
|= WS_EX_CLIENTEDGE
;
2475 wnd
->dwStyle
&= ~ WS_BORDER
;
2480 /* Ignore all other messages before we get a WM_CREATE */
2481 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2484 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2485 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2488 case LB_RESETCONTENT16
:
2489 case LB_RESETCONTENT
:
2490 LISTBOX_ResetContent( wnd
, descr
);
2491 LISTBOX_UpdateScroll( wnd
, descr
);
2492 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
2495 case LB_ADDSTRING16
:
2496 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2499 wParam
= LISTBOX_FindStringPos( wnd
, descr
, (LPCSTR
)lParam
, FALSE
);
2500 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2502 case LB_INSERTSTRING16
:
2503 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2504 wParam
= (INT
)(INT16
)wParam
;
2506 case LB_INSERTSTRING
:
2507 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2510 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2513 wParam
= LISTBOX_FindFileStrPos( wnd
, descr
, (LPCSTR
)lParam
);
2514 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2516 case LB_DELETESTRING16
:
2517 case LB_DELETESTRING
:
2518 if (LISTBOX_RemoveItem( wnd
, descr
, wParam
) != LB_ERR
)
2519 return descr
->nb_items
;
2523 case LB_GETITEMDATA16
:
2524 case LB_GETITEMDATA
:
2525 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2527 return descr
->items
[wParam
].data
;
2529 case LB_SETITEMDATA16
:
2530 case LB_SETITEMDATA
:
2531 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2533 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2538 return descr
->nb_items
;
2541 lParam
= (LPARAM
)MapSL(lParam
);
2544 return LISTBOX_GetText( wnd
, descr
, wParam
, (LPSTR
)lParam
);
2546 case LB_GETTEXTLEN16
:
2549 if (wParam
>= descr
->nb_items
)
2551 return (HAS_STRINGS(descr
) ? strlen(descr
->items
[wParam
].str
)
2554 case LB_GETCURSEL16
:
2556 if (descr
->nb_items
==0)
2558 if (!IS_MULTISELECT(descr
))
2559 return descr
->selected_item
;
2561 if (descr
->selected_item
!=-1)
2562 return descr
->selected_item
;
2564 return descr
->focus_item
;
2565 /* otherwise, if the user tries to move the selection with the */
2566 /* arrow keys, we will give the application something to choke on */
2567 case LB_GETTOPINDEX16
:
2568 case LB_GETTOPINDEX
:
2569 return descr
->top_item
;
2571 case LB_GETITEMHEIGHT16
:
2572 case LB_GETITEMHEIGHT
:
2573 return LISTBOX_GetItemHeight( wnd
, descr
, wParam
);
2575 case LB_SETITEMHEIGHT16
:
2576 lParam
= LOWORD(lParam
);
2578 case LB_SETITEMHEIGHT
:
2579 return LISTBOX_SetItemHeight( wnd
, descr
, wParam
, lParam
);
2581 case LB_ITEMFROMPOINT
:
2586 pt
.x
= LOWORD(lParam
);
2587 pt
.y
= HIWORD(lParam
);
2590 rect
.right
= descr
->width
;
2591 rect
.bottom
= descr
->height
;
2593 return MAKELONG( LISTBOX_GetItemFromPoint(wnd
, descr
, pt
.x
, pt
.y
),
2594 !PtInRect( &rect
, pt
) );
2597 case LB_SETCARETINDEX16
:
2598 case LB_SETCARETINDEX
:
2599 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2600 if (LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, !lParam
) == LB_ERR
)
2607 case LB_GETCARETINDEX16
:
2608 case LB_GETCARETINDEX
:
2609 return descr
->focus_item
;
2611 case LB_SETTOPINDEX16
:
2612 case LB_SETTOPINDEX
:
2613 return LISTBOX_SetTopItem( wnd
, descr
, wParam
, TRUE
);
2615 case LB_SETCOLUMNWIDTH16
:
2616 case LB_SETCOLUMNWIDTH
:
2617 return LISTBOX_SetColumnWidth( wnd
, descr
, wParam
);
2619 case LB_GETITEMRECT16
:
2622 ret
= LISTBOX_GetItemRect( wnd
, descr
, (INT16
)wParam
, &rect
);
2623 CONV_RECT32TO16( &rect
, MapSL(lParam
) );
2627 case LB_GETITEMRECT
:
2628 return LISTBOX_GetItemRect( wnd
, descr
, wParam
, (RECT
*)lParam
);
2630 case LB_FINDSTRING16
:
2631 wParam
= (INT
)(INT16
)wParam
;
2632 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2635 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, FALSE
);
2637 case LB_FINDSTRINGEXACT16
:
2638 wParam
= (INT
)(INT16
)wParam
;
2639 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2641 case LB_FINDSTRINGEXACT
:
2642 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2644 case LB_SELECTSTRING16
:
2645 wParam
= (INT
)(INT16
)wParam
;
2646 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2648 case LB_SELECTSTRING
:
2650 INT index
= LISTBOX_FindString( wnd
, descr
, wParam
,
2651 (LPCSTR
)lParam
, FALSE
);
2652 if (index
== LB_ERR
)
2654 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
2659 wParam
= (INT
)(INT16
)wParam
;
2662 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2664 return descr
->items
[wParam
].selected
;
2667 lParam
= (INT
)(INT16
)lParam
;
2670 return LISTBOX_SetSelection( wnd
, descr
, lParam
, wParam
, FALSE
);
2672 case LB_SETCURSEL16
:
2673 wParam
= (INT
)(INT16
)wParam
;
2676 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2677 LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, TRUE
);
2678 return LISTBOX_SetSelection( wnd
, descr
, wParam
, TRUE
, FALSE
);
2680 case LB_GETSELCOUNT16
:
2681 case LB_GETSELCOUNT
:
2682 return LISTBOX_GetSelCount( wnd
, descr
);
2684 case LB_GETSELITEMS16
:
2685 return LISTBOX_GetSelItems16( wnd
, descr
, wParam
,
2686 (LPINT16
)MapSL(lParam
) );
2688 case LB_GETSELITEMS
:
2689 return LISTBOX_GetSelItems( wnd
, descr
, wParam
, (LPINT
)lParam
);
2691 case LB_SELITEMRANGE16
:
2692 case LB_SELITEMRANGE
:
2693 if (LOWORD(lParam
) <= HIWORD(lParam
))
2694 return LISTBOX_SelectItemRange( wnd
, descr
, LOWORD(lParam
),
2695 HIWORD(lParam
), wParam
);
2697 return LISTBOX_SelectItemRange( wnd
, descr
, HIWORD(lParam
),
2698 LOWORD(lParam
), wParam
);
2700 case LB_SELITEMRANGEEX16
:
2701 case LB_SELITEMRANGEEX
:
2702 if ((INT
)lParam
>= (INT
)wParam
)
2703 return LISTBOX_SelectItemRange( wnd
, descr
, wParam
, lParam
, TRUE
);
2705 return LISTBOX_SelectItemRange( wnd
, descr
, lParam
, wParam
, FALSE
);
2707 case LB_GETHORIZONTALEXTENT16
:
2708 case LB_GETHORIZONTALEXTENT
:
2709 return descr
->horz_extent
;
2711 case LB_SETHORIZONTALEXTENT16
:
2712 case LB_SETHORIZONTALEXTENT
:
2713 return LISTBOX_SetHorizontalExtent( wnd
, descr
, wParam
);
2715 case LB_GETANCHORINDEX16
:
2716 case LB_GETANCHORINDEX
:
2717 return descr
->anchor_item
;
2719 case LB_SETANCHORINDEX16
:
2720 wParam
= (INT
)(INT16
)wParam
;
2722 case LB_SETANCHORINDEX
:
2723 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2725 descr
->anchor_item
= (INT
)wParam
;
2729 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2730 * be set automatically (this is different in Win32) */
2731 if (wParam
& DDL_DRIVES
) wParam
|= DDL_EXCLUSIVE
;
2732 return LISTBOX_Directory( wnd
, descr
, wParam
, MapSL(lParam
), FALSE
);
2735 return LISTBOX_Directory( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2738 return descr
->locale
;
2741 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2744 case LB_INITSTORAGE
:
2745 return LISTBOX_InitStorage( wnd
, descr
, wParam
, (DWORD
)lParam
);
2748 return LISTBOX_SetCount( wnd
, descr
, (INT
)wParam
);
2750 case LB_SETTABSTOPS16
:
2751 return LISTBOX_SetTabStops( wnd
, descr
, (INT
)(INT16
)wParam
, MapSL(lParam
), TRUE
);
2753 case LB_SETTABSTOPS
:
2754 return LISTBOX_SetTabStops( wnd
, descr
, wParam
, (LPINT
)lParam
, FALSE
);
2758 if (descr
->caret_on
)
2760 descr
->caret_on
= TRUE
;
2761 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2762 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2767 if (!descr
->caret_on
)
2769 descr
->caret_on
= FALSE
;
2770 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2771 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2775 return LISTBOX_Destroy( wnd
, descr
);
2778 InvalidateRect( hwnd
, NULL
, TRUE
);
2782 LISTBOX_SetRedraw( wnd
, descr
, wParam
!= 0 );
2786 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2791 HDC hdc
= ( wParam
) ? ((HDC
)wParam
)
2792 : BeginPaint( hwnd
, &ps
);
2793 ret
= LISTBOX_Paint( wnd
, descr
, hdc
);
2794 if( !wParam
) EndPaint( hwnd
, &ps
);
2798 LISTBOX_UpdateSize( wnd
, descr
);
2803 LISTBOX_SetFont( wnd
, descr
, (HFONT
)wParam
);
2804 if (lParam
) InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
2807 descr
->in_focus
= TRUE
;
2808 descr
->caret_on
= TRUE
;
2809 if (descr
->focus_item
!= -1)
2810 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2811 SEND_NOTIFICATION( wnd
, descr
, LBN_SETFOCUS
);
2814 descr
->in_focus
= FALSE
;
2815 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2816 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2817 SEND_NOTIFICATION( wnd
, descr
, LBN_KILLFOCUS
);
2820 return LISTBOX_HandleHScroll( wnd
, descr
, wParam
, lParam
);
2822 return LISTBOX_HandleVScroll( wnd
, descr
, wParam
, lParam
);
2823 case WM_MOUSEACTIVATE
:
2824 return MA_NOACTIVATE
;
2826 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2827 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2828 return LISTBOX_HandleMouseWheel( wnd
, descr
, wParam
, lParam
);
2829 case WM_LBUTTONDOWN
:
2830 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2831 (INT16
)LOWORD(lParam
),
2832 (INT16
)HIWORD(lParam
) );
2833 case WM_LBUTTONDBLCLK
:
2834 if (descr
->style
& LBS_NOTIFY
)
2835 SEND_NOTIFICATION( wnd
, descr
, LBN_DBLCLK
);
2838 if (GetCapture() == hwnd
)
2839 LISTBOX_HandleMouseMove( wnd
, descr
, (INT16
)LOWORD(lParam
),
2840 (INT16
)HIWORD(lParam
) );
2843 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2845 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2847 return LISTBOX_HandleChar( wnd
, descr
, wParam
);
2849 return LISTBOX_HandleSystemTimer( wnd
, descr
);
2851 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
2854 HBRUSH hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
2855 wParam
, (LPARAM
)wnd
->hwndSelf
);
2856 GetClientRect(hwnd
, &rect
);
2857 if (hbrush
) FillRect( (HDC
)wParam
, &rect
, hbrush
);
2862 return SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2866 case WM_QUERYDROPOBJECT
:
2871 LPDRAGINFO16 dragInfo
= MapSL( lParam
);
2872 dragInfo
->l
= LISTBOX_GetItemFromPoint( wnd
, descr
, dragInfo
->pt
.x
,
2874 return SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2879 if ((msg
>= WM_USER
) && (msg
< 0xc000))
2880 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2881 hwnd
, msg
, wParam
, lParam
);
2882 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2887 /***********************************************************************
2890 * This is just a wrapper for the real wndproc, it only does window locking
2893 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
2895 WND
* wndPtr
= WIN_FindWndPtr( hwnd
);
2896 LRESULT res
= ListBoxWndProc_locked(wndPtr
,msg
,wParam
,lParam
);
2898 WIN_ReleaseWndPtr(wndPtr
);
2902 /***********************************************************************
2905 LRESULT
COMBO_Directory( LPHEADCOMBO lphc
, UINT attrib
, LPSTR dir
, BOOL bLong
)
2907 WND
*wnd
= WIN_FindWndPtr( lphc
->hWndLBox
);
2911 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2914 LRESULT lRet
= LISTBOX_Directory( wnd
, descr
, attrib
, dir
, bLong
);
2916 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0,
2917 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
2918 WIN_ReleaseWndPtr(wnd
);
2921 WIN_ReleaseWndPtr(wnd
);
2926 /***********************************************************************
2927 * ComboLBWndProc_locked
2929 * The real combo listbox wndproc, but called with locked WND struct.
2931 static inline LRESULT WINAPI
ComboLBWndProc_locked( WND
* wnd
, UINT msg
,
2932 WPARAM wParam
, LPARAM lParam
)
2935 HWND hwnd
= wnd
->hwndSelf
;
2939 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2941 TRACE_(combo
)("[%04x]: msg %s wp %08x lp %08lx\n",
2942 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2944 if( descr
|| msg
== WM_CREATE
)
2946 LPHEADCOMBO lphc
= (descr
) ? descr
->lphc
: NULL
;
2951 #define lpcs ((LPCREATESTRUCTA)lParam)
2952 TRACE_(combo
)("\tpassed parent handle = 0x%08x\n",
2953 (UINT
)lpcs
->lpCreateParams
);
2955 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
2957 return LISTBOX_Create( wnd
, lphc
);
2959 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
2960 (CB_GETTYPE(lphc
) != CBS_SIMPLE
) )
2966 mousePos
.x
= (INT16
)LOWORD(lParam
);
2967 mousePos
.y
= (INT16
)HIWORD(lParam
);
2970 * If we are in a dropdown combobox, we simulate that
2971 * the mouse is captured to show the tracking of the item.
2973 GetClientRect(hwnd
, &clientRect
);
2975 if (PtInRect( &clientRect
, mousePos
))
2977 captured
= descr
->captured
;
2978 descr
->captured
= TRUE
;
2980 LISTBOX_HandleMouseMove( wnd
, descr
,
2981 mousePos
.x
, mousePos
.y
);
2983 descr
->captured
= captured
;
2988 LISTBOX_HandleMouseMove( wnd
, descr
,
2989 mousePos
.x
, mousePos
.y
);
2998 * If we are in Win3.1 look, go with the default behavior.
3000 return ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3003 if (TWEAK_WineLook
> WIN31_LOOK
)
3009 * If the mouse button "up" is not in the listbox,
3010 * we make sure there is no selection by re-selecting the
3011 * item that was selected when the listbox was made visible.
3013 mousePos
.x
= (INT16
)LOWORD(lParam
);
3014 mousePos
.y
= (INT16
)HIWORD(lParam
);
3016 GetClientRect(hwnd
, &clientRect
);
3019 * When the user clicks outside the combobox and the focus
3020 * is lost, the owning combobox will send a fake buttonup with
3021 * 0xFFFFFFF as the mouse location, we must also revert the
3022 * selection to the original selection.
3024 if ( (lParam
== 0xFFFFFFFF) ||
3025 (!PtInRect( &clientRect
, mousePos
)) )
3027 LISTBOX_MoveCaret( wnd
,
3033 return LISTBOX_HandleLButtonUp( wnd
, descr
);
3034 case WM_LBUTTONDBLCLK
:
3035 case WM_LBUTTONDOWN
:
3036 return LISTBOX_HandleLButtonDownCombo(wnd
, descr
, msg
, wParam
,
3037 (INT16
)LOWORD(lParam
),
3038 (INT16
)HIWORD(lParam
) );
3039 case WM_MOUSEACTIVATE
:
3040 return MA_NOACTIVATE
;
3044 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3046 /* for some reason(?) Windows makes it possible to
3047 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3049 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3050 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3051 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3053 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3057 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
3059 case LB_SETCURSEL16
:
3061 lRet
= ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3062 lRet
=(lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
3065 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3070 return ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3073 lRet
= DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3075 TRACE_(combo
)("\t default on msg [%04x]\n", (UINT16
)msg
);
3080 /***********************************************************************
3083 * NOTE: in Windows, winproc address of the ComboLBox is the same
3084 * as that of the Listbox.
3086 * This is just a wrapper for the real wndproc, it only does window locking
3089 LRESULT WINAPI
ComboLBWndProcA( HWND hwnd
, UINT msg
,
3090 WPARAM wParam
, LPARAM lParam
)
3092 WND
*wnd
= WIN_FindWndPtr( hwnd
);
3093 LRESULT res
= ComboLBWndProc_locked(wnd
,msg
,wParam
,lParam
);
3095 WIN_ReleaseWndPtr(wnd
);