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
);
115 static LRESULT
LISTBOX_GetItemRect( WND
*wnd
, LB_DESCR
*descr
, INT index
,
118 /*********************************************************************
119 * listbox class descriptor
121 const struct builtin_class_descr LISTBOX_builtin_class
=
123 "ListBox", /* name */
124 CS_GLOBALCLASS
| CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
125 ListBoxWndProcA
, /* procA */
126 NULL
, /* procW (FIXME) */
127 sizeof(LB_DESCR
*), /* extra */
128 IDC_ARROWA
, /* cursor */
133 /*********************************************************************
134 * combolbox class descriptor
136 const struct builtin_class_descr COMBOLBOX_builtin_class
=
138 "ComboLBox", /* name */
139 CS_GLOBALCLASS
| CS_DBLCLKS
| CS_SAVEBITS
, /* style */
140 ComboLBWndProcA
, /* procA */
141 NULL
, /* procW (FIXME) */
142 sizeof(LB_DESCR
*), /* extra */
143 IDC_ARROWA
, /* cursor */
148 /***********************************************************************
151 void LISTBOX_Dump( WND
*wnd
)
155 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
157 TRACE( "Listbox:\n" );
158 TRACE( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
159 wnd
->hwndSelf
, (UINT
)descr
, descr
->heap
, descr
->nb_items
,
161 for (i
= 0, item
= descr
->items
; i
< descr
->nb_items
; i
++, item
++)
163 TRACE( "%4d: %-40s %d %08lx %3d\n",
164 i
, item
->str
, item
->selected
, item
->data
, item
->height
);
169 /***********************************************************************
170 * LISTBOX_GetCurrentPageSize
172 * Return the current page size
174 static INT
LISTBOX_GetCurrentPageSize( WND
*wnd
, LB_DESCR
*descr
)
177 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
178 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
180 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
182 if (i
== descr
->top_item
) return 1;
183 else return i
- descr
->top_item
;
187 /***********************************************************************
188 * LISTBOX_GetMaxTopIndex
190 * Return the maximum possible index for the top of the listbox.
192 static INT
LISTBOX_GetMaxTopIndex( WND
*wnd
, LB_DESCR
*descr
)
196 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
198 page
= descr
->height
;
199 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
200 if ((page
-= descr
->items
[max
].height
) < 0) break;
201 if (max
< descr
->nb_items
- 1) max
++;
203 else if (descr
->style
& LBS_MULTICOLUMN
)
205 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
206 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
207 max
= (max
- page
) * descr
->page_size
;
211 max
= descr
->nb_items
- descr
->page_size
;
213 if (max
< 0) max
= 0;
218 /***********************************************************************
219 * LISTBOX_UpdateScroll
221 * Update the scrollbars. Should be called whenever the content
222 * of the listbox changes.
224 static void LISTBOX_UpdateScroll( WND
*wnd
, LB_DESCR
*descr
)
228 /* Check the listbox scroll bar flags individually before we call
229 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
230 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
231 scroll bar when we do not need one.
232 if (!(descr->style & WS_VSCROLL)) return;
235 /* It is important that we check descr->style, and not wnd->dwStyle,
236 for WS_VSCROLL, as the former is exactly the one passed in
237 argument to CreateWindow.
238 In Windows (and from now on in Wine :) a listbox created
239 with such a style (no WS_SCROLL) does not update
240 the scrollbar with listbox-related data, thus letting
241 the programmer use it for his/her own purposes. */
243 if (descr
->style
& LBS_NOREDRAW
) return;
244 info
.cbSize
= sizeof(info
);
246 if (descr
->style
& LBS_MULTICOLUMN
)
249 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
250 info
.nPos
= descr
->top_item
/ descr
->page_size
;
251 info
.nPage
= descr
->width
/ descr
->column_width
;
252 if (info
.nPage
< 1) info
.nPage
= 1;
253 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
254 if (descr
->style
& LBS_DISABLENOSCROLL
)
255 info
.fMask
|= SIF_DISABLENOSCROLL
;
256 if (descr
->style
& WS_HSCROLL
)
257 SetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
259 info
.fMask
= SIF_RANGE
;
260 if (descr
->style
& WS_VSCROLL
)
261 SetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
266 info
.nMax
= descr
->nb_items
- 1;
267 info
.nPos
= descr
->top_item
;
268 info
.nPage
= LISTBOX_GetCurrentPageSize( wnd
, descr
);
269 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
270 if (descr
->style
& LBS_DISABLENOSCROLL
)
271 info
.fMask
|= SIF_DISABLENOSCROLL
;
272 if (descr
->style
& WS_VSCROLL
)
273 SetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
275 if (descr
->horz_extent
)
278 info
.nMax
= descr
->horz_extent
- 1;
279 info
.nPos
= descr
->horz_pos
;
280 info
.nPage
= descr
->width
;
281 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
282 if (descr
->style
& LBS_DISABLENOSCROLL
)
283 info
.fMask
|= SIF_DISABLENOSCROLL
;
284 if (descr
->style
& WS_HSCROLL
)
285 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( wnd
, 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 (1==LISTBOX_GetItemRect( wnd
, descr
, descr
->focus_item
, &rect
))
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( WND
*wnd
, LB_DESCR
*descr
, INT index
,
432 /* Index <= 0 is legal even on empty listboxes */
433 if (index
&& (index
>= descr
->nb_items
)) return -1;
434 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
435 if (descr
->style
& LBS_MULTICOLUMN
)
437 INT col
= (index
/ descr
->page_size
) -
438 (descr
->top_item
/ descr
->page_size
);
439 rect
->left
+= col
* descr
->column_width
;
440 rect
->right
= rect
->left
+ descr
->column_width
;
441 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
442 rect
->bottom
= rect
->top
+ descr
->item_height
;
444 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
447 rect
->right
+= descr
->horz_pos
;
448 if ((index
>= 0) && (index
< descr
->nb_items
))
450 if (index
< descr
->top_item
)
452 for (i
= descr
->top_item
-1; i
>= index
; i
--)
453 rect
->top
-= descr
->items
[i
].height
;
457 for (i
= descr
->top_item
; i
< index
; i
++)
458 rect
->top
+= descr
->items
[i
].height
;
460 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
466 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
467 rect
->bottom
= rect
->top
+ descr
->item_height
;
468 rect
->right
+= descr
->horz_pos
;
471 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
472 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
476 /***********************************************************************
477 * LISTBOX_GetItemFromPoint
479 * Return the item nearest from point (x,y) (in client coordinates).
481 static INT
LISTBOX_GetItemFromPoint( WND
*wnd
, LB_DESCR
*descr
,
484 INT index
= descr
->top_item
;
486 if (!descr
->nb_items
) return -1; /* No items */
487 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
492 while (index
< descr
->nb_items
)
494 if ((pos
+= descr
->items
[index
].height
) > y
) break;
503 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
507 else if (descr
->style
& LBS_MULTICOLUMN
)
509 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
510 if (y
>= 0) index
+= y
/ descr
->item_height
;
511 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
512 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
516 index
+= (y
/ descr
->item_height
);
518 if (index
< 0) return 0;
519 if (index
>= descr
->nb_items
) return -1;
524 /***********************************************************************
529 static void LISTBOX_PaintItem( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
,
530 const RECT
*rect
, INT index
, UINT action
, BOOL ignoreFocus
)
532 LB_ITEMDATA
*item
= NULL
;
533 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
535 if (IS_OWNERDRAW(descr
))
543 if (action
== ODA_FOCUS
)
544 DrawFocusRect( hdc
, rect
);
546 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
550 /* some programs mess with the clipping region when
551 drawing the item, *and* restore the previous region
552 after they are done, so a region has better to exist
553 else everything ends clipped */
554 GetClientRect(wnd
->hwndSelf
, &r
);
555 hrgn
= CreateRectRgnIndirect(&r
);
556 SelectClipRgn( hdc
, hrgn
);
557 DeleteObject( hrgn
);
559 dis
.CtlType
= ODT_LISTBOX
;
560 dis
.CtlID
= wnd
->wIDmenu
;
561 dis
.hwndItem
= wnd
->hwndSelf
;
562 dis
.itemAction
= action
;
566 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
567 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
569 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
570 if (wnd
->dwStyle
& WS_DISABLED
) dis
.itemState
|= ODS_DISABLED
;
571 dis
.itemData
= item
? item
->data
: 0;
573 TRACE("[%04x]: drawitem %d (%s) action=%02x "
574 "state=%02x rect=%d,%d-%d,%d\n",
575 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
576 dis
.itemState
, rect
->left
, rect
->top
,
577 rect
->right
, rect
->bottom
);
578 SendMessageA(descr
->owner
, WM_DRAWITEM
, wnd
->wIDmenu
, (LPARAM
)&dis
);
582 COLORREF oldText
= 0, oldBk
= 0;
584 if (action
== ODA_FOCUS
)
586 DrawFocusRect( hdc
, rect
);
589 if (item
&& item
->selected
)
591 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
592 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
595 TRACE("[%04x]: painting %d (%s) action=%02x "
596 "rect=%d,%d-%d,%d\n",
597 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
598 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
600 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
,
601 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
602 else if (!(descr
->style
& LBS_USETABSTOPS
))
603 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
,
604 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
605 strlen(item
->str
), NULL
);
608 /* Output empty string to paint background in the full width. */
609 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
,
610 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
611 TabbedTextOutA( hdc
, rect
->left
+ 1 , rect
->top
,
612 item
->str
, strlen(item
->str
),
613 descr
->nb_tabs
, descr
->tabs
, 0);
615 if (item
&& item
->selected
)
617 SetBkColor( hdc
, oldBk
);
618 SetTextColor( hdc
, oldText
);
620 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
622 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
627 /***********************************************************************
630 * Change the redraw flag.
632 static void LISTBOX_SetRedraw( WND
*wnd
, LB_DESCR
*descr
, BOOL on
)
636 if (!(descr
->style
& LBS_NOREDRAW
)) return;
637 descr
->style
&= ~LBS_NOREDRAW
;
638 if (descr
->style
& LBS_DISPLAYCHANGED
)
639 { /* page was changed while setredraw false, refresh automatically */
640 InvalidateRect(wnd
->hwndSelf
, NULL
, TRUE
);
641 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
642 { /* reset top of page if less than number of items/page */
643 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
644 if (descr
->top_item
< 0) descr
->top_item
= 0;
646 descr
->style
&= ~LBS_DISPLAYCHANGED
;
648 LISTBOX_UpdateScroll( wnd
, descr
);
650 else descr
->style
|= LBS_NOREDRAW
;
654 /***********************************************************************
655 * LISTBOX_RepaintItem
657 * Repaint a single item synchronously.
659 static void LISTBOX_RepaintItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
665 HBRUSH hbrush
, oldBrush
= 0;
667 /* Do not repaint the item if the item is not visible */
668 if (!IsWindowVisible(wnd
->hwndSelf
)) return;
669 if (descr
->style
& LBS_NOREDRAW
)
671 descr
->style
|= LBS_DISPLAYCHANGED
;
674 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) != 1) return;
675 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
))) return;
676 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
677 hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
678 hdc
, (LPARAM
)wnd
->hwndSelf
);
679 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
680 if (wnd
->dwStyle
& WS_DISABLED
)
681 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
682 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
683 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, index
, action
, FALSE
);
684 if (oldFont
) SelectObject( hdc
, oldFont
);
685 if (oldBrush
) SelectObject( hdc
, oldBrush
);
686 ReleaseDC( wnd
->hwndSelf
, hdc
);
690 /***********************************************************************
691 * LISTBOX_InitStorage
693 static LRESULT
LISTBOX_InitStorage( WND
*wnd
, LB_DESCR
*descr
, INT nb_items
,
698 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
699 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
701 nb_items
+= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
702 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
703 nb_items
* sizeof(LB_ITEMDATA
) )))
705 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
713 /***********************************************************************
714 * LISTBOX_SetTabStops
716 static BOOL
LISTBOX_SetTabStops( WND
*wnd
, LB_DESCR
*descr
, INT count
,
717 LPINT tabs
, BOOL short_ints
)
719 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
720 if (descr
->tabs
) HeapFree( descr
->heap
, 0, descr
->tabs
);
721 if (!(descr
->nb_tabs
= count
))
726 /* FIXME: count = 1 */
727 if (!(descr
->tabs
= (INT
*)HeapAlloc( descr
->heap
, 0,
728 descr
->nb_tabs
* sizeof(INT
) )))
733 LPINT16 p
= (LPINT16
)tabs
;
735 TRACE("[%04x]: settabstops ", wnd
->hwndSelf
);
736 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
737 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
738 if (TRACE_ON(listbox
)) DPRINTF("%hd ", descr
->tabs
[i
]);
740 if (TRACE_ON(listbox
)) DPRINTF("\n");
742 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
743 /* FIXME: repaint the window? */
748 /***********************************************************************
751 static LRESULT
LISTBOX_GetText( WND
*wnd
, LB_DESCR
*descr
, INT index
,
754 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
755 if (HAS_STRINGS(descr
))
758 return strlen(descr
->items
[index
].str
);
759 strcpy( buffer
, descr
->items
[index
].str
);
760 return strlen(buffer
);
763 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
764 return sizeof(DWORD
);
769 /***********************************************************************
770 * LISTBOX_FindStringPos
772 * Find the nearest string located before a given string in sort order.
773 * If 'exact' is TRUE, return an error if we don't get an exact match.
775 static INT
LISTBOX_FindStringPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
,
778 INT index
, min
, max
, res
= -1;
780 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
782 max
= descr
->nb_items
;
785 index
= (min
+ max
) / 2;
786 if (HAS_STRINGS(descr
))
787 res
= lstrcmpiA( descr
->items
[index
].str
, str
);
790 COMPAREITEMSTRUCT cis
;
792 cis
.CtlType
= ODT_LISTBOX
;
793 cis
.CtlID
= wnd
->wIDmenu
;
794 cis
.hwndItem
= wnd
->hwndSelf
;
796 cis
.itemData1
= descr
->items
[index
].data
;
798 cis
.itemData2
= (DWORD
)str
;
799 cis
.dwLocaleId
= descr
->locale
;
800 res
= SendMessageA( descr
->owner
, WM_COMPAREITEM
,
801 wnd
->wIDmenu
, (LPARAM
)&cis
);
803 if (!res
) return index
;
804 if (res
> 0) max
= index
;
805 else min
= index
+ 1;
807 return exact
? -1 : max
;
811 /***********************************************************************
812 * LISTBOX_FindFileStrPos
814 * Find the nearest string located before a given string in directory
815 * sort order (i.e. first files, then directories, then drives).
817 static INT
LISTBOX_FindFileStrPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
)
819 INT min
, max
, res
= -1;
821 if (!HAS_STRINGS(descr
))
822 return LISTBOX_FindStringPos( wnd
, descr
, str
, FALSE
);
824 max
= descr
->nb_items
;
827 INT index
= (min
+ max
) / 2;
828 const char *p
= descr
->items
[index
].str
;
829 if (*p
== '[') /* drive or directory */
831 if (*str
!= '[') res
= -1;
832 else if (p
[1] == '-') /* drive */
834 if (str
[1] == '-') res
= str
[2] - p
[2];
839 if (str
[1] == '-') res
= 1;
840 else res
= lstrcmpiA( str
, p
);
845 if (*str
== '[') res
= 1;
846 else res
= lstrcmpiA( str
, p
);
848 if (!res
) return index
;
849 if (res
< 0) max
= index
;
850 else min
= index
+ 1;
856 /***********************************************************************
859 * Find the item beginning with a given string.
861 static INT
LISTBOX_FindString( WND
*wnd
, LB_DESCR
*descr
, INT start
,
862 LPCSTR str
, BOOL exact
)
867 if (start
>= descr
->nb_items
) start
= -1;
868 item
= descr
->items
+ start
+ 1;
869 if (HAS_STRINGS(descr
))
871 if (!str
|| ! str
[0] ) return LB_ERR
;
874 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
875 if (!lstrcmpiA( str
, item
->str
)) return i
;
876 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
877 if (!lstrcmpiA( str
, item
->str
)) return i
;
881 /* Special case for drives and directories: ignore prefix */
882 #define CHECK_DRIVE(item) \
883 if ((item)->str[0] == '[') \
885 if (!strncasecmp( str, (item)->str+1, len )) return i; \
886 if (((item)->str[1] == '-') && !strncasecmp(str,(item)->str+2,len)) \
890 INT len
= strlen(str
);
891 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
893 if (!strncasecmp( str
, item
->str
, len
)) return i
;
896 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
898 if (!strncasecmp( str
, item
->str
, len
)) return i
;
906 if (exact
&& (descr
->style
& LBS_SORT
))
907 /* If sorted, use a WM_COMPAREITEM binary search */
908 return LISTBOX_FindStringPos( wnd
, descr
, str
, TRUE
);
910 /* Otherwise use a linear search */
911 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
912 if (item
->data
== (DWORD
)str
) return i
;
913 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
914 if (item
->data
== (DWORD
)str
) return i
;
920 /***********************************************************************
921 * LISTBOX_GetSelCount
923 static LRESULT
LISTBOX_GetSelCount( WND
*wnd
, LB_DESCR
*descr
)
926 LB_ITEMDATA
*item
= descr
->items
;
928 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
929 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
930 if (item
->selected
) count
++;
935 /***********************************************************************
936 * LISTBOX_GetSelItems16
938 static LRESULT
LISTBOX_GetSelItems16( WND
*wnd
, LB_DESCR
*descr
, INT16 max
,
942 LB_ITEMDATA
*item
= descr
->items
;
944 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
945 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
946 if (item
->selected
) array
[count
++] = (INT16
)i
;
951 /***********************************************************************
952 * LISTBOX_GetSelItems32
954 static LRESULT
LISTBOX_GetSelItems( WND
*wnd
, LB_DESCR
*descr
, INT max
,
958 LB_ITEMDATA
*item
= descr
->items
;
960 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
961 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
962 if (item
->selected
) array
[count
++] = i
;
967 /***********************************************************************
970 static LRESULT
LISTBOX_Paint( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
)
972 INT i
, col_pos
= descr
->page_size
- 1;
974 RECT focusRect
= {-1, -1, -1, -1};
976 HBRUSH hbrush
, oldBrush
= 0;
978 if (descr
->style
& LBS_NOREDRAW
) return 0;
980 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
981 if (descr
->style
& LBS_MULTICOLUMN
)
982 rect
.right
= rect
.left
+ descr
->column_width
;
983 else if (descr
->horz_pos
)
985 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
986 rect
.right
+= descr
->horz_pos
;
989 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
990 hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
991 hdc
, (LPARAM
)wnd
->hwndSelf
);
992 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
993 if (wnd
->dwStyle
& WS_DISABLED
)
994 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
996 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
999 /* Special case for empty listbox: paint focus rect */
1000 rect
.bottom
= rect
.top
+ descr
->item_height
;
1001 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, descr
->focus_item
,
1003 rect
.top
= rect
.bottom
;
1006 /* Paint all the item, regarding the selection
1007 Focus state will be painted after */
1009 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1011 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1012 rect
.bottom
= rect
.top
+ descr
->item_height
;
1014 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1016 if (i
== descr
->focus_item
)
1018 /* keep the focus rect, to paint the focus item after */
1019 focusRect
.left
= rect
.left
;
1020 focusRect
.right
= rect
.right
;
1021 focusRect
.top
= rect
.top
;
1022 focusRect
.bottom
= rect
.bottom
;
1024 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1025 rect
.top
= rect
.bottom
;
1027 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1029 if (!IS_OWNERDRAW(descr
))
1031 /* Clear the bottom of the column */
1032 if (rect
.top
< descr
->height
)
1034 rect
.bottom
= descr
->height
;
1035 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1036 &rect
, NULL
, 0, NULL
);
1040 /* Go to the next column */
1041 rect
.left
+= descr
->column_width
;
1042 rect
.right
+= descr
->column_width
;
1044 col_pos
= descr
->page_size
- 1;
1049 if (rect
.top
>= descr
->height
) break;
1053 /* Paint the focus item now */
1054 if (focusRect
.top
!= focusRect
.bottom
&& descr
->caret_on
)
1055 LISTBOX_PaintItem( wnd
, descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1057 if (!IS_OWNERDRAW(descr
))
1059 /* Clear the remainder of the client area */
1060 if (rect
.top
< descr
->height
)
1062 rect
.bottom
= descr
->height
;
1063 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1064 &rect
, NULL
, 0, NULL
);
1066 if (rect
.right
< descr
->width
)
1068 rect
.left
= rect
.right
;
1069 rect
.right
= descr
->width
;
1071 rect
.bottom
= descr
->height
;
1072 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1073 &rect
, NULL
, 0, NULL
);
1076 if (oldFont
) SelectObject( hdc
, oldFont
);
1077 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1082 /***********************************************************************
1083 * LISTBOX_InvalidateItems
1085 * Invalidate all items from a given item. If the specified item is not
1086 * visible, nothing happens.
1088 static void LISTBOX_InvalidateItems( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1092 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) == 1)
1094 if (descr
->style
& LBS_NOREDRAW
)
1096 descr
->style
|= LBS_DISPLAYCHANGED
;
1099 rect
.bottom
= descr
->height
;
1100 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
1101 if (descr
->style
& LBS_MULTICOLUMN
)
1103 /* Repaint the other columns */
1104 rect
.left
= rect
.right
;
1105 rect
.right
= descr
->width
;
1107 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
1113 /***********************************************************************
1114 * LISTBOX_GetItemHeight
1116 static LRESULT
LISTBOX_GetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1118 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1120 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1121 return descr
->items
[index
].height
;
1123 else return descr
->item_height
;
1127 /***********************************************************************
1128 * LISTBOX_SetItemHeight
1130 static LRESULT
LISTBOX_SetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1133 if (!height
) height
= 1;
1135 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1137 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1138 TRACE("[%04x]: item %d height = %d\n",
1139 wnd
->hwndSelf
, index
, height
);
1140 descr
->items
[index
].height
= height
;
1141 LISTBOX_UpdateScroll( wnd
, descr
);
1142 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1144 else if (height
!= descr
->item_height
)
1146 TRACE("[%04x]: new height = %d\n",
1147 wnd
->hwndSelf
, height
);
1148 descr
->item_height
= height
;
1149 LISTBOX_UpdatePage( wnd
, descr
);
1150 LISTBOX_UpdateScroll( wnd
, descr
);
1151 InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
1157 /***********************************************************************
1158 * LISTBOX_SetHorizontalPos
1160 static void LISTBOX_SetHorizontalPos( WND
*wnd
, LB_DESCR
*descr
, INT pos
)
1164 if (pos
> descr
->horz_extent
- descr
->width
)
1165 pos
= descr
->horz_extent
- descr
->width
;
1166 if (pos
< 0) pos
= 0;
1167 if (!(diff
= descr
->horz_pos
- pos
)) return;
1168 TRACE("[%04x]: new horz pos = %d\n",
1169 wnd
->hwndSelf
, pos
);
1170 descr
->horz_pos
= pos
;
1171 LISTBOX_UpdateScroll( wnd
, descr
);
1172 if (abs(diff
) < descr
->width
)
1173 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
1174 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1176 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1180 /***********************************************************************
1181 * LISTBOX_SetHorizontalExtent
1183 static LRESULT
LISTBOX_SetHorizontalExtent( WND
*wnd
, LB_DESCR
*descr
,
1186 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1188 if (extent
<= 0) extent
= 1;
1189 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1190 TRACE("[%04x]: new horz extent = %d\n",
1191 wnd
->hwndSelf
, extent
);
1192 descr
->horz_extent
= extent
;
1193 if (descr
->horz_pos
> extent
- descr
->width
)
1194 LISTBOX_SetHorizontalPos( wnd
, descr
, extent
- descr
->width
);
1196 LISTBOX_UpdateScroll( wnd
, descr
);
1201 /***********************************************************************
1202 * LISTBOX_SetColumnWidth
1204 static LRESULT
LISTBOX_SetColumnWidth( WND
*wnd
, LB_DESCR
*descr
, UINT width
)
1206 if (width
== descr
->column_width
) return LB_OKAY
;
1207 TRACE("[%04x]: new column width = %d\n",
1208 wnd
->hwndSelf
, width
);
1209 descr
->column_width
= width
;
1210 LISTBOX_UpdatePage( wnd
, descr
);
1215 /***********************************************************************
1218 * Returns the item height.
1220 static INT
LISTBOX_SetFont( WND
*wnd
, LB_DESCR
*descr
, HFONT font
)
1228 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
)))
1230 ERR("unable to get DC.\n" );
1233 if (font
) oldFont
= SelectObject( hdc
, font
);
1234 GetTextMetricsA( hdc
, &tm
);
1235 if (oldFont
) SelectObject( hdc
, oldFont
);
1236 ReleaseDC( wnd
->hwndSelf
, hdc
);
1237 if (!IS_OWNERDRAW(descr
))
1238 LISTBOX_SetItemHeight( wnd
, descr
, 0, tm
.tmHeight
);
1239 return tm
.tmHeight
;
1243 /***********************************************************************
1244 * LISTBOX_MakeItemVisible
1246 * Make sure that a given item is partially or fully visible.
1248 static void LISTBOX_MakeItemVisible( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1253 if (index
<= descr
->top_item
) top
= index
;
1254 else if (descr
->style
& LBS_MULTICOLUMN
)
1256 INT cols
= descr
->width
;
1257 if (!fully
) cols
+= descr
->column_width
- 1;
1258 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1260 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1261 top
= index
- descr
->page_size
* (cols
- 1);
1263 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1265 INT height
= fully
? descr
->items
[index
].height
: 1;
1266 for (top
= index
; top
> descr
->top_item
; top
--)
1267 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1271 if (index
< descr
->top_item
+ descr
->page_size
) return;
1272 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1273 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1274 top
= index
- descr
->page_size
+ 1;
1276 LISTBOX_SetTopItem( wnd
, descr
, top
, TRUE
);
1279 /***********************************************************************
1280 * LISTBOX_SetCaretIndex
1283 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1286 static LRESULT
LISTBOX_SetCaretIndex( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1287 BOOL fully_visible
)
1289 INT oldfocus
= descr
->focus_item
;
1291 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1292 if (index
== oldfocus
) return LB_OKAY
;
1293 descr
->focus_item
= index
;
1294 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1295 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1297 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1298 if (descr
->caret_on
&& (descr
->in_focus
))
1299 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1305 /***********************************************************************
1306 * LISTBOX_SelectItemRange
1308 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1310 static LRESULT
LISTBOX_SelectItemRange( WND
*wnd
, LB_DESCR
*descr
, INT first
,
1315 /* A few sanity checks */
1317 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1318 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1319 if (last
== -1) last
= descr
->nb_items
- 1;
1320 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1321 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1322 /* selected_item reflects last selected/unselected item on multiple sel */
1323 descr
->selected_item
= last
;
1325 if (on
) /* Turn selection on */
1327 for (i
= first
; i
<= last
; i
++)
1329 if (descr
->items
[i
].selected
) continue;
1330 descr
->items
[i
].selected
= TRUE
;
1331 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1333 LISTBOX_SetCaretIndex( wnd
, descr
, last
, TRUE
);
1335 else /* Turn selection off */
1337 for (i
= first
; i
<= last
; i
++)
1339 if (!descr
->items
[i
].selected
) continue;
1340 descr
->items
[i
].selected
= FALSE
;
1341 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1347 /***********************************************************************
1348 * LISTBOX_SetSelection
1350 static LRESULT
LISTBOX_SetSelection( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1351 BOOL on
, BOOL send_notify
)
1353 TRACE( "index=%d notify=%s\n", index
, send_notify
? "YES" : "NO" );
1355 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1356 if (descr
->style
& LBS_MULTIPLESEL
)
1358 if (index
== -1) /* Select all items */
1359 return LISTBOX_SelectItemRange( wnd
, descr
, 0, -1, on
);
1360 else /* Only one item */
1361 return LISTBOX_SelectItemRange( wnd
, descr
, index
, index
, on
);
1365 INT oldsel
= descr
->selected_item
;
1366 if (index
== oldsel
) return LB_OKAY
;
1367 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1368 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1369 descr
->selected_item
= index
;
1370 if (oldsel
!= -1) LISTBOX_RepaintItem( wnd
, descr
, oldsel
, ODA_SELECT
);
1371 if (index
!= -1) LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_SELECT
);
1372 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( wnd
, descr
,
1373 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1375 if( descr
->lphc
) /* set selection change flag for parent combo */
1376 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1382 /***********************************************************************
1385 * Change the caret position and extend the selection to the new caret.
1387 static void LISTBOX_MoveCaret( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1388 BOOL fully_visible
)
1390 INT oldfocus
= descr
->focus_item
;
1392 if ((index
< 0) || (index
>= descr
->nb_items
))
1395 /* Important, repaint needs to be done in this order if
1396 you want to mimic Windows behavior:
1397 1. Remove the focus and paint the item
1398 2. Remove the selection and paint the item(s)
1399 3. Set the selection and repaint the item(s)
1400 4. Set the focus to 'index' and repaint the item */
1402 /* 1. remove the focus and repaint the item */
1403 descr
->focus_item
= -1;
1404 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1405 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1407 /* 2. then turn off the previous selection */
1408 /* 3. repaint the new selected item */
1409 if (descr
->style
& LBS_EXTENDEDSEL
)
1411 if (descr
->anchor_item
!= -1)
1413 INT first
= min( index
, descr
->anchor_item
);
1414 INT last
= max( index
, descr
->anchor_item
);
1416 LISTBOX_SelectItemRange( wnd
, descr
, 0, first
- 1, FALSE
);
1417 LISTBOX_SelectItemRange( wnd
, descr
, last
+ 1, -1, FALSE
);
1418 LISTBOX_SelectItemRange( wnd
, descr
, first
, last
, TRUE
);
1421 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1423 /* Set selection to new caret item */
1424 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
1427 /* 4. repaint the new item with the focus */
1428 descr
->focus_item
= index
;
1429 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1430 if (descr
->caret_on
&& (descr
->in_focus
))
1431 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1435 /***********************************************************************
1436 * LISTBOX_InsertItem
1438 static LRESULT
LISTBOX_InsertItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1439 LPSTR str
, DWORD data
)
1443 INT oldfocus
= descr
->focus_item
;
1445 if (index
== -1) index
= descr
->nb_items
;
1446 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1447 if (!descr
->items
) max_items
= 0;
1448 else max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
1449 if (descr
->nb_items
== max_items
)
1451 /* We need to grow the array */
1452 max_items
+= LB_ARRAY_GRANULARITY
;
1453 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1454 max_items
* sizeof(LB_ITEMDATA
) )))
1456 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1459 descr
->items
= item
;
1462 /* Insert the item structure */
1464 item
= &descr
->items
[index
];
1465 if (index
< descr
->nb_items
)
1466 RtlMoveMemory( item
+ 1, item
,
1467 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1471 item
->selected
= FALSE
;
1474 /* Get item height */
1476 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1478 MEASUREITEMSTRUCT mis
;
1480 mis
.CtlType
= ODT_LISTBOX
;
1481 mis
.CtlID
= wnd
->wIDmenu
;
1483 mis
.itemData
= descr
->items
[index
].data
;
1484 mis
.itemHeight
= descr
->item_height
;
1485 SendMessageA( descr
->owner
, WM_MEASUREITEM
, wnd
->wIDmenu
, (LPARAM
)&mis
);
1486 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1487 TRACE("[%04x]: measure item %d (%s) = %d\n",
1488 wnd
->hwndSelf
, index
, str
? str
: "", item
->height
);
1491 /* Repaint the items */
1493 LISTBOX_UpdateScroll( wnd
, descr
);
1494 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1496 /* Move selection and focused item */
1497 /* If listbox was empty, set focus to the first item */
1498 if (descr
->nb_items
== 1)
1499 LISTBOX_SetCaretIndex( wnd
, descr
, 0, FALSE
);
1500 /* single select don't change selection index in win31 */
1501 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1503 descr
->selected_item
++;
1504 LISTBOX_SetSelection( wnd
, descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1508 if (index
<= descr
->selected_item
)
1510 descr
->selected_item
++;
1511 descr
->focus_item
= oldfocus
; /* focus not changed */
1518 /***********************************************************************
1519 * LISTBOX_InsertString
1521 static LRESULT
LISTBOX_InsertString( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1524 LPSTR new_str
= NULL
;
1528 if (HAS_STRINGS(descr
))
1531 if (!(new_str
= HEAP_strdupA( descr
->heap
, 0, str
)))
1533 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1537 else data
= (DWORD
)str
;
1539 if (index
== -1) index
= descr
->nb_items
;
1540 if ((ret
= LISTBOX_InsertItem( wnd
, descr
, index
, new_str
, data
)) != 0)
1542 if (new_str
) HeapFree( descr
->heap
, 0, new_str
);
1546 TRACE("[%04x]: added item %d '%s'\n",
1547 wnd
->hwndSelf
, index
, HAS_STRINGS(descr
) ? new_str
: "" );
1552 /***********************************************************************
1553 * LISTBOX_DeleteItem
1555 * Delete the content of an item. 'index' must be a valid index.
1557 static void LISTBOX_DeleteItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1559 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1560 * while Win95 sends it for all items with user data.
1561 * It's probably better to send it too often than not
1562 * often enough, so this is what we do here.
1564 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1566 DELETEITEMSTRUCT dis
;
1568 dis
.CtlType
= ODT_LISTBOX
;
1569 dis
.CtlID
= wnd
->wIDmenu
;
1571 dis
.hwndItem
= wnd
->hwndSelf
;
1572 dis
.itemData
= descr
->items
[index
].data
;
1573 SendMessageA( descr
->owner
, WM_DELETEITEM
, wnd
->wIDmenu
, (LPARAM
)&dis
);
1575 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1576 HeapFree( descr
->heap
, 0, descr
->items
[index
].str
);
1580 /***********************************************************************
1581 * LISTBOX_RemoveItem
1583 * Remove an item from the listbox and delete its content.
1585 static LRESULT
LISTBOX_RemoveItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1590 if (index
== -1) index
= descr
->nb_items
- 1;
1591 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1593 /* We need to invalidate the original rect instead of the updated one. */
1594 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1596 LISTBOX_DeleteItem( wnd
, descr
, index
);
1598 /* Remove the item */
1600 item
= &descr
->items
[index
];
1601 if (index
< descr
->nb_items
-1)
1602 RtlMoveMemory( item
, item
+ 1,
1603 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1605 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1607 /* Shrink the item array if possible */
1609 max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1610 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1612 max_items
-= LB_ARRAY_GRANULARITY
;
1613 item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1614 max_items
* sizeof(LB_ITEMDATA
) );
1615 if (item
) descr
->items
= item
;
1617 /* Repaint the items */
1619 LISTBOX_UpdateScroll( wnd
, descr
);
1620 /* if we removed the scrollbar, reset the top of the list
1621 (correct for owner-drawn ???) */
1622 if (descr
->nb_items
== descr
->page_size
)
1623 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1625 /* Move selection and focused item */
1626 if (!IS_MULTISELECT(descr
))
1628 if (index
== descr
->selected_item
)
1629 descr
->selected_item
= -1;
1630 else if (index
< descr
->selected_item
)
1632 descr
->selected_item
--;
1633 if (ISWIN31
) /* win 31 do not change the selected item number */
1634 LISTBOX_SetSelection( wnd
, descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1638 if (descr
->focus_item
>= descr
->nb_items
)
1640 descr
->focus_item
= descr
->nb_items
- 1;
1641 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1647 /***********************************************************************
1648 * LISTBOX_ResetContent
1650 static void LISTBOX_ResetContent( WND
*wnd
, LB_DESCR
*descr
)
1654 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( wnd
, descr
, i
);
1655 if (descr
->items
) HeapFree( descr
->heap
, 0, descr
->items
);
1656 descr
->nb_items
= 0;
1657 descr
->top_item
= 0;
1658 descr
->selected_item
= -1;
1659 descr
->focus_item
= 0;
1660 descr
->anchor_item
= -1;
1661 descr
->items
= NULL
;
1665 /***********************************************************************
1668 static LRESULT
LISTBOX_SetCount( WND
*wnd
, LB_DESCR
*descr
, INT count
)
1672 if (HAS_STRINGS(descr
)) return LB_ERR
;
1673 /* FIXME: this is far from optimal... */
1674 if (count
> descr
->nb_items
)
1676 while (count
> descr
->nb_items
)
1677 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, 0 )) < 0)
1680 else if (count
< descr
->nb_items
)
1682 while (count
< descr
->nb_items
)
1683 if ((ret
= LISTBOX_RemoveItem( wnd
, descr
, -1 )) < 0)
1690 /***********************************************************************
1693 static LRESULT
LISTBOX_Directory( WND
*wnd
, LB_DESCR
*descr
, UINT attrib
,
1694 LPCSTR filespec
, BOOL long_names
)
1697 LRESULT ret
= LB_OKAY
;
1698 WIN32_FIND_DATAA entry
;
1701 /* don't scan directory if we just want drives exclusively */
1702 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1703 /* scan directory */
1704 if ((handle
= FindFirstFileA(filespec
,&entry
)) == INVALID_HANDLE_VALUE
)
1706 if (GetLastError() != ERROR_NO_MORE_FILES
) return LB_ERR
;
1713 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1715 if (!(attrib
& DDL_DIRECTORY
) ||
1716 !strcmp( entry
.cAlternateFileName
, "." )) continue;
1717 if (long_names
) sprintf( buffer
, "[%s]", entry
.cFileName
);
1718 else sprintf( buffer
, "[%s]", entry
.cAlternateFileName
);
1720 else /* not a directory */
1722 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1723 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1725 if ((attrib
& DDL_EXCLUSIVE
) &&
1726 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1729 if (long_names
) strcpy( buffer
, entry
.cFileName
);
1730 else strcpy( buffer
, entry
.cAlternateFileName
);
1732 if (!long_names
) CharLowerA( buffer
);
1733 pos
= LISTBOX_FindFileStrPos( wnd
, descr
, buffer
);
1734 if ((ret
= LISTBOX_InsertString( wnd
, descr
, pos
, buffer
)) < 0)
1736 } while (FindNextFileA( handle
, &entry
));
1737 FindClose( handle
);
1742 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1744 char buffer
[] = "[-a-]";
1745 char root
[] = "A:\\";
1747 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1749 if (GetDriveTypeA(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1750 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, buffer
)) < 0)
1758 /***********************************************************************
1759 * LISTBOX_HandleVScroll
1761 static LRESULT
LISTBOX_HandleVScroll( WND
*wnd
, LB_DESCR
*descr
,
1762 WPARAM wParam
, LPARAM lParam
)
1766 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1767 switch(LOWORD(wParam
))
1770 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
- 1, TRUE
);
1773 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ 1, TRUE
);
1776 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-
1777 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1780 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+
1781 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1783 case SB_THUMBPOSITION
:
1784 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
), TRUE
);
1787 info
.cbSize
= sizeof(info
);
1788 info
.fMask
= SIF_TRACKPOS
;
1789 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1790 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
, TRUE
);
1793 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1796 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1803 /***********************************************************************
1804 * LISTBOX_HandleHScroll
1806 static LRESULT
LISTBOX_HandleHScroll( WND
*wnd
, LB_DESCR
*descr
,
1807 WPARAM wParam
, LPARAM lParam
)
1812 if (descr
->style
& LBS_MULTICOLUMN
)
1814 switch(LOWORD(wParam
))
1817 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-descr
->page_size
,
1821 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+descr
->page_size
,
1825 page
= descr
->width
/ descr
->column_width
;
1826 if (page
< 1) page
= 1;
1827 LISTBOX_SetTopItem( wnd
, descr
,
1828 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1831 page
= descr
->width
/ descr
->column_width
;
1832 if (page
< 1) page
= 1;
1833 LISTBOX_SetTopItem( wnd
, descr
,
1834 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1836 case SB_THUMBPOSITION
:
1837 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1841 info
.cbSize
= sizeof(info
);
1842 info
.fMask
= SIF_TRACKPOS
;
1843 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1844 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1848 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1851 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1855 else if (descr
->horz_extent
)
1857 switch(LOWORD(wParam
))
1860 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
- 1 );
1863 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
+ 1 );
1866 LISTBOX_SetHorizontalPos( wnd
, descr
,
1867 descr
->horz_pos
- descr
->width
);
1870 LISTBOX_SetHorizontalPos( wnd
, descr
,
1871 descr
->horz_pos
+ descr
->width
);
1873 case SB_THUMBPOSITION
:
1874 LISTBOX_SetHorizontalPos( wnd
, descr
, HIWORD(wParam
) );
1877 info
.cbSize
= sizeof(info
);
1878 info
.fMask
= SIF_TRACKPOS
;
1879 GetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
);
1880 LISTBOX_SetHorizontalPos( wnd
, descr
, info
.nTrackPos
);
1883 LISTBOX_SetHorizontalPos( wnd
, descr
, 0 );
1886 LISTBOX_SetHorizontalPos( wnd
, descr
,
1887 descr
->horz_extent
- descr
->width
);
1894 static LRESULT
LISTBOX_HandleMouseWheel(WND
*wnd
, LB_DESCR
*descr
,WPARAM wParam
, LPARAM lParam
)
1896 short gcWheelDelta
= 0;
1897 UINT pulScrollLines
= 3;
1899 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1901 gcWheelDelta
-= (short) HIWORD(wParam
);
1903 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
1905 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
1906 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
1907 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ cLineScroll
, TRUE
);
1912 /***********************************************************************
1913 * LISTBOX_HandleLButtonDown
1915 static LRESULT
LISTBOX_HandleLButtonDown( WND
*wnd
, LB_DESCR
*descr
,
1916 WPARAM wParam
, INT x
, INT y
)
1918 INT index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1919 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1920 wnd
->hwndSelf
, x
, y
, index
);
1921 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
1923 if (!descr
->in_focus
)
1925 if( !descr
->lphc
) SetFocus( wnd
->hwndSelf
);
1926 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
1927 : descr
->lphc
->self
->hwndSelf
);
1930 if (index
== -1) return 0;
1932 if (descr
->style
& LBS_EXTENDEDSEL
)
1934 /* we should perhaps make sure that all items are deselected
1935 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1936 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1937 LISTBOX_SetSelection( wnd, descr, -1, FALSE, FALSE);
1940 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1941 if (wParam
& MK_CONTROL
)
1943 LISTBOX_SetCaretIndex( wnd
, descr
, index
, FALSE
);
1944 LISTBOX_SetSelection( wnd
, descr
, index
,
1945 !descr
->items
[index
].selected
,
1946 (descr
->style
& LBS_NOTIFY
) != 0);
1948 else LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1952 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1953 LISTBOX_SetSelection( wnd
, descr
, index
,
1954 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1955 !descr
->items
[index
].selected
),
1956 (descr
->style
& LBS_NOTIFY
) != 0 );
1959 descr
->captured
= TRUE
;
1960 SetCapture( wnd
->hwndSelf
);
1964 if (descr
->style
& LBS_NOTIFY
)
1965 SendMessageA( descr
->owner
, WM_LBTRACKPOINT
, index
,
1966 MAKELPARAM( x
, y
) );
1967 if (wnd
->dwExStyle
& WS_EX_DRAGDETECT
)
1974 if (DragDetect( wnd
->hwndSelf
, pt
))
1975 SendMessageA( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
1982 /*************************************************************************
1983 * LISTBOX_HandleLButtonDownCombo [Internal]
1985 * Process LButtonDown message for the ComboListBox
1988 * pWnd [I] The windows internal structure
1989 * pDescr [I] The ListBox internal structure
1990 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
1991 * x [I] X Mouse Coordinate
1992 * y [I] Y Mouse Coordinate
1995 * 0 since we are processing the WM_LBUTTONDOWN Message
1998 * This function is only to be used when a ListBox is a ComboListBox
2001 static LRESULT
LISTBOX_HandleLButtonDownCombo( WND
*pWnd
, LB_DESCR
*pDescr
,
2002 UINT msg
, WPARAM wParam
, INT x
, INT y
)
2004 RECT clientRect
, screenRect
;
2010 GetClientRect(pWnd
->hwndSelf
, &clientRect
);
2012 if(PtInRect(&clientRect
, mousePos
))
2014 /* MousePos is in client, resume normal processing */
2015 if (msg
== WM_LBUTTONDOWN
)
2017 pDescr
->lphc
->droppedIndex
= pDescr
->nb_items
? pDescr
->selected_item
: -1;
2018 return LISTBOX_HandleLButtonDown( pWnd
, pDescr
, wParam
, x
, y
);
2020 else if (pDescr
->style
& LBS_NOTIFY
)
2021 SEND_NOTIFICATION( pWnd
, pDescr
, LBN_DBLCLK
);
2026 POINT screenMousePos
;
2027 HWND hWndOldCapture
;
2029 /* Check the Non-Client Area */
2030 screenMousePos
= mousePos
;
2031 hWndOldCapture
= GetCapture();
2033 GetWindowRect(pWnd
->hwndSelf
, &screenRect
);
2034 ClientToScreen(pWnd
->hwndSelf
, &screenMousePos
);
2036 if(!PtInRect(&screenRect
, screenMousePos
))
2038 LISTBOX_SetSelection( pWnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2039 COMBO_FlipListbox( pDescr
->lphc
, FALSE
, FALSE
);
2044 /* Check to see the NC is a scrollbar */
2046 /* Check Vertical scroll bar */
2047 if (pWnd
->dwStyle
& WS_VSCROLL
)
2049 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2050 if (PtInRect( &clientRect
, mousePos
))
2052 nHitTestType
= HTVSCROLL
;
2055 /* Check horizontal scroll bar */
2056 if (pWnd
->dwStyle
& WS_HSCROLL
)
2058 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2059 if (PtInRect( &clientRect
, mousePos
))
2061 nHitTestType
= HTHSCROLL
;
2064 /* Windows sends this message when a scrollbar is clicked
2067 if(nHitTestType
!= 0)
2069 SendMessageA(pWnd
->hwndSelf
, WM_NCLBUTTONDOWN
, nHitTestType
,
2070 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2072 /* Resume the Capture after scrolling is complete
2074 if(hWndOldCapture
!= 0)
2076 SetCapture(hWndOldCapture
);
2083 /***********************************************************************
2084 * LISTBOX_HandleLButtonUp
2086 static LRESULT
LISTBOX_HandleLButtonUp( WND
*wnd
, LB_DESCR
*descr
)
2088 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2089 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2090 LISTBOX_Timer
= LB_TIMER_NONE
;
2091 if (descr
->captured
)
2093 descr
->captured
= FALSE
;
2094 if (GetCapture() == wnd
->hwndSelf
) ReleaseCapture();
2095 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2096 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2102 /***********************************************************************
2103 * LISTBOX_HandleTimer
2105 * Handle scrolling upon a timer event.
2106 * Return TRUE if scrolling should continue.
2108 static LRESULT
LISTBOX_HandleTimer( WND
*wnd
, LB_DESCR
*descr
,
2109 INT index
, TIMER_DIRECTION dir
)
2114 if (descr
->top_item
) index
= descr
->top_item
- 1;
2118 if (descr
->top_item
) index
-= descr
->page_size
;
2121 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( wnd
, descr
);
2122 if (index
== descr
->focus_item
) index
++;
2123 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2125 case LB_TIMER_RIGHT
:
2126 if (index
+ descr
->page_size
< descr
->nb_items
)
2127 index
+= descr
->page_size
;
2132 if (index
== descr
->focus_item
) return FALSE
;
2133 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
2138 /***********************************************************************
2139 * LISTBOX_HandleSystemTimer
2141 * WM_SYSTIMER handler.
2143 static LRESULT
LISTBOX_HandleSystemTimer( WND
*wnd
, LB_DESCR
*descr
)
2145 if (!LISTBOX_HandleTimer( wnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
2147 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2148 LISTBOX_Timer
= LB_TIMER_NONE
;
2154 /***********************************************************************
2155 * LISTBOX_HandleMouseMove
2157 * WM_MOUSEMOVE handler.
2159 static void LISTBOX_HandleMouseMove( WND
*wnd
, LB_DESCR
*descr
,
2163 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2165 if (!descr
->captured
) return;
2167 if (descr
->style
& LBS_MULTICOLUMN
)
2170 else if (y
>= descr
->item_height
* descr
->page_size
)
2171 y
= descr
->item_height
* descr
->page_size
- 1;
2175 dir
= LB_TIMER_LEFT
;
2178 else if (x
>= descr
->width
)
2180 dir
= LB_TIMER_RIGHT
;
2181 x
= descr
->width
- 1;
2186 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2187 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2190 index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
2191 if (index
== -1) index
= descr
->focus_item
;
2192 if (!LISTBOX_HandleTimer( wnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2194 /* Start/stop the system timer */
2196 if (dir
!= LB_TIMER_NONE
)
2197 SetSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2198 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2199 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2200 LISTBOX_Timer
= dir
;
2204 /***********************************************************************
2205 * LISTBOX_HandleKeyDown
2207 static LRESULT
LISTBOX_HandleKeyDown( WND
*wnd
, LB_DESCR
*descr
, WPARAM wParam
)
2210 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2211 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2212 bForceSelection
= FALSE
; /* only for single select list */
2214 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2216 caret
= SendMessageA( descr
->owner
, WM_VKEYTOITEM
,
2217 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2219 if (caret
== -2) return 0;
2221 if (caret
== -1) switch(wParam
)
2224 if (descr
->style
& LBS_MULTICOLUMN
)
2226 bForceSelection
= FALSE
;
2227 if (descr
->focus_item
>= descr
->page_size
)
2228 caret
= descr
->focus_item
- descr
->page_size
;
2233 caret
= descr
->focus_item
- 1;
2234 if (caret
< 0) caret
= 0;
2237 if (descr
->style
& LBS_MULTICOLUMN
)
2239 bForceSelection
= FALSE
;
2240 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2241 caret
= descr
->focus_item
+ descr
->page_size
;
2246 caret
= descr
->focus_item
+ 1;
2247 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2251 if (descr
->style
& LBS_MULTICOLUMN
)
2253 INT page
= descr
->width
/ descr
->column_width
;
2254 if (page
< 1) page
= 1;
2255 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2257 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(wnd
,descr
)+1;
2258 if (caret
< 0) caret
= 0;
2261 if (descr
->style
& LBS_MULTICOLUMN
)
2263 INT page
= descr
->width
/ descr
->column_width
;
2264 if (page
< 1) page
= 1;
2265 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2267 else caret
= descr
->focus_item
+LISTBOX_GetCurrentPageSize(wnd
,descr
)-1;
2268 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2274 caret
= descr
->nb_items
- 1;
2277 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2278 else if (descr
->style
& LBS_MULTIPLESEL
)
2280 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
,
2281 !descr
->items
[descr
->focus_item
].selected
,
2282 (descr
->style
& LBS_NOTIFY
) != 0 );
2286 bForceSelection
= FALSE
;
2288 if (bForceSelection
) /* focused item is used instead of key */
2289 caret
= descr
->focus_item
;
2292 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
2293 !(GetKeyState( VK_SHIFT
) & 0x8000))
2294 descr
->anchor_item
= caret
;
2295 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2296 LISTBOX_SetSelection( wnd
, descr
, caret
, TRUE
, FALSE
);
2297 if (descr
->style
& LBS_NOTIFY
)
2299 if( descr
->lphc
&& CB_GETTYPE(descr
->lphc
) != CBS_SIMPLE
)
2301 /* make sure that combo parent doesn't hide us */
2302 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2304 if (descr
->nb_items
) SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2311 /***********************************************************************
2312 * LISTBOX_HandleChar
2314 static LRESULT
LISTBOX_HandleChar( WND
*wnd
, LB_DESCR
*descr
,
2320 str
[0] = wParam
& 0xff;
2323 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2325 caret
= SendMessageA( descr
->owner
, WM_CHARTOITEM
,
2326 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2328 if (caret
== -2) return 0;
2331 caret
= LISTBOX_FindString( wnd
, descr
, descr
->focus_item
, str
, FALSE
);
2334 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2335 LISTBOX_SetSelection( wnd
, descr
, caret
, TRUE
, FALSE
);
2336 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2337 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2338 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2344 /***********************************************************************
2347 static BOOL
LISTBOX_Create( WND
*wnd
, LPHEADCOMBO lphc
)
2350 MEASUREITEMSTRUCT mis
;
2353 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2355 if (!(descr
->heap
= HeapCreate( 0, 0x10000, 0 )))
2357 HeapFree( GetProcessHeap(), 0, descr
);
2360 GetClientRect( wnd
->hwndSelf
, &rect
);
2361 descr
->owner
= GetParent( wnd
->hwndSelf
);
2362 descr
->style
= wnd
->dwStyle
;
2363 descr
->width
= rect
.right
- rect
.left
;
2364 descr
->height
= rect
.bottom
- rect
.top
;
2365 descr
->items
= NULL
;
2366 descr
->nb_items
= 0;
2367 descr
->top_item
= 0;
2368 descr
->selected_item
= -1;
2369 descr
->focus_item
= 0;
2370 descr
->anchor_item
= -1;
2371 descr
->item_height
= 1;
2372 descr
->page_size
= 1;
2373 descr
->column_width
= 150;
2374 descr
->horz_extent
= (wnd
->dwStyle
& WS_HSCROLL
) ? 1 : 0;
2375 descr
->horz_pos
= 0;
2378 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2379 descr
->in_focus
= FALSE
;
2380 descr
->captured
= FALSE
;
2382 descr
->locale
= 0; /* FIXME */
2385 if( ( GetExpWinVer16( wnd
->hInstance
) & 0xFF00 ) == 0x0300
2386 && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2388 /* Win95 document "List Box Differences" from MSDN:
2389 If a list box in a version 3.x application has either the
2390 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2391 horizontal and vertical scroll bars.
2393 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2398 TRACE_(combo
)("[%04x]: resetting owner %04x -> %04x\n",
2399 wnd
->hwndSelf
, descr
->owner
, lphc
->self
->hwndSelf
);
2400 descr
->owner
= lphc
->self
->hwndSelf
;
2403 *(LB_DESCR
**)wnd
->wExtra
= descr
;
2405 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2407 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2408 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2409 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2410 descr
->item_height
= LISTBOX_SetFont( wnd
, descr
, 0 );
2412 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2414 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2416 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2417 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2421 mis
.CtlType
= ODT_LISTBOX
;
2422 mis
.CtlID
= wnd
->wIDmenu
;
2426 mis
.itemHeight
= descr
->item_height
;
2427 SendMessageA( descr
->owner
, WM_MEASUREITEM
, wnd
->wIDmenu
, (LPARAM
)&mis
);
2428 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2436 /***********************************************************************
2439 static BOOL
LISTBOX_Destroy( WND
*wnd
, LB_DESCR
*descr
)
2441 LISTBOX_ResetContent( wnd
, descr
);
2442 HeapDestroy( descr
->heap
);
2443 HeapFree( GetProcessHeap(), 0, descr
);
2449 /***********************************************************************
2452 static inline LRESULT WINAPI
ListBoxWndProc_locked( WND
* wnd
, UINT msg
,
2453 WPARAM wParam
, LPARAM lParam
)
2457 HWND hwnd
= wnd
->hwndSelf
;
2460 if (!(descr
= *(LB_DESCR
**)wnd
->wExtra
))
2466 if (!LISTBOX_Create( wnd
, NULL
))
2468 TRACE("creating wnd=%04x descr=%p\n",
2469 hwnd
, *(LB_DESCR
**)wnd
->wExtra
);
2475 * When a listbox is not in a combobox and the look
2476 * is win95, the WS_BORDER style is replaced with
2477 * the WS_EX_CLIENTEDGE style.
2479 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
2480 (wnd
->dwStyle
& WS_BORDER
) )
2482 wnd
->dwExStyle
|= WS_EX_CLIENTEDGE
;
2483 wnd
->dwStyle
&= ~ WS_BORDER
;
2488 /* Ignore all other messages before we get a WM_CREATE */
2489 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2492 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2493 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2496 case LB_RESETCONTENT16
:
2497 case LB_RESETCONTENT
:
2498 LISTBOX_ResetContent( wnd
, descr
);
2499 LISTBOX_UpdateScroll( wnd
, descr
);
2500 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
2503 case LB_ADDSTRING16
:
2504 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2507 wParam
= LISTBOX_FindStringPos( wnd
, descr
, (LPCSTR
)lParam
, FALSE
);
2508 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2510 case LB_INSERTSTRING16
:
2511 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2512 wParam
= (INT
)(INT16
)wParam
;
2514 case LB_INSERTSTRING
:
2515 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2518 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2521 wParam
= LISTBOX_FindFileStrPos( wnd
, descr
, (LPCSTR
)lParam
);
2522 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2524 case LB_DELETESTRING16
:
2525 case LB_DELETESTRING
:
2526 if (LISTBOX_RemoveItem( wnd
, descr
, wParam
) != LB_ERR
)
2527 return descr
->nb_items
;
2531 case LB_GETITEMDATA16
:
2532 case LB_GETITEMDATA
:
2533 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2535 return descr
->items
[wParam
].data
;
2537 case LB_SETITEMDATA16
:
2538 case LB_SETITEMDATA
:
2539 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2541 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2546 return descr
->nb_items
;
2549 lParam
= (LPARAM
)MapSL(lParam
);
2552 return LISTBOX_GetText( wnd
, descr
, wParam
, (LPSTR
)lParam
);
2554 case LB_GETTEXTLEN16
:
2557 if (wParam
>= descr
->nb_items
)
2559 return (HAS_STRINGS(descr
) ? strlen(descr
->items
[wParam
].str
)
2562 case LB_GETCURSEL16
:
2564 if (descr
->nb_items
==0)
2566 if (!IS_MULTISELECT(descr
))
2567 return descr
->selected_item
;
2569 if (descr
->selected_item
!=-1)
2570 return descr
->selected_item
;
2572 return descr
->focus_item
;
2573 /* otherwise, if the user tries to move the selection with the */
2574 /* arrow keys, we will give the application something to choke on */
2575 case LB_GETTOPINDEX16
:
2576 case LB_GETTOPINDEX
:
2577 return descr
->top_item
;
2579 case LB_GETITEMHEIGHT16
:
2580 case LB_GETITEMHEIGHT
:
2581 return LISTBOX_GetItemHeight( wnd
, descr
, wParam
);
2583 case LB_SETITEMHEIGHT16
:
2584 lParam
= LOWORD(lParam
);
2586 case LB_SETITEMHEIGHT
:
2587 return LISTBOX_SetItemHeight( wnd
, descr
, wParam
, lParam
);
2589 case LB_ITEMFROMPOINT
:
2594 pt
.x
= LOWORD(lParam
);
2595 pt
.y
= HIWORD(lParam
);
2598 rect
.right
= descr
->width
;
2599 rect
.bottom
= descr
->height
;
2601 return MAKELONG( LISTBOX_GetItemFromPoint(wnd
, descr
, pt
.x
, pt
.y
),
2602 !PtInRect( &rect
, pt
) );
2605 case LB_SETCARETINDEX16
:
2606 case LB_SETCARETINDEX
:
2607 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2608 if (LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, !lParam
) == LB_ERR
)
2615 case LB_GETCARETINDEX16
:
2616 case LB_GETCARETINDEX
:
2617 return descr
->focus_item
;
2619 case LB_SETTOPINDEX16
:
2620 case LB_SETTOPINDEX
:
2621 return LISTBOX_SetTopItem( wnd
, descr
, wParam
, TRUE
);
2623 case LB_SETCOLUMNWIDTH16
:
2624 case LB_SETCOLUMNWIDTH
:
2625 return LISTBOX_SetColumnWidth( wnd
, descr
, wParam
);
2627 case LB_GETITEMRECT16
:
2630 ret
= LISTBOX_GetItemRect( wnd
, descr
, (INT16
)wParam
, &rect
);
2631 CONV_RECT32TO16( &rect
, MapSL(lParam
) );
2635 case LB_GETITEMRECT
:
2636 return LISTBOX_GetItemRect( wnd
, descr
, wParam
, (RECT
*)lParam
);
2638 case LB_FINDSTRING16
:
2639 wParam
= (INT
)(INT16
)wParam
;
2640 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2643 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, FALSE
);
2645 case LB_FINDSTRINGEXACT16
:
2646 wParam
= (INT
)(INT16
)wParam
;
2647 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2649 case LB_FINDSTRINGEXACT
:
2650 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2652 case LB_SELECTSTRING16
:
2653 wParam
= (INT
)(INT16
)wParam
;
2654 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2656 case LB_SELECTSTRING
:
2658 INT index
= LISTBOX_FindString( wnd
, descr
, wParam
,
2659 (LPCSTR
)lParam
, FALSE
);
2660 if (index
== LB_ERR
)
2662 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
2667 wParam
= (INT
)(INT16
)wParam
;
2670 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2672 return descr
->items
[wParam
].selected
;
2675 lParam
= (INT
)(INT16
)lParam
;
2678 return LISTBOX_SetSelection( wnd
, descr
, lParam
, wParam
, FALSE
);
2680 case LB_SETCURSEL16
:
2681 wParam
= (INT
)(INT16
)wParam
;
2684 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2685 LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, TRUE
);
2686 return LISTBOX_SetSelection( wnd
, descr
, wParam
, TRUE
, FALSE
);
2688 case LB_GETSELCOUNT16
:
2689 case LB_GETSELCOUNT
:
2690 return LISTBOX_GetSelCount( wnd
, descr
);
2692 case LB_GETSELITEMS16
:
2693 return LISTBOX_GetSelItems16( wnd
, descr
, wParam
,
2694 (LPINT16
)MapSL(lParam
) );
2696 case LB_GETSELITEMS
:
2697 return LISTBOX_GetSelItems( wnd
, descr
, wParam
, (LPINT
)lParam
);
2699 case LB_SELITEMRANGE16
:
2700 case LB_SELITEMRANGE
:
2701 if (LOWORD(lParam
) <= HIWORD(lParam
))
2702 return LISTBOX_SelectItemRange( wnd
, descr
, LOWORD(lParam
),
2703 HIWORD(lParam
), wParam
);
2705 return LISTBOX_SelectItemRange( wnd
, descr
, HIWORD(lParam
),
2706 LOWORD(lParam
), wParam
);
2708 case LB_SELITEMRANGEEX16
:
2709 case LB_SELITEMRANGEEX
:
2710 if ((INT
)lParam
>= (INT
)wParam
)
2711 return LISTBOX_SelectItemRange( wnd
, descr
, wParam
, lParam
, TRUE
);
2713 return LISTBOX_SelectItemRange( wnd
, descr
, lParam
, wParam
, FALSE
);
2715 case LB_GETHORIZONTALEXTENT16
:
2716 case LB_GETHORIZONTALEXTENT
:
2717 return descr
->horz_extent
;
2719 case LB_SETHORIZONTALEXTENT16
:
2720 case LB_SETHORIZONTALEXTENT
:
2721 return LISTBOX_SetHorizontalExtent( wnd
, descr
, wParam
);
2723 case LB_GETANCHORINDEX16
:
2724 case LB_GETANCHORINDEX
:
2725 return descr
->anchor_item
;
2727 case LB_SETANCHORINDEX16
:
2728 wParam
= (INT
)(INT16
)wParam
;
2730 case LB_SETANCHORINDEX
:
2731 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2733 descr
->anchor_item
= (INT
)wParam
;
2737 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2738 * be set automatically (this is different in Win32) */
2739 if (wParam
& DDL_DRIVES
) wParam
|= DDL_EXCLUSIVE
;
2740 return LISTBOX_Directory( wnd
, descr
, wParam
, MapSL(lParam
), FALSE
);
2743 return LISTBOX_Directory( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2746 return descr
->locale
;
2749 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2752 case LB_INITSTORAGE
:
2753 return LISTBOX_InitStorage( wnd
, descr
, wParam
, (DWORD
)lParam
);
2756 return LISTBOX_SetCount( wnd
, descr
, (INT
)wParam
);
2758 case LB_SETTABSTOPS16
:
2759 return LISTBOX_SetTabStops( wnd
, descr
, (INT
)(INT16
)wParam
, MapSL(lParam
), TRUE
);
2761 case LB_SETTABSTOPS
:
2762 return LISTBOX_SetTabStops( wnd
, descr
, wParam
, (LPINT
)lParam
, FALSE
);
2766 if (descr
->caret_on
)
2768 descr
->caret_on
= TRUE
;
2769 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2770 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2775 if (!descr
->caret_on
)
2777 descr
->caret_on
= FALSE
;
2778 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2779 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2783 return LISTBOX_Destroy( wnd
, descr
);
2786 InvalidateRect( hwnd
, NULL
, TRUE
);
2790 LISTBOX_SetRedraw( wnd
, descr
, wParam
!= 0 );
2794 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2799 HDC hdc
= ( wParam
) ? ((HDC
)wParam
)
2800 : BeginPaint( hwnd
, &ps
);
2801 ret
= LISTBOX_Paint( wnd
, descr
, hdc
);
2802 if( !wParam
) EndPaint( hwnd
, &ps
);
2806 LISTBOX_UpdateSize( wnd
, descr
);
2811 LISTBOX_SetFont( wnd
, descr
, (HFONT
)wParam
);
2812 if (lParam
) InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
2815 descr
->in_focus
= TRUE
;
2816 descr
->caret_on
= TRUE
;
2817 if (descr
->focus_item
!= -1)
2818 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2819 SEND_NOTIFICATION( wnd
, descr
, LBN_SETFOCUS
);
2822 descr
->in_focus
= FALSE
;
2823 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2824 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2825 SEND_NOTIFICATION( wnd
, descr
, LBN_KILLFOCUS
);
2828 return LISTBOX_HandleHScroll( wnd
, descr
, wParam
, lParam
);
2830 return LISTBOX_HandleVScroll( wnd
, descr
, wParam
, lParam
);
2831 case WM_MOUSEACTIVATE
:
2832 return MA_NOACTIVATE
;
2834 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2835 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2836 return LISTBOX_HandleMouseWheel( wnd
, descr
, wParam
, lParam
);
2837 case WM_LBUTTONDOWN
:
2838 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2839 (INT16
)LOWORD(lParam
),
2840 (INT16
)HIWORD(lParam
) );
2841 case WM_LBUTTONDBLCLK
:
2842 if (descr
->style
& LBS_NOTIFY
)
2843 SEND_NOTIFICATION( wnd
, descr
, LBN_DBLCLK
);
2846 if (GetCapture() == hwnd
)
2847 LISTBOX_HandleMouseMove( wnd
, descr
, (INT16
)LOWORD(lParam
),
2848 (INT16
)HIWORD(lParam
) );
2851 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2853 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2855 return LISTBOX_HandleChar( wnd
, descr
, wParam
);
2857 return LISTBOX_HandleSystemTimer( wnd
, descr
);
2859 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
2862 HBRUSH hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
2863 wParam
, (LPARAM
)wnd
->hwndSelf
);
2864 GetClientRect(hwnd
, &rect
);
2865 if (hbrush
) FillRect( (HDC
)wParam
, &rect
, hbrush
);
2870 return SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2874 case WM_QUERYDROPOBJECT
:
2879 LPDRAGINFO16 dragInfo
= MapSL( lParam
);
2880 dragInfo
->l
= LISTBOX_GetItemFromPoint( wnd
, descr
, dragInfo
->pt
.x
,
2882 return SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2887 if ((msg
>= WM_USER
) && (msg
< 0xc000))
2888 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2889 hwnd
, msg
, wParam
, lParam
);
2890 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2895 /***********************************************************************
2898 * This is just a wrapper for the real wndproc, it only does window locking
2901 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
2903 WND
* wndPtr
= WIN_FindWndPtr( hwnd
);
2904 LRESULT res
= ListBoxWndProc_locked(wndPtr
,msg
,wParam
,lParam
);
2906 WIN_ReleaseWndPtr(wndPtr
);
2910 /***********************************************************************
2913 LRESULT
COMBO_Directory( LPHEADCOMBO lphc
, UINT attrib
, LPSTR dir
, BOOL bLong
)
2915 WND
*wnd
= WIN_FindWndPtr( lphc
->hWndLBox
);
2919 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2922 LRESULT lRet
= LISTBOX_Directory( wnd
, descr
, attrib
, dir
, bLong
);
2924 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0,
2925 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
2926 WIN_ReleaseWndPtr(wnd
);
2929 WIN_ReleaseWndPtr(wnd
);
2934 /***********************************************************************
2935 * ComboLBWndProc_locked
2937 * The real combo listbox wndproc, but called with locked WND struct.
2939 static inline LRESULT WINAPI
ComboLBWndProc_locked( WND
* wnd
, UINT msg
,
2940 WPARAM wParam
, LPARAM lParam
)
2943 HWND hwnd
= wnd
->hwndSelf
;
2947 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2949 TRACE_(combo
)("[%04x]: msg %s wp %08x lp %08lx\n",
2950 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2952 if( descr
|| msg
== WM_CREATE
)
2954 LPHEADCOMBO lphc
= (descr
) ? descr
->lphc
: NULL
;
2959 #define lpcs ((LPCREATESTRUCTA)lParam)
2960 TRACE_(combo
)("\tpassed parent handle = 0x%08x\n",
2961 (UINT
)lpcs
->lpCreateParams
);
2963 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
2965 return LISTBOX_Create( wnd
, lphc
);
2967 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
2968 (CB_GETTYPE(lphc
) != CBS_SIMPLE
) )
2974 mousePos
.x
= (INT16
)LOWORD(lParam
);
2975 mousePos
.y
= (INT16
)HIWORD(lParam
);
2978 * If we are in a dropdown combobox, we simulate that
2979 * the mouse is captured to show the tracking of the item.
2981 GetClientRect(hwnd
, &clientRect
);
2983 if (PtInRect( &clientRect
, mousePos
))
2985 captured
= descr
->captured
;
2986 descr
->captured
= TRUE
;
2988 LISTBOX_HandleMouseMove( wnd
, descr
,
2989 mousePos
.x
, mousePos
.y
);
2991 descr
->captured
= captured
;
2996 LISTBOX_HandleMouseMove( wnd
, descr
,
2997 mousePos
.x
, mousePos
.y
);
3006 * If we are in Win3.1 look, go with the default behavior.
3008 return ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3011 if (TWEAK_WineLook
> WIN31_LOOK
)
3017 * If the mouse button "up" is not in the listbox,
3018 * we make sure there is no selection by re-selecting the
3019 * item that was selected when the listbox was made visible.
3021 mousePos
.x
= (INT16
)LOWORD(lParam
);
3022 mousePos
.y
= (INT16
)HIWORD(lParam
);
3024 GetClientRect(hwnd
, &clientRect
);
3027 * When the user clicks outside the combobox and the focus
3028 * is lost, the owning combobox will send a fake buttonup with
3029 * 0xFFFFFFF as the mouse location, we must also revert the
3030 * selection to the original selection.
3032 if ( (lParam
== 0xFFFFFFFF) ||
3033 (!PtInRect( &clientRect
, mousePos
)) )
3035 LISTBOX_MoveCaret( wnd
,
3041 return LISTBOX_HandleLButtonUp( wnd
, descr
);
3042 case WM_LBUTTONDBLCLK
:
3043 case WM_LBUTTONDOWN
:
3044 return LISTBOX_HandleLButtonDownCombo(wnd
, descr
, msg
, wParam
,
3045 (INT16
)LOWORD(lParam
),
3046 (INT16
)HIWORD(lParam
) );
3047 case WM_MOUSEACTIVATE
:
3048 return MA_NOACTIVATE
;
3052 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3054 /* for some reason(?) Windows makes it possible to
3055 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3057 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3058 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3059 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3061 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3065 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
3067 case LB_SETCURSEL16
:
3069 lRet
= ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3070 lRet
=(lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
3073 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3078 return ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3081 lRet
= DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3083 TRACE_(combo
)("\t default on msg [%04x]\n", (UINT16
)msg
);
3088 /***********************************************************************
3091 * NOTE: in Windows, winproc address of the ComboLBox is the same
3092 * as that of the Listbox.
3094 * This is just a wrapper for the real wndproc, it only does window locking
3097 LRESULT WINAPI
ComboLBWndProcA( HWND hwnd
, UINT msg
,
3098 WPARAM wParam
, LPARAM lParam
)
3100 WND
*wnd
= WIN_FindWndPtr( hwnd
);
3101 LRESULT res
= ComboLBWndProc_locked(wnd
,msg
,wParam
,lParam
);
3103 WIN_ReleaseWndPtr(wnd
);