4 * Copyright 1996 Alexandre Julliard
12 #include "wine/winuser16.h"
13 #include "wine/winbase16.h"
19 #include "selectors.h"
22 #include "debugtools.h"
25 DEFAULT_DEBUG_CHANNEL(listbox
);
26 DECLARE_DEBUG_CHANNEL(combo
);
35 /* Items array granularity */
36 #define LB_ARRAY_GRANULARITY 16
38 /* Scrolling timeout in ms */
39 #define LB_SCROLL_TIMEOUT 50
41 /* Listbox system timer id */
44 /* flag listbox changed while setredraw false - internal style */
45 #define LBS_DISPLAYCHANGED 0x80000000
50 LPSTR str
; /* Item text */
51 BOOL selected
; /* Is item selected? */
52 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
53 DWORD data
; /* User data */
56 /* Listbox structure */
59 HANDLE heap
; /* Heap for this listbox */
60 HWND owner
; /* Owner window to send notifications to */
61 UINT style
; /* Window style */
62 INT width
; /* Window width */
63 INT height
; /* Window height */
64 LB_ITEMDATA
*items
; /* Array of items */
65 INT nb_items
; /* Number of items */
66 INT top_item
; /* Top visible item */
67 INT selected_item
; /* Selected item */
68 INT focus_item
; /* Item that has the focus */
69 INT anchor_item
; /* Anchor item for extended selection */
70 INT item_height
; /* Default item height */
71 INT page_size
; /* Items per listbox page */
72 INT column_width
; /* Column width for multi-column listboxes */
73 INT horz_extent
; /* Horizontal extent (0 if no hscroll) */
74 INT horz_pos
; /* Horizontal position */
75 INT nb_tabs
; /* Number of tabs in array */
76 INT
*tabs
; /* Array of tabs */
77 BOOL caret_on
; /* Is caret on? */
78 BOOL captured
; /* Is mouse captured? */
80 HFONT font
; /* Current font */
81 LCID locale
; /* Current locale for string comparisons */
82 LPHEADCOMBO lphc
; /* ComboLBox */
86 #define IS_OWNERDRAW(descr) \
87 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
89 #define HAS_STRINGS(descr) \
90 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
93 #define IS_MULTISELECT(descr) \
94 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
96 #define SEND_NOTIFICATION(wnd,descr,code) \
97 (SendMessageA( (descr)->owner, WM_COMMAND, \
98 MAKEWPARAM((wnd)->wIDmenu, (code)), (wnd)->hwndSelf ))
100 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
102 /* Current timer status */
112 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
115 /***********************************************************************
118 void LISTBOX_Dump( WND
*wnd
)
122 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
124 TRACE( "Listbox:\n" );
125 TRACE( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
126 wnd
->hwndSelf
, (UINT
)descr
, descr
->heap
, descr
->nb_items
,
128 for (i
= 0, item
= descr
->items
; i
< descr
->nb_items
; i
++, item
++)
130 TRACE( "%4d: %-40s %d %08lx %3d\n",
131 i
, item
->str
, item
->selected
, item
->data
, item
->height
);
136 /***********************************************************************
137 * LISTBOX_GetCurrentPageSize
139 * Return the current page size
141 static INT
LISTBOX_GetCurrentPageSize( WND
*wnd
, LB_DESCR
*descr
)
144 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
145 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
147 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
149 if (i
== descr
->top_item
) return 1;
150 else return i
- descr
->top_item
;
154 /***********************************************************************
155 * LISTBOX_GetMaxTopIndex
157 * Return the maximum possible index for the top of the listbox.
159 static INT
LISTBOX_GetMaxTopIndex( WND
*wnd
, LB_DESCR
*descr
)
163 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
165 page
= descr
->height
;
166 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
167 if ((page
-= descr
->items
[max
].height
) < 0) break;
168 if (max
< descr
->nb_items
- 1) max
++;
170 else if (descr
->style
& LBS_MULTICOLUMN
)
172 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
173 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
174 max
= (max
- page
) * descr
->page_size
;
178 max
= descr
->nb_items
- descr
->page_size
;
180 if (max
< 0) max
= 0;
185 /***********************************************************************
186 * LISTBOX_UpdateScroll
188 * Update the scrollbars. Should be called whenever the content
189 * of the listbox changes.
191 static void LISTBOX_UpdateScroll( WND
*wnd
, LB_DESCR
*descr
)
195 /* Check the listbox scroll bar flags individually before we call
196 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
197 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
198 scroll bar when we do not need one.
199 if (!(descr->style & WS_VSCROLL)) return;
202 /* It is important that we check descr->style, and not wnd->dwStyle,
203 for WS_VSCROLL, as the former is exactly the one passed in
204 argument to CreateWindow.
205 In Windows (and from now on in Wine :) a listbox created
206 with such a style (no WS_SCROLL) does not update
207 the scrollbar with listbox-related data, thus letting
208 the programmer use it for his/her own purposes. */
210 if (descr
->style
& LBS_NOREDRAW
) return;
211 info
.cbSize
= sizeof(info
);
213 if (descr
->style
& LBS_MULTICOLUMN
)
216 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
217 info
.nPos
= descr
->top_item
/ descr
->page_size
;
218 info
.nPage
= descr
->width
/ descr
->column_width
;
219 if (info
.nPage
< 1) info
.nPage
= 1;
220 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
221 if (descr
->style
& LBS_DISABLENOSCROLL
)
222 info
.fMask
|= SIF_DISABLENOSCROLL
;
223 if (descr
->style
& WS_HSCROLL
)
224 SetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
226 info
.fMask
= SIF_RANGE
;
227 if (descr
->style
& WS_VSCROLL
)
228 SetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
233 info
.nMax
= descr
->nb_items
- 1;
234 info
.nPos
= descr
->top_item
;
235 info
.nPage
= LISTBOX_GetCurrentPageSize( wnd
, descr
);
236 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
237 if (descr
->style
& LBS_DISABLENOSCROLL
)
238 info
.fMask
|= SIF_DISABLENOSCROLL
;
239 if (descr
->style
& WS_VSCROLL
)
240 SetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
242 if (descr
->horz_extent
)
245 info
.nMax
= descr
->horz_extent
- 1;
246 info
.nPos
= descr
->horz_pos
;
247 info
.nPage
= descr
->width
;
248 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
249 if (descr
->style
& LBS_DISABLENOSCROLL
)
250 info
.fMask
|= SIF_DISABLENOSCROLL
;
251 if (descr
->style
& WS_HSCROLL
)
252 SetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
259 /***********************************************************************
262 * Set the top item of the listbox, scrolling up or down if necessary.
264 static LRESULT
LISTBOX_SetTopItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
267 INT max
= LISTBOX_GetMaxTopIndex( wnd
, descr
);
268 if (index
> max
) index
= max
;
269 if (index
< 0) index
= 0;
270 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
271 if (descr
->top_item
== index
) return LB_OKAY
;
272 if (descr
->style
& LBS_MULTICOLUMN
)
274 INT diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
275 if (scroll
&& (abs(diff
) < descr
->width
))
276 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
277 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
285 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
289 if (index
> descr
->top_item
)
291 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
292 diff
-= descr
->items
[i
].height
;
296 for (i
= index
; i
< descr
->top_item
; i
++)
297 diff
+= descr
->items
[i
].height
;
301 diff
= (descr
->top_item
- index
) * descr
->item_height
;
303 if (abs(diff
) < descr
->height
)
304 ScrollWindowEx( wnd
->hwndSelf
, 0, diff
, NULL
, NULL
, 0, NULL
,
305 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
309 if (!scroll
) InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
310 descr
->top_item
= index
;
311 LISTBOX_UpdateScroll( wnd
, descr
);
316 /***********************************************************************
319 * Update the page size. Should be called when the size of
320 * the client area or the item height changes.
322 static void LISTBOX_UpdatePage( WND
*wnd
, LB_DESCR
*descr
)
326 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
328 if (page_size
== descr
->page_size
) return;
329 descr
->page_size
= page_size
;
330 if (descr
->style
& LBS_MULTICOLUMN
)
331 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
332 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
, FALSE
);
336 /***********************************************************************
339 * Update the size of the listbox. Should be called when the size of
340 * the client area changes.
342 static void LISTBOX_UpdateSize( WND
*wnd
, LB_DESCR
*descr
)
346 GetClientRect( wnd
->hwndSelf
, &rect
);
347 descr
->width
= rect
.right
- rect
.left
;
348 descr
->height
= rect
.bottom
- rect
.top
;
349 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
351 UINT remaining
= descr
->height
% descr
->item_height
;
352 if ((descr
->height
> descr
->item_height
) && remaining
)
354 if (!(wnd
->flags
& WIN_ISWIN32
))
355 { /* give a margin for error to 16 bits programs - if we need
356 less than the height of the nonclient area, round to the
357 *next* number of items */
358 int ncheight
= wnd
->rectWindow
.bottom
- wnd
->rectWindow
.top
- descr
->height
;
359 if ((descr
->item_height
- remaining
) <= ncheight
)
360 remaining
= remaining
- descr
->item_height
;
362 TRACE("[%04x]: changing height %d -> %d\n",
363 wnd
->hwndSelf
, descr
->height
,
364 descr
->height
- remaining
);
365 SetWindowPos( wnd
->hwndSelf
, 0, 0, 0,
366 wnd
->rectWindow
.right
- wnd
->rectWindow
.left
,
367 wnd
->rectWindow
.bottom
- wnd
->rectWindow
.top
- remaining
,
368 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
372 TRACE("[%04x]: new size = %d,%d\n",
373 wnd
->hwndSelf
, descr
->width
, descr
->height
);
374 LISTBOX_UpdatePage( wnd
, descr
);
375 LISTBOX_UpdateScroll( wnd
, descr
);
379 /***********************************************************************
380 * LISTBOX_GetItemRect
382 * Get the rectangle enclosing an item, in listbox client coordinates.
383 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
385 static LRESULT
LISTBOX_GetItemRect( WND
*wnd
, LB_DESCR
*descr
, INT index
,
388 /* Index <= 0 is legal even on empty listboxes */
389 if (index
&& (index
>= descr
->nb_items
)) return -1;
390 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
391 if (descr
->style
& LBS_MULTICOLUMN
)
393 INT col
= (index
/ descr
->page_size
) -
394 (descr
->top_item
/ descr
->page_size
);
395 rect
->left
+= col
* descr
->column_width
;
396 rect
->right
= rect
->left
+ descr
->column_width
;
397 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
398 rect
->bottom
= rect
->top
+ descr
->item_height
;
400 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
403 rect
->right
+= descr
->horz_pos
;
404 if ((index
>= 0) && (index
< descr
->nb_items
))
406 if (index
< descr
->top_item
)
408 for (i
= descr
->top_item
-1; i
>= index
; i
--)
409 rect
->top
-= descr
->items
[i
].height
;
413 for (i
= descr
->top_item
; i
< index
; i
++)
414 rect
->top
+= descr
->items
[i
].height
;
416 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
422 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
423 rect
->bottom
= rect
->top
+ descr
->item_height
;
424 rect
->right
+= descr
->horz_pos
;
427 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
428 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
432 /***********************************************************************
433 * LISTBOX_GetItemFromPoint
435 * Return the item nearest from point (x,y) (in client coordinates).
437 static INT
LISTBOX_GetItemFromPoint( WND
*wnd
, LB_DESCR
*descr
,
440 INT index
= descr
->top_item
;
442 if (!descr
->nb_items
) return -1; /* No items */
443 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
448 while (index
< descr
->nb_items
)
450 if ((pos
+= descr
->items
[index
].height
) > y
) break;
459 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
463 else if (descr
->style
& LBS_MULTICOLUMN
)
465 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
466 if (y
>= 0) index
+= y
/ descr
->item_height
;
467 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
468 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
472 index
+= (y
/ descr
->item_height
);
474 if (index
< 0) return 0;
475 if (index
>= descr
->nb_items
) return -1;
480 /***********************************************************************
485 static void LISTBOX_PaintItem( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
,
486 const RECT
*rect
, INT index
, UINT action
)
488 LB_ITEMDATA
*item
= NULL
;
489 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
491 if (IS_OWNERDRAW(descr
))
499 if (action
== ODA_FOCUS
)
500 DrawFocusRect( hdc
, rect
);
502 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
506 /* some programs mess with the clipping region when
507 drawing the item, *and* restore the previous region
508 after they are done, so a region has better to exist
509 else everything ends clipped */
510 GetClientRect(wnd
->hwndSelf
, &r
);
511 hrgn
= CreateRectRgnIndirect(&r
);
512 SelectClipRgn( hdc
, hrgn
);
513 DeleteObject( hrgn
);
515 dis
.CtlType
= ODT_LISTBOX
;
516 dis
.CtlID
= wnd
->wIDmenu
;
517 dis
.hwndItem
= wnd
->hwndSelf
;
518 dis
.itemAction
= action
;
522 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
523 if ((descr
->focus_item
== index
) &&
525 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
526 if (wnd
->dwStyle
& WS_DISABLED
) dis
.itemState
|= ODS_DISABLED
;
527 dis
.itemData
= item
? item
->data
: 0;
529 TRACE("[%04x]: drawitem %d (%s) action=%02x "
530 "state=%02x rect=%d,%d-%d,%d\n",
531 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
532 dis
.itemState
, rect
->left
, rect
->top
,
533 rect
->right
, rect
->bottom
);
534 SendMessageA(descr
->owner
, WM_DRAWITEM
, wnd
->wIDmenu
, (LPARAM
)&dis
);
538 COLORREF oldText
= 0, oldBk
= 0;
540 if (action
== ODA_FOCUS
)
542 DrawFocusRect( hdc
, rect
);
545 if (item
&& item
->selected
)
547 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
548 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
551 TRACE("[%04x]: painting %d (%s) action=%02x "
552 "rect=%d,%d-%d,%d\n",
553 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
554 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
556 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
,
557 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
558 else if (!(descr
->style
& LBS_USETABSTOPS
))
559 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
,
560 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
561 strlen(item
->str
), NULL
);
564 /* Output empty string to paint background in the full width. */
565 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
,
566 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
567 TabbedTextOutA( hdc
, rect
->left
+ 1 , rect
->top
,
568 item
->str
, strlen(item
->str
),
569 descr
->nb_tabs
, descr
->tabs
, 0);
571 if (item
&& item
->selected
)
573 SetBkColor( hdc
, oldBk
);
574 SetTextColor( hdc
, oldText
);
576 if ((descr
->focus_item
== index
) &&
578 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
583 /***********************************************************************
586 * Change the redraw flag.
588 static void LISTBOX_SetRedraw( WND
*wnd
, LB_DESCR
*descr
, BOOL on
)
592 if (!(descr
->style
& LBS_NOREDRAW
)) return;
593 descr
->style
&= ~LBS_NOREDRAW
;
594 if (descr
->style
& LBS_DISPLAYCHANGED
)
595 { /* page was changed while setredraw false, refresh automatically */
596 InvalidateRect(wnd
->hwndSelf
, NULL
, TRUE
);
597 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
598 { /* reset top of page if less than number of items/page */
599 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
600 if (descr
->top_item
< 0) descr
->top_item
= 0;
602 descr
->style
&= ~LBS_DISPLAYCHANGED
;
604 LISTBOX_UpdateScroll( wnd
, descr
);
606 else descr
->style
|= LBS_NOREDRAW
;
610 /***********************************************************************
611 * LISTBOX_RepaintItem
613 * Repaint a single item synchronously.
615 static void LISTBOX_RepaintItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
621 HBRUSH hbrush
, oldBrush
= 0;
623 /* Do not repaint the item if the item is not visible */
624 if (!IsWindowVisible(wnd
->hwndSelf
)) return;
625 if (descr
->style
& LBS_NOREDRAW
)
627 descr
->style
|= LBS_DISPLAYCHANGED
;
630 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) != 1) return;
631 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
))) return;
632 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
633 hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
634 hdc
, (LPARAM
)wnd
->hwndSelf
);
635 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
636 if (wnd
->dwStyle
& WS_DISABLED
)
637 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
638 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
639 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, index
, action
);
640 if (oldFont
) SelectObject( hdc
, oldFont
);
641 if (oldBrush
) SelectObject( hdc
, oldBrush
);
642 ReleaseDC( wnd
->hwndSelf
, hdc
);
646 /***********************************************************************
647 * LISTBOX_InitStorage
649 static LRESULT
LISTBOX_InitStorage( WND
*wnd
, LB_DESCR
*descr
, INT nb_items
,
654 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
655 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
657 nb_items
+= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
658 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
659 nb_items
* sizeof(LB_ITEMDATA
) )))
661 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
669 /***********************************************************************
670 * LISTBOX_SetTabStops
672 static BOOL
LISTBOX_SetTabStops( WND
*wnd
, LB_DESCR
*descr
, INT count
,
673 LPINT tabs
, BOOL short_ints
)
675 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
676 if (descr
->tabs
) HeapFree( descr
->heap
, 0, descr
->tabs
);
677 if (!(descr
->nb_tabs
= count
))
682 /* FIXME: count = 1 */
683 if (!(descr
->tabs
= (INT
*)HeapAlloc( descr
->heap
, 0,
684 descr
->nb_tabs
* sizeof(INT
) )))
689 LPINT16 p
= (LPINT16
)tabs
;
691 TRACE("[%04x]: settabstops ", wnd
->hwndSelf
);
692 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
693 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
694 if (TRACE_ON(listbox
)) DPRINTF("%hd ", descr
->tabs
[i
]);
696 if (TRACE_ON(listbox
)) DPRINTF("\n");
698 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
699 /* FIXME: repaint the window? */
704 /***********************************************************************
707 static LRESULT
LISTBOX_GetText( WND
*wnd
, LB_DESCR
*descr
, INT index
,
710 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
711 if (HAS_STRINGS(descr
))
714 return strlen(descr
->items
[index
].str
);
715 strcpy( buffer
, descr
->items
[index
].str
);
716 return strlen(buffer
);
719 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
720 return sizeof(DWORD
);
725 /***********************************************************************
726 * LISTBOX_FindStringPos
728 * Find the nearest string located before a given string in sort order.
729 * If 'exact' is TRUE, return an error if we don't get an exact match.
731 static INT
LISTBOX_FindStringPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
,
734 INT index
, min
, max
, res
= -1;
736 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
738 max
= descr
->nb_items
;
741 index
= (min
+ max
) / 2;
742 if (HAS_STRINGS(descr
))
743 res
= lstrcmpiA( descr
->items
[index
].str
, str
);
746 COMPAREITEMSTRUCT cis
;
748 cis
.CtlType
= ODT_LISTBOX
;
749 cis
.CtlID
= wnd
->wIDmenu
;
750 cis
.hwndItem
= wnd
->hwndSelf
;
752 cis
.itemData1
= descr
->items
[index
].data
;
754 cis
.itemData2
= (DWORD
)str
;
755 cis
.dwLocaleId
= descr
->locale
;
756 res
= SendMessageA( descr
->owner
, WM_COMPAREITEM
,
757 wnd
->wIDmenu
, (LPARAM
)&cis
);
759 if (!res
) return index
;
760 if (res
> 0) max
= index
;
761 else min
= index
+ 1;
763 return exact
? -1 : max
;
767 /***********************************************************************
768 * LISTBOX_FindFileStrPos
770 * Find the nearest string located before a given string in directory
771 * sort order (i.e. first files, then directories, then drives).
773 static INT
LISTBOX_FindFileStrPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
)
775 INT min
, max
, res
= -1;
777 if (!HAS_STRINGS(descr
))
778 return LISTBOX_FindStringPos( wnd
, descr
, str
, FALSE
);
780 max
= descr
->nb_items
;
783 INT index
= (min
+ max
) / 2;
784 const char *p
= descr
->items
[index
].str
;
785 if (*p
== '[') /* drive or directory */
787 if (*str
!= '[') res
= -1;
788 else if (p
[1] == '-') /* drive */
790 if (str
[1] == '-') res
= str
[2] - p
[2];
795 if (str
[1] == '-') res
= 1;
796 else res
= lstrcmpiA( str
, p
);
801 if (*str
== '[') res
= 1;
802 else res
= lstrcmpiA( str
, p
);
804 if (!res
) return index
;
805 if (res
< 0) max
= index
;
806 else min
= index
+ 1;
812 /***********************************************************************
815 * Find the item beginning with a given string.
817 static INT
LISTBOX_FindString( WND
*wnd
, LB_DESCR
*descr
, INT start
,
818 LPCSTR str
, BOOL exact
)
823 if (start
>= descr
->nb_items
) start
= -1;
824 item
= descr
->items
+ start
+ 1;
825 if (HAS_STRINGS(descr
))
827 if (!str
|| ! str
[0] ) return LB_ERR
;
830 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
831 if (!lstrcmpiA( str
, item
->str
)) return i
;
832 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
833 if (!lstrcmpiA( str
, item
->str
)) return i
;
837 /* Special case for drives and directories: ignore prefix */
838 #define CHECK_DRIVE(item) \
839 if ((item)->str[0] == '[') \
841 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
842 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
846 INT len
= strlen(str
);
847 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
849 if (!lstrncmpiA( str
, item
->str
, len
)) return i
;
852 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
854 if (!lstrncmpiA( str
, item
->str
, len
)) return i
;
862 if (exact
&& (descr
->style
& LBS_SORT
))
863 /* If sorted, use a WM_COMPAREITEM binary search */
864 return LISTBOX_FindStringPos( wnd
, descr
, str
, TRUE
);
866 /* Otherwise use a linear search */
867 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
868 if (item
->data
== (DWORD
)str
) return i
;
869 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
870 if (item
->data
== (DWORD
)str
) return i
;
876 /***********************************************************************
877 * LISTBOX_GetSelCount
879 static LRESULT
LISTBOX_GetSelCount( WND
*wnd
, LB_DESCR
*descr
)
882 LB_ITEMDATA
*item
= descr
->items
;
884 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
885 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
886 if (item
->selected
) count
++;
891 /***********************************************************************
892 * LISTBOX_GetSelItems16
894 static LRESULT
LISTBOX_GetSelItems16( WND
*wnd
, LB_DESCR
*descr
, INT16 max
,
898 LB_ITEMDATA
*item
= descr
->items
;
900 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
901 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
902 if (item
->selected
) array
[count
++] = (INT16
)i
;
907 /***********************************************************************
908 * LISTBOX_GetSelItems32
910 static LRESULT
LISTBOX_GetSelItems( WND
*wnd
, LB_DESCR
*descr
, INT max
,
914 LB_ITEMDATA
*item
= descr
->items
;
916 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
917 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
918 if (item
->selected
) array
[count
++] = i
;
923 /***********************************************************************
926 static LRESULT
LISTBOX_Paint( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
)
928 INT i
, col_pos
= descr
->page_size
- 1;
930 RECT focusRect
= {-1, -1, -1, -1};
932 HBRUSH hbrush
, oldBrush
= 0;
935 if (descr
->style
& LBS_NOREDRAW
) return 0;
937 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
938 if (descr
->style
& LBS_MULTICOLUMN
)
939 rect
.right
= rect
.left
+ descr
->column_width
;
940 else if (descr
->horz_pos
)
942 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
943 rect
.right
+= descr
->horz_pos
;
946 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
947 hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
948 hdc
, (LPARAM
)wnd
->hwndSelf
);
949 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
950 if (wnd
->dwStyle
& WS_DISABLED
)
951 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
953 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
956 /* Special case for empty listbox: paint focus rect */
957 rect
.bottom
= rect
.top
+ descr
->item_height
;
958 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, descr
->focus_item
,
960 rect
.top
= rect
.bottom
;
963 /* Paint all the item, regarding the selection
964 Focus state will be painted after */
965 focusItem
= descr
->focus_item
;
966 descr
->focus_item
= -1;
968 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
970 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
971 rect
.bottom
= rect
.top
+ descr
->item_height
;
973 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
977 /* keep the focus rect, to paint the focus item after */
978 focusRect
.left
= rect
.left
;
979 focusRect
.right
= rect
.right
;
980 focusRect
.top
= rect
.top
;
981 focusRect
.bottom
= rect
.bottom
;
983 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
);
984 rect
.top
= rect
.bottom
;
986 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
988 if (!IS_OWNERDRAW(descr
))
990 /* Clear the bottom of the column */
991 if (rect
.top
< descr
->height
)
993 rect
.bottom
= descr
->height
;
994 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
995 &rect
, NULL
, 0, NULL
);
999 /* Go to the next column */
1000 rect
.left
+= descr
->column_width
;
1001 rect
.right
+= descr
->column_width
;
1003 col_pos
= descr
->page_size
- 1;
1008 if (rect
.top
>= descr
->height
) break;
1012 /* Paint the focus item now */
1013 descr
->focus_item
= focusItem
;
1014 if (focusRect
.top
!= focusRect
.bottom
&& descr
->caret_on
)
1015 LISTBOX_PaintItem( wnd
, descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
);
1017 if (!IS_OWNERDRAW(descr
))
1019 /* Clear the remainder of the client area */
1020 if (rect
.top
< descr
->height
)
1022 rect
.bottom
= descr
->height
;
1023 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1024 &rect
, NULL
, 0, NULL
);
1026 if (rect
.right
< descr
->width
)
1028 rect
.left
= rect
.right
;
1029 rect
.right
= descr
->width
;
1031 rect
.bottom
= descr
->height
;
1032 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1033 &rect
, NULL
, 0, NULL
);
1036 if (oldFont
) SelectObject( hdc
, oldFont
);
1037 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1042 /***********************************************************************
1043 * LISTBOX_InvalidateItems
1045 * Invalidate all items from a given item. If the specified item is not
1046 * visible, nothing happens.
1048 static void LISTBOX_InvalidateItems( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1052 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) == 1)
1054 if (descr
->style
& LBS_NOREDRAW
)
1056 descr
->style
|= LBS_DISPLAYCHANGED
;
1059 rect
.bottom
= descr
->height
;
1060 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
1061 if (descr
->style
& LBS_MULTICOLUMN
)
1063 /* Repaint the other columns */
1064 rect
.left
= rect
.right
;
1065 rect
.right
= descr
->width
;
1067 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
1073 /***********************************************************************
1074 * LISTBOX_GetItemHeight
1076 static LRESULT
LISTBOX_GetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1078 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1080 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1081 return descr
->items
[index
].height
;
1083 else return descr
->item_height
;
1087 /***********************************************************************
1088 * LISTBOX_SetItemHeight
1090 static LRESULT
LISTBOX_SetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1093 if (!height
) height
= 1;
1095 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1097 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1098 TRACE("[%04x]: item %d height = %d\n",
1099 wnd
->hwndSelf
, index
, height
);
1100 descr
->items
[index
].height
= height
;
1101 LISTBOX_UpdateScroll( wnd
, descr
);
1102 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1104 else if (height
!= descr
->item_height
)
1106 TRACE("[%04x]: new height = %d\n",
1107 wnd
->hwndSelf
, height
);
1108 descr
->item_height
= height
;
1109 LISTBOX_UpdatePage( wnd
, descr
);
1110 LISTBOX_UpdateScroll( wnd
, descr
);
1111 InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
1117 /***********************************************************************
1118 * LISTBOX_SetHorizontalPos
1120 static void LISTBOX_SetHorizontalPos( WND
*wnd
, LB_DESCR
*descr
, INT pos
)
1124 if (pos
> descr
->horz_extent
- descr
->width
)
1125 pos
= descr
->horz_extent
- descr
->width
;
1126 if (pos
< 0) pos
= 0;
1127 if (!(diff
= descr
->horz_pos
- pos
)) return;
1128 TRACE("[%04x]: new horz pos = %d\n",
1129 wnd
->hwndSelf
, pos
);
1130 descr
->horz_pos
= pos
;
1131 LISTBOX_UpdateScroll( wnd
, descr
);
1132 if (abs(diff
) < descr
->width
)
1133 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
1134 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1136 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1140 /***********************************************************************
1141 * LISTBOX_SetHorizontalExtent
1143 static LRESULT
LISTBOX_SetHorizontalExtent( WND
*wnd
, LB_DESCR
*descr
,
1146 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1148 if (extent
<= 0) extent
= 1;
1149 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1150 TRACE("[%04x]: new horz extent = %d\n",
1151 wnd
->hwndSelf
, extent
);
1152 descr
->horz_extent
= extent
;
1153 if (descr
->horz_pos
> extent
- descr
->width
)
1154 LISTBOX_SetHorizontalPos( wnd
, descr
, extent
- descr
->width
);
1156 LISTBOX_UpdateScroll( wnd
, descr
);
1161 /***********************************************************************
1162 * LISTBOX_SetColumnWidth
1164 static LRESULT
LISTBOX_SetColumnWidth( WND
*wnd
, LB_DESCR
*descr
, UINT width
)
1166 if (width
== descr
->column_width
) return LB_OKAY
;
1167 TRACE("[%04x]: new column width = %d\n",
1168 wnd
->hwndSelf
, width
);
1169 descr
->column_width
= width
;
1170 LISTBOX_UpdatePage( wnd
, descr
);
1175 /***********************************************************************
1178 * Returns the item height.
1180 static INT
LISTBOX_SetFont( WND
*wnd
, LB_DESCR
*descr
, HFONT font
)
1188 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
)))
1190 ERR("unable to get DC.\n" );
1193 if (font
) oldFont
= SelectObject( hdc
, font
);
1194 GetTextMetricsA( hdc
, &tm
);
1195 if (oldFont
) SelectObject( hdc
, oldFont
);
1196 ReleaseDC( wnd
->hwndSelf
, hdc
);
1197 if (!IS_OWNERDRAW(descr
))
1198 LISTBOX_SetItemHeight( wnd
, descr
, 0, tm
.tmHeight
);
1199 return tm
.tmHeight
;
1203 /***********************************************************************
1204 * LISTBOX_MakeItemVisible
1206 * Make sure that a given item is partially or fully visible.
1208 static void LISTBOX_MakeItemVisible( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1213 if (index
<= descr
->top_item
) top
= index
;
1214 else if (descr
->style
& LBS_MULTICOLUMN
)
1216 INT cols
= descr
->width
;
1217 if (!fully
) cols
+= descr
->column_width
- 1;
1218 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1220 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1221 top
= index
- descr
->page_size
* (cols
- 1);
1223 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1225 INT height
= fully
? descr
->items
[index
].height
: 1;
1226 for (top
= index
; top
> descr
->top_item
; top
--)
1227 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1231 if (index
< descr
->top_item
+ descr
->page_size
) return;
1232 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1233 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1234 top
= index
- descr
->page_size
+ 1;
1236 LISTBOX_SetTopItem( wnd
, descr
, top
, TRUE
);
1239 /***********************************************************************
1240 * LISTBOX_SetCaretIndex
1243 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1246 static LRESULT
LISTBOX_SetCaretIndex( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1247 BOOL fully_visible
)
1249 INT oldfocus
= descr
->focus_item
;
1251 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1252 if (index
== oldfocus
) return LB_OKAY
;
1253 descr
->focus_item
= index
;
1254 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1255 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1257 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1258 if (descr
->caret_on
&& (descr
->in_focus
))
1259 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1265 /***********************************************************************
1266 * LISTBOX_SelectItemRange
1268 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1270 static LRESULT
LISTBOX_SelectItemRange( WND
*wnd
, LB_DESCR
*descr
, INT first
,
1275 /* A few sanity checks */
1277 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1278 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1279 if (last
== -1) last
= descr
->nb_items
- 1;
1280 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1281 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1282 /* selected_item reflects last selected/unselected item on multiple sel */
1283 descr
->selected_item
= last
;
1285 if (on
) /* Turn selection on */
1287 for (i
= first
; i
<= last
; i
++)
1289 if (descr
->items
[i
].selected
) continue;
1290 descr
->items
[i
].selected
= TRUE
;
1291 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1293 LISTBOX_SetCaretIndex( wnd
, descr
, last
, TRUE
);
1295 else /* Turn selection off */
1297 for (i
= first
; i
<= last
; i
++)
1299 if (!descr
->items
[i
].selected
) continue;
1300 descr
->items
[i
].selected
= FALSE
;
1301 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1307 /***********************************************************************
1308 * LISTBOX_SetSelection
1310 static LRESULT
LISTBOX_SetSelection( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1311 BOOL on
, BOOL send_notify
)
1313 TRACE( "index=%d notify=%s\n", index
, send_notify
? "YES" : "NO" );
1315 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1316 if (descr
->style
& LBS_MULTIPLESEL
)
1318 if (index
== -1) /* Select all items */
1319 return LISTBOX_SelectItemRange( wnd
, descr
, 0, -1, on
);
1320 else /* Only one item */
1321 return LISTBOX_SelectItemRange( wnd
, descr
, index
, index
, on
);
1325 INT oldsel
= descr
->selected_item
;
1326 if (index
== oldsel
) return LB_OKAY
;
1327 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1328 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1329 descr
->selected_item
= index
;
1330 if (oldsel
!= -1) LISTBOX_RepaintItem( wnd
, descr
, oldsel
, ODA_SELECT
);
1331 if (index
!= -1) LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_SELECT
);
1332 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( wnd
, descr
,
1333 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1335 if( descr
->lphc
) /* set selection change flag for parent combo */
1336 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1342 /***********************************************************************
1345 * Change the caret position and extend the selection to the new caret.
1347 static void LISTBOX_MoveCaret( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1348 BOOL fully_visible
)
1350 INT oldfocus
= descr
->focus_item
;
1352 if ((index
< 0) || (index
>= descr
->nb_items
))
1355 /* Important, repaint needs to be done in this order if
1356 you want to mimic Windows behavior:
1357 1. Remove the focus and paint the item
1358 2. Remove the selection and paint the item(s)
1359 3. Set the selection and repaint the item(s)
1360 4. Set the focus to 'index' and repaint the item */
1362 /* 1. remove the focus and repaint the item */
1363 descr
->focus_item
= -1;
1364 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1365 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1367 /* 2. then turn off the previous selection */
1368 /* 3. repaint the new selected item */
1369 if (descr
->style
& LBS_EXTENDEDSEL
)
1371 if (descr
->anchor_item
!= -1)
1373 INT first
= min( index
, descr
->anchor_item
);
1374 INT last
= max( index
, descr
->anchor_item
);
1376 LISTBOX_SelectItemRange( wnd
, descr
, 0, first
- 1, FALSE
);
1377 LISTBOX_SelectItemRange( wnd
, descr
, last
+ 1, -1, FALSE
);
1378 LISTBOX_SelectItemRange( wnd
, descr
, first
, last
, TRUE
);
1381 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1383 /* Set selection to new caret item */
1384 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
1387 /* 4. repaint the new item with the focus */
1388 descr
->focus_item
= index
;
1389 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1390 if (descr
->caret_on
&& (descr
->in_focus
))
1391 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1395 /***********************************************************************
1396 * LISTBOX_InsertItem
1398 static LRESULT
LISTBOX_InsertItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1399 LPSTR str
, DWORD data
)
1403 INT oldfocus
= descr
->focus_item
;
1405 if (index
== -1) index
= descr
->nb_items
;
1406 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1407 if (!descr
->items
) max_items
= 0;
1408 else max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
1409 if (descr
->nb_items
== max_items
)
1411 /* We need to grow the array */
1412 max_items
+= LB_ARRAY_GRANULARITY
;
1413 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1414 max_items
* sizeof(LB_ITEMDATA
) )))
1416 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1419 descr
->items
= item
;
1422 /* Insert the item structure */
1424 item
= &descr
->items
[index
];
1425 if (index
< descr
->nb_items
)
1426 RtlMoveMemory( item
+ 1, item
,
1427 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1431 item
->selected
= FALSE
;
1434 /* Get item height */
1436 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1438 MEASUREITEMSTRUCT mis
;
1440 mis
.CtlType
= ODT_LISTBOX
;
1441 mis
.CtlID
= wnd
->wIDmenu
;
1443 mis
.itemData
= descr
->items
[index
].data
;
1444 mis
.itemHeight
= descr
->item_height
;
1445 SendMessageA( descr
->owner
, WM_MEASUREITEM
, wnd
->wIDmenu
, (LPARAM
)&mis
);
1446 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1447 TRACE("[%04x]: measure item %d (%s) = %d\n",
1448 wnd
->hwndSelf
, index
, str
? str
: "", item
->height
);
1451 /* Repaint the items */
1453 LISTBOX_UpdateScroll( wnd
, descr
);
1454 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1456 /* Move selection and focused item */
1457 /* If listbox was empty, set focus to the first item */
1458 if (descr
->nb_items
== 1)
1459 LISTBOX_SetCaretIndex( wnd
, descr
, 0, FALSE
);
1460 /* single select don't change selection index in win31 */
1461 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1463 descr
->selected_item
++;
1464 LISTBOX_SetSelection( wnd
, descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1468 if (index
<= descr
->selected_item
)
1470 descr
->selected_item
++;
1471 descr
->focus_item
= oldfocus
; /* focus not changed */
1478 /***********************************************************************
1479 * LISTBOX_InsertString
1481 static LRESULT
LISTBOX_InsertString( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1484 LPSTR new_str
= NULL
;
1488 if (HAS_STRINGS(descr
))
1491 if (!(new_str
= HEAP_strdupA( descr
->heap
, 0, str
)))
1493 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1497 else data
= (DWORD
)str
;
1499 if (index
== -1) index
= descr
->nb_items
;
1500 if ((ret
= LISTBOX_InsertItem( wnd
, descr
, index
, new_str
, data
)) != 0)
1502 if (new_str
) HeapFree( descr
->heap
, 0, new_str
);
1506 TRACE("[%04x]: added item %d '%s'\n",
1507 wnd
->hwndSelf
, index
, HAS_STRINGS(descr
) ? new_str
: "" );
1512 /***********************************************************************
1513 * LISTBOX_DeleteItem
1515 * Delete the content of an item. 'index' must be a valid index.
1517 static void LISTBOX_DeleteItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1519 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1520 * while Win95 sends it for all items with user data.
1521 * It's probably better to send it too often than not
1522 * often enough, so this is what we do here.
1524 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1526 DELETEITEMSTRUCT dis
;
1528 dis
.CtlType
= ODT_LISTBOX
;
1529 dis
.CtlID
= wnd
->wIDmenu
;
1531 dis
.hwndItem
= wnd
->hwndSelf
;
1532 dis
.itemData
= descr
->items
[index
].data
;
1533 SendMessageA( descr
->owner
, WM_DELETEITEM
, wnd
->wIDmenu
, (LPARAM
)&dis
);
1535 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1536 HeapFree( descr
->heap
, 0, descr
->items
[index
].str
);
1540 /***********************************************************************
1541 * LISTBOX_RemoveItem
1543 * Remove an item from the listbox and delete its content.
1545 static LRESULT
LISTBOX_RemoveItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1550 if (index
== -1) index
= descr
->nb_items
- 1;
1551 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1553 /* We need to invalidate the original rect instead of the updated one. */
1554 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1556 LISTBOX_DeleteItem( wnd
, descr
, index
);
1558 /* Remove the item */
1560 item
= &descr
->items
[index
];
1561 if (index
< descr
->nb_items
-1)
1562 RtlMoveMemory( item
, item
+ 1,
1563 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1565 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1567 /* Shrink the item array if possible */
1569 max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1570 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1572 max_items
-= LB_ARRAY_GRANULARITY
;
1573 item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1574 max_items
* sizeof(LB_ITEMDATA
) );
1575 if (item
) descr
->items
= item
;
1577 /* Repaint the items */
1579 LISTBOX_UpdateScroll( wnd
, descr
);
1580 /* if we removed the scrollbar, reset the top of the list
1581 (correct for owner-drawn ???) */
1582 if (descr
->nb_items
== descr
->page_size
)
1583 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1585 /* Move selection and focused item */
1586 if (!IS_MULTISELECT(descr
))
1588 if (index
== descr
->selected_item
)
1589 descr
->selected_item
= -1;
1590 else if (index
< descr
->selected_item
)
1592 descr
->selected_item
--;
1593 if (ISWIN31
) /* win 31 do not change the selected item number */
1594 LISTBOX_SetSelection( wnd
, descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1598 if (descr
->focus_item
>= descr
->nb_items
)
1600 descr
->focus_item
= descr
->nb_items
- 1;
1601 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1607 /***********************************************************************
1608 * LISTBOX_ResetContent
1610 static void LISTBOX_ResetContent( WND
*wnd
, LB_DESCR
*descr
)
1614 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( wnd
, descr
, i
);
1615 if (descr
->items
) HeapFree( descr
->heap
, 0, descr
->items
);
1616 descr
->nb_items
= 0;
1617 descr
->top_item
= 0;
1618 descr
->selected_item
= -1;
1619 descr
->focus_item
= 0;
1620 descr
->anchor_item
= -1;
1621 descr
->items
= NULL
;
1622 LISTBOX_UpdateScroll( wnd
, descr
);
1623 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1627 /***********************************************************************
1630 static LRESULT
LISTBOX_SetCount( WND
*wnd
, LB_DESCR
*descr
, INT count
)
1634 if (HAS_STRINGS(descr
)) return LB_ERR
;
1635 /* FIXME: this is far from optimal... */
1636 if (count
> descr
->nb_items
)
1638 while (count
> descr
->nb_items
)
1639 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, 0 )) < 0)
1642 else if (count
< descr
->nb_items
)
1644 while (count
< descr
->nb_items
)
1645 if ((ret
= LISTBOX_RemoveItem( wnd
, descr
, -1 )) < 0)
1652 /***********************************************************************
1655 static LRESULT
LISTBOX_Directory( WND
*wnd
, LB_DESCR
*descr
, UINT attrib
,
1656 LPCSTR filespec
, BOOL long_names
)
1659 LRESULT ret
= LB_OKAY
;
1660 WIN32_FIND_DATAA entry
;
1663 if ((handle
= FindFirstFileA(filespec
,&entry
)) == INVALID_HANDLE_VALUE
)
1665 if (GetLastError() != ERROR_NO_MORE_FILES
) return LB_ERR
;
1672 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1674 if (!(attrib
& DDL_DIRECTORY
) ||
1675 !strcmp( entry
.cAlternateFileName
, "." )) continue;
1676 if (long_names
) sprintf( buffer
, "[%s]", entry
.cFileName
);
1677 else sprintf( buffer
, "[%s]", entry
.cAlternateFileName
);
1679 else /* not a directory */
1681 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1682 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1684 if ((attrib
& DDL_EXCLUSIVE
) &&
1685 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1688 if (long_names
) strcpy( buffer
, entry
.cFileName
);
1689 else strcpy( buffer
, entry
.cAlternateFileName
);
1691 if (!long_names
) CharLowerA( buffer
);
1692 pos
= LISTBOX_FindFileStrPos( wnd
, descr
, buffer
);
1693 if ((ret
= LISTBOX_InsertString( wnd
, descr
, pos
, buffer
)) < 0)
1695 } while (FindNextFileA( handle
, &entry
));
1696 FindClose( handle
);
1699 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1701 char buffer
[] = "[-a-]";
1703 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++, buffer
[2]++)
1705 if (!DRIVE_IsValid(drive
)) continue;
1706 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, buffer
)) < 0)
1714 /***********************************************************************
1715 * LISTBOX_HandleVScroll
1717 static LRESULT
LISTBOX_HandleVScroll( WND
*wnd
, LB_DESCR
*descr
,
1718 WPARAM wParam
, LPARAM lParam
)
1722 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1723 switch(LOWORD(wParam
))
1726 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
- 1, TRUE
);
1729 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ 1, TRUE
);
1732 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-
1733 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1736 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+
1737 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1739 case SB_THUMBPOSITION
:
1740 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
), TRUE
);
1743 info
.cbSize
= sizeof(info
);
1744 info
.fMask
= SIF_TRACKPOS
;
1745 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1746 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
, TRUE
);
1749 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1752 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1759 /***********************************************************************
1760 * LISTBOX_HandleHScroll
1762 static LRESULT
LISTBOX_HandleHScroll( WND
*wnd
, LB_DESCR
*descr
,
1763 WPARAM wParam
, LPARAM lParam
)
1768 if (descr
->style
& LBS_MULTICOLUMN
)
1770 switch(LOWORD(wParam
))
1773 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-descr
->page_size
,
1777 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+descr
->page_size
,
1781 page
= descr
->width
/ descr
->column_width
;
1782 if (page
< 1) page
= 1;
1783 LISTBOX_SetTopItem( wnd
, descr
,
1784 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1787 page
= descr
->width
/ descr
->column_width
;
1788 if (page
< 1) page
= 1;
1789 LISTBOX_SetTopItem( wnd
, descr
,
1790 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1792 case SB_THUMBPOSITION
:
1793 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1797 info
.cbSize
= sizeof(info
);
1798 info
.fMask
= SIF_TRACKPOS
;
1799 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1800 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1804 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1807 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1811 else if (descr
->horz_extent
)
1813 switch(LOWORD(wParam
))
1816 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
- 1 );
1819 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
+ 1 );
1822 LISTBOX_SetHorizontalPos( wnd
, descr
,
1823 descr
->horz_pos
- descr
->width
);
1826 LISTBOX_SetHorizontalPos( wnd
, descr
,
1827 descr
->horz_pos
+ descr
->width
);
1829 case SB_THUMBPOSITION
:
1830 LISTBOX_SetHorizontalPos( wnd
, descr
, HIWORD(wParam
) );
1833 info
.cbSize
= sizeof(info
);
1834 info
.fMask
= SIF_TRACKPOS
;
1835 GetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
);
1836 LISTBOX_SetHorizontalPos( wnd
, descr
, info
.nTrackPos
);
1839 LISTBOX_SetHorizontalPos( wnd
, descr
, 0 );
1842 LISTBOX_SetHorizontalPos( wnd
, descr
,
1843 descr
->horz_extent
- descr
->width
);
1850 static LRESULT
LISTBOX_HandleMouseWheel(WND
*wnd
, LB_DESCR
*descr
,WPARAM wParam
, LPARAM lParam
)
1852 short gcWheelDelta
= 0;
1853 UINT pulScrollLines
= 3;
1855 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1857 gcWheelDelta
-= (short) HIWORD(wParam
);
1859 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
1861 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
1862 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
1863 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ cLineScroll
, TRUE
);
1868 /***********************************************************************
1869 * LISTBOX_HandleLButtonDown
1871 static LRESULT
LISTBOX_HandleLButtonDown( WND
*wnd
, LB_DESCR
*descr
,
1872 WPARAM wParam
, INT x
, INT y
)
1874 INT index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1875 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1876 wnd
->hwndSelf
, x
, y
, index
);
1877 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
1879 if (!descr
->in_focus
)
1881 if( !descr
->lphc
) SetFocus( wnd
->hwndSelf
);
1882 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
1883 : descr
->lphc
->self
->hwndSelf
);
1888 if (descr
->style
& LBS_EXTENDEDSEL
)
1890 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1891 if (wParam
& MK_CONTROL
)
1893 LISTBOX_SetCaretIndex( wnd
, descr
, index
, FALSE
);
1894 LISTBOX_SetSelection( wnd
, descr
, index
,
1895 !descr
->items
[index
].selected
,
1896 (descr
->style
& LBS_NOTIFY
) != 0);
1898 else LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1902 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1903 LISTBOX_SetSelection( wnd
, descr
, index
,
1904 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1905 !descr
->items
[index
].selected
),
1906 (descr
->style
& LBS_NOTIFY
) != 0 );
1910 descr
->captured
= TRUE
;
1911 SetCapture( wnd
->hwndSelf
);
1912 if (index
!= -1 && !descr
->lphc
)
1914 if (descr
->style
& LBS_NOTIFY
)
1915 SendMessageA( descr
->owner
, WM_LBTRACKPOINT
, index
,
1916 MAKELPARAM( x
, y
) );
1917 if (wnd
->dwExStyle
& WS_EX_DRAGDETECT
)
1924 if (DragDetect( wnd
->hwndSelf
, pt
))
1925 SendMessageA( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
1932 /*************************************************************************
1933 * LISTBOX_HandleLButtonDownCombo [Internal]
1935 * Process LButtonDown message for the ComboListBox
1938 * pWnd [I] The windows internal structure
1939 * pDescr [I] The ListBox internal structure
1940 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
1941 * x [I] X Mouse Coordinate
1942 * y [I] Y Mouse Coordinate
1945 * 0 since we are processing the WM_LBUTTONDOWN Message
1948 * This function is only to be used when a ListBox is a ComboListBox
1951 static LRESULT
LISTBOX_HandleLButtonDownCombo( WND
*pWnd
, LB_DESCR
*pDescr
,
1952 UINT msg
, WPARAM wParam
, INT x
, INT y
)
1954 RECT clientRect
, screenRect
;
1960 GetClientRect(pWnd
->hwndSelf
, &clientRect
);
1962 if(PtInRect(&clientRect
, mousePos
))
1964 /* MousePos is in client, resume normal processing */
1965 if (msg
== WM_LBUTTONDOWN
)
1967 pDescr
->lphc
->droppedIndex
= pDescr
->nb_items
? pDescr
->selected_item
: -1;
1968 return LISTBOX_HandleLButtonDown( pWnd
, pDescr
, wParam
, x
, y
);
1970 else if (pDescr
->style
& LBS_NOTIFY
)
1971 SEND_NOTIFICATION( pWnd
, pDescr
, LBN_DBLCLK
);
1976 POINT screenMousePos
;
1977 HWND hWndOldCapture
;
1979 /* Check the Non-Client Area */
1980 screenMousePos
= mousePos
;
1981 hWndOldCapture
= GetCapture();
1983 GetWindowRect(pWnd
->hwndSelf
, &screenRect
);
1984 ClientToScreen(pWnd
->hwndSelf
, &screenMousePos
);
1986 if(!PtInRect(&screenRect
, screenMousePos
))
1988 LISTBOX_SetSelection( pWnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
, FALSE
);
1989 COMBO_FlipListbox( pDescr
->lphc
, FALSE
, FALSE
);
1994 /* Check to see the NC is a scrollbar */
1996 /* Check Vertical scroll bar */
1997 if (pWnd
->dwStyle
& WS_VSCROLL
)
1999 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2000 if (PtInRect( &clientRect
, mousePos
))
2002 nHitTestType
= HTVSCROLL
;
2005 /* Check horizontal scroll bar */
2006 if (pWnd
->dwStyle
& WS_HSCROLL
)
2008 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2009 if (PtInRect( &clientRect
, mousePos
))
2011 nHitTestType
= HTHSCROLL
;
2014 /* Windows sends this message when a scrollbar is clicked
2017 if(nHitTestType
!= 0)
2019 SendMessageA(pWnd
->hwndSelf
, WM_NCLBUTTONDOWN
, nHitTestType
,
2020 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2022 /* Resume the Capture after scrolling is complete
2024 if(hWndOldCapture
!= 0)
2026 SetCapture(hWndOldCapture
);
2033 /***********************************************************************
2034 * LISTBOX_HandleLButtonUp
2036 static LRESULT
LISTBOX_HandleLButtonUp( WND
*wnd
, LB_DESCR
*descr
)
2038 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2039 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2040 LISTBOX_Timer
= LB_TIMER_NONE
;
2041 if (descr
->captured
)
2043 descr
->captured
= FALSE
;
2044 if (GetCapture() == wnd
->hwndSelf
) ReleaseCapture();
2045 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2046 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2052 /***********************************************************************
2053 * LISTBOX_HandleTimer
2055 * Handle scrolling upon a timer event.
2056 * Return TRUE if scrolling should continue.
2058 static LRESULT
LISTBOX_HandleTimer( WND
*wnd
, LB_DESCR
*descr
,
2059 INT index
, TIMER_DIRECTION dir
)
2064 if (descr
->top_item
) index
= descr
->top_item
- 1;
2068 if (descr
->top_item
) index
-= descr
->page_size
;
2071 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( wnd
, descr
);
2072 if (index
== descr
->focus_item
) index
++;
2073 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2075 case LB_TIMER_RIGHT
:
2076 if (index
+ descr
->page_size
< descr
->nb_items
)
2077 index
+= descr
->page_size
;
2082 if (index
== descr
->focus_item
) return FALSE
;
2083 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
2088 /***********************************************************************
2089 * LISTBOX_HandleSystemTimer
2091 * WM_SYSTIMER handler.
2093 static LRESULT
LISTBOX_HandleSystemTimer( WND
*wnd
, LB_DESCR
*descr
)
2095 if (!LISTBOX_HandleTimer( wnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
2097 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2098 LISTBOX_Timer
= LB_TIMER_NONE
;
2104 /***********************************************************************
2105 * LISTBOX_HandleMouseMove
2107 * WM_MOUSEMOVE handler.
2109 static void LISTBOX_HandleMouseMove( WND
*wnd
, LB_DESCR
*descr
,
2113 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2115 if (!descr
->captured
) return;
2117 if (descr
->style
& LBS_MULTICOLUMN
)
2120 else if (y
>= descr
->item_height
* descr
->page_size
)
2121 y
= descr
->item_height
* descr
->page_size
- 1;
2125 dir
= LB_TIMER_LEFT
;
2128 else if (x
>= descr
->width
)
2130 dir
= LB_TIMER_RIGHT
;
2131 x
= descr
->width
- 1;
2136 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2137 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2140 index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
2141 if (index
== -1) index
= descr
->focus_item
;
2142 if (!LISTBOX_HandleTimer( wnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2144 /* Start/stop the system timer */
2146 if (dir
!= LB_TIMER_NONE
)
2147 SetSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2148 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2149 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2150 LISTBOX_Timer
= dir
;
2154 /***********************************************************************
2155 * LISTBOX_HandleKeyDown
2157 static LRESULT
LISTBOX_HandleKeyDown( WND
*wnd
, LB_DESCR
*descr
, WPARAM wParam
)
2160 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2161 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2162 bForceSelection
= FALSE
; /* only for single select list */
2164 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2166 caret
= SendMessageA( descr
->owner
, WM_VKEYTOITEM
,
2167 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2169 if (caret
== -2) return 0;
2171 if (caret
== -1) switch(wParam
)
2174 if (descr
->style
& LBS_MULTICOLUMN
)
2176 bForceSelection
= FALSE
;
2177 if (descr
->focus_item
>= descr
->page_size
)
2178 caret
= descr
->focus_item
- descr
->page_size
;
2183 caret
= descr
->focus_item
- 1;
2184 if (caret
< 0) caret
= 0;
2187 if (descr
->style
& LBS_MULTICOLUMN
)
2189 bForceSelection
= FALSE
;
2190 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2191 caret
= descr
->focus_item
+ descr
->page_size
;
2196 caret
= descr
->focus_item
+ 1;
2197 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2201 if (descr
->style
& LBS_MULTICOLUMN
)
2203 INT page
= descr
->width
/ descr
->column_width
;
2204 if (page
< 1) page
= 1;
2205 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2207 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(wnd
,descr
)+1;
2208 if (caret
< 0) caret
= 0;
2211 if (descr
->style
& LBS_MULTICOLUMN
)
2213 INT page
= descr
->width
/ descr
->column_width
;
2214 if (page
< 1) page
= 1;
2215 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2217 else caret
= descr
->focus_item
+LISTBOX_GetCurrentPageSize(wnd
,descr
)-1;
2218 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2224 caret
= descr
->nb_items
- 1;
2227 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2228 else if (descr
->style
& LBS_MULTIPLESEL
)
2230 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
,
2231 !descr
->items
[descr
->focus_item
].selected
,
2232 (descr
->style
& LBS_NOTIFY
) != 0 );
2236 bForceSelection
= FALSE
;
2238 if (bForceSelection
) /* focused item is used instead of key */
2239 caret
= descr
->focus_item
;
2242 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
2243 !(GetKeyState( VK_SHIFT
) & 0x8000))
2244 descr
->anchor_item
= caret
;
2245 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2246 LISTBOX_SetSelection( wnd
, descr
, caret
, TRUE
, FALSE
);
2247 if (descr
->style
& LBS_NOTIFY
)
2249 if( descr
->lphc
&& CB_GETTYPE(descr
->lphc
) != CBS_SIMPLE
)
2251 /* make sure that combo parent doesn't hide us */
2252 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2254 if (descr
->nb_items
) SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2261 /***********************************************************************
2262 * LISTBOX_HandleChar
2264 static LRESULT
LISTBOX_HandleChar( WND
*wnd
, LB_DESCR
*descr
,
2270 str
[0] = wParam
& 0xff;
2273 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2275 caret
= SendMessageA( descr
->owner
, WM_CHARTOITEM
,
2276 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2278 if (caret
== -2) return 0;
2281 caret
= LISTBOX_FindString( wnd
, descr
, descr
->focus_item
, str
, FALSE
);
2284 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2285 LISTBOX_SetSelection( wnd
, descr
, caret
, TRUE
, FALSE
);
2286 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2287 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2288 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2294 /***********************************************************************
2297 static BOOL
LISTBOX_Create( WND
*wnd
, LPHEADCOMBO lphc
)
2300 MEASUREITEMSTRUCT mis
;
2303 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2305 if (!(descr
->heap
= HeapCreate( 0, 0x10000, 0 )))
2307 HeapFree( GetProcessHeap(), 0, descr
);
2310 GetClientRect( wnd
->hwndSelf
, &rect
);
2311 descr
->owner
= GetParent( wnd
->hwndSelf
);
2312 descr
->style
= wnd
->dwStyle
;
2313 descr
->width
= rect
.right
- rect
.left
;
2314 descr
->height
= rect
.bottom
- rect
.top
;
2315 descr
->items
= NULL
;
2316 descr
->nb_items
= 0;
2317 descr
->top_item
= 0;
2318 descr
->selected_item
= -1;
2319 descr
->focus_item
= 0;
2320 descr
->anchor_item
= -1;
2321 descr
->item_height
= 1;
2322 descr
->page_size
= 1;
2323 descr
->column_width
= 150;
2324 descr
->horz_extent
= (wnd
->dwStyle
& WS_HSCROLL
) ? 1 : 0;
2325 descr
->horz_pos
= 0;
2328 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2329 descr
->in_focus
= FALSE
;
2330 descr
->captured
= FALSE
;
2332 descr
->locale
= 0; /* FIXME */
2335 if( ( GetExpWinVer16( wnd
->hInstance
) & 0xFF00 ) == 0x0300
2336 && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2338 /* Win95 document "List Box Differences" from MSDN:
2339 If a list box in a version 3.x application has either the
2340 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2341 horizontal and vertical scroll bars.
2343 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2348 TRACE_(combo
)("[%04x]: resetting owner %04x -> %04x\n",
2349 wnd
->hwndSelf
, descr
->owner
, lphc
->self
->hwndSelf
);
2350 descr
->owner
= lphc
->self
->hwndSelf
;
2353 *(LB_DESCR
**)wnd
->wExtra
= descr
;
2355 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2357 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2358 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2359 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2360 descr
->item_height
= LISTBOX_SetFont( wnd
, descr
, 0 );
2362 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2364 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2366 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2367 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2371 mis
.CtlType
= ODT_LISTBOX
;
2372 mis
.CtlID
= wnd
->wIDmenu
;
2376 mis
.itemHeight
= descr
->item_height
;
2377 SendMessageA( descr
->owner
, WM_MEASUREITEM
, wnd
->wIDmenu
, (LPARAM
)&mis
);
2378 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2386 /***********************************************************************
2389 static BOOL
LISTBOX_Destroy( WND
*wnd
, LB_DESCR
*descr
)
2391 LISTBOX_ResetContent( wnd
, descr
);
2392 HeapDestroy( descr
->heap
);
2393 HeapFree( GetProcessHeap(), 0, descr
);
2399 /***********************************************************************
2402 static inline LRESULT WINAPI
ListBoxWndProc_locked( WND
* wnd
, UINT msg
,
2403 WPARAM wParam
, LPARAM lParam
)
2407 HWND hwnd
= wnd
->hwndSelf
;
2410 if (!(descr
= *(LB_DESCR
**)wnd
->wExtra
))
2416 if (!LISTBOX_Create( wnd
, NULL
))
2418 TRACE("creating wnd=%04x descr=%p\n",
2419 hwnd
, *(LB_DESCR
**)wnd
->wExtra
);
2425 * When a listbox is not in a combobox and the look
2426 * is win95, the WS_BORDER style is replaced with
2427 * the WS_EX_CLIENTEDGE style.
2429 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
2430 (wnd
->dwStyle
& WS_BORDER
) )
2432 wnd
->dwExStyle
|= WS_EX_CLIENTEDGE
;
2433 wnd
->dwStyle
&= ~ WS_BORDER
;
2438 /* Ignore all other messages before we get a WM_CREATE */
2439 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2442 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2443 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2446 case LB_RESETCONTENT16
:
2447 case LB_RESETCONTENT
:
2448 LISTBOX_ResetContent( wnd
, descr
);
2451 case LB_ADDSTRING16
:
2452 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2455 wParam
= LISTBOX_FindStringPos( wnd
, descr
, (LPCSTR
)lParam
, FALSE
);
2456 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2458 case LB_INSERTSTRING16
:
2459 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2460 wParam
= (INT
)(INT16
)wParam
;
2462 case LB_INSERTSTRING
:
2463 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2466 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2469 wParam
= LISTBOX_FindFileStrPos( wnd
, descr
, (LPCSTR
)lParam
);
2470 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2472 case LB_DELETESTRING16
:
2473 case LB_DELETESTRING
:
2474 if (LISTBOX_RemoveItem( wnd
, descr
, wParam
) != LB_ERR
)
2475 return descr
->nb_items
;
2479 case LB_GETITEMDATA16
:
2480 case LB_GETITEMDATA
:
2481 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2483 return descr
->items
[wParam
].data
;
2485 case LB_SETITEMDATA16
:
2486 case LB_SETITEMDATA
:
2487 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2489 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2494 return descr
->nb_items
;
2497 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2500 return LISTBOX_GetText( wnd
, descr
, wParam
, (LPSTR
)lParam
);
2502 case LB_GETTEXTLEN16
:
2505 if (wParam
>= descr
->nb_items
)
2507 return (HAS_STRINGS(descr
) ? strlen(descr
->items
[wParam
].str
)
2510 case LB_GETCURSEL16
:
2512 if (descr
->nb_items
==0)
2514 if (!IS_MULTISELECT(descr
))
2515 return descr
->selected_item
;
2517 if (descr
->selected_item
!=-1)
2518 return descr
->selected_item
;
2520 return descr
->focus_item
;
2521 /* otherwise, if the user tries to move the selection with the */
2522 /* arrow keys, we will give the application something to choke on */
2523 case LB_GETTOPINDEX16
:
2524 case LB_GETTOPINDEX
:
2525 return descr
->top_item
;
2527 case LB_GETITEMHEIGHT16
:
2528 case LB_GETITEMHEIGHT
:
2529 return LISTBOX_GetItemHeight( wnd
, descr
, wParam
);
2531 case LB_SETITEMHEIGHT16
:
2532 lParam
= LOWORD(lParam
);
2534 case LB_SETITEMHEIGHT
:
2535 return LISTBOX_SetItemHeight( wnd
, descr
, wParam
, lParam
);
2537 case LB_ITEMFROMPOINT
:
2542 pt
.x
= LOWORD(lParam
);
2543 pt
.y
= HIWORD(lParam
);
2546 rect
.right
= descr
->width
;
2547 rect
.bottom
= descr
->height
;
2549 return MAKELONG( LISTBOX_GetItemFromPoint(wnd
, descr
, pt
.x
, pt
.y
),
2550 !PtInRect( &rect
, pt
) );
2553 case LB_SETCARETINDEX16
:
2554 case LB_SETCARETINDEX
:
2555 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2556 if (LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, !lParam
) == LB_ERR
)
2563 case LB_GETCARETINDEX16
:
2564 case LB_GETCARETINDEX
:
2565 return descr
->focus_item
;
2567 case LB_SETTOPINDEX16
:
2568 case LB_SETTOPINDEX
:
2569 return LISTBOX_SetTopItem( wnd
, descr
, wParam
, TRUE
);
2571 case LB_SETCOLUMNWIDTH16
:
2572 case LB_SETCOLUMNWIDTH
:
2573 return LISTBOX_SetColumnWidth( wnd
, descr
, wParam
);
2575 case LB_GETITEMRECT16
:
2578 ret
= LISTBOX_GetItemRect( wnd
, descr
, (INT16
)wParam
, &rect
);
2579 CONV_RECT32TO16( &rect
, (RECT16
*)PTR_SEG_TO_LIN(lParam
) );
2583 case LB_GETITEMRECT
:
2584 return LISTBOX_GetItemRect( wnd
, descr
, wParam
, (RECT
*)lParam
);
2586 case LB_FINDSTRING16
:
2587 wParam
= (INT
)(INT16
)wParam
;
2588 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2591 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, FALSE
);
2593 case LB_FINDSTRINGEXACT16
:
2594 wParam
= (INT
)(INT16
)wParam
;
2595 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2597 case LB_FINDSTRINGEXACT
:
2598 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2600 case LB_SELECTSTRING16
:
2601 wParam
= (INT
)(INT16
)wParam
;
2602 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2604 case LB_SELECTSTRING
:
2606 INT index
= LISTBOX_FindString( wnd
, descr
, wParam
,
2607 (LPCSTR
)lParam
, FALSE
);
2608 if (index
== LB_ERR
)
2610 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
2615 wParam
= (INT
)(INT16
)wParam
;
2618 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2620 return descr
->items
[wParam
].selected
;
2623 lParam
= (INT
)(INT16
)lParam
;
2626 return LISTBOX_SetSelection( wnd
, descr
, lParam
, wParam
, FALSE
);
2628 case LB_SETCURSEL16
:
2629 wParam
= (INT
)(INT16
)wParam
;
2632 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2633 LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, TRUE
);
2634 return LISTBOX_SetSelection( wnd
, descr
, wParam
, TRUE
, FALSE
);
2636 case LB_GETSELCOUNT16
:
2637 case LB_GETSELCOUNT
:
2638 return LISTBOX_GetSelCount( wnd
, descr
);
2640 case LB_GETSELITEMS16
:
2641 return LISTBOX_GetSelItems16( wnd
, descr
, wParam
,
2642 (LPINT16
)PTR_SEG_TO_LIN(lParam
) );
2644 case LB_GETSELITEMS
:
2645 return LISTBOX_GetSelItems( wnd
, descr
, wParam
, (LPINT
)lParam
);
2647 case LB_SELITEMRANGE16
:
2648 case LB_SELITEMRANGE
:
2649 if (LOWORD(lParam
) <= HIWORD(lParam
))
2650 return LISTBOX_SelectItemRange( wnd
, descr
, LOWORD(lParam
),
2651 HIWORD(lParam
), wParam
);
2653 return LISTBOX_SelectItemRange( wnd
, descr
, HIWORD(lParam
),
2654 LOWORD(lParam
), wParam
);
2656 case LB_SELITEMRANGEEX16
:
2657 case LB_SELITEMRANGEEX
:
2658 if ((INT
)lParam
>= (INT
)wParam
)
2659 return LISTBOX_SelectItemRange( wnd
, descr
, wParam
, lParam
, TRUE
);
2661 return LISTBOX_SelectItemRange( wnd
, descr
, lParam
, wParam
, FALSE
);
2663 case LB_GETHORIZONTALEXTENT16
:
2664 case LB_GETHORIZONTALEXTENT
:
2665 return descr
->horz_extent
;
2667 case LB_SETHORIZONTALEXTENT16
:
2668 case LB_SETHORIZONTALEXTENT
:
2669 return LISTBOX_SetHorizontalExtent( wnd
, descr
, wParam
);
2671 case LB_GETANCHORINDEX16
:
2672 case LB_GETANCHORINDEX
:
2673 return descr
->anchor_item
;
2675 case LB_SETANCHORINDEX16
:
2676 wParam
= (INT
)(INT16
)wParam
;
2678 case LB_SETANCHORINDEX
:
2679 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2681 descr
->anchor_item
= (INT
)wParam
;
2685 return LISTBOX_Directory( wnd
, descr
, wParam
,
2686 (LPCSTR
)PTR_SEG_TO_LIN(lParam
), FALSE
);
2689 return LISTBOX_Directory( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2692 return descr
->locale
;
2695 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2698 case LB_INITSTORAGE
:
2699 return LISTBOX_InitStorage( wnd
, descr
, wParam
, (DWORD
)lParam
);
2702 return LISTBOX_SetCount( wnd
, descr
, (INT
)wParam
);
2704 case LB_SETTABSTOPS16
:
2705 return LISTBOX_SetTabStops( wnd
, descr
, (INT
)(INT16
)wParam
,
2706 (LPINT
)PTR_SEG_TO_LIN(lParam
), TRUE
);
2708 case LB_SETTABSTOPS
:
2709 return LISTBOX_SetTabStops( wnd
, descr
, wParam
, (LPINT
)lParam
, FALSE
);
2713 if (descr
->caret_on
)
2715 descr
->caret_on
= TRUE
;
2716 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2717 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2722 if (!descr
->caret_on
)
2724 descr
->caret_on
= FALSE
;
2725 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2726 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2730 return LISTBOX_Destroy( wnd
, descr
);
2733 InvalidateRect( hwnd
, NULL
, TRUE
);
2737 LISTBOX_SetRedraw( wnd
, descr
, wParam
!= 0 );
2741 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2746 HDC hdc
= ( wParam
) ? ((HDC
)wParam
)
2747 : BeginPaint( hwnd
, &ps
);
2748 ret
= LISTBOX_Paint( wnd
, descr
, hdc
);
2749 if( !wParam
) EndPaint( hwnd
, &ps
);
2753 LISTBOX_UpdateSize( wnd
, descr
);
2758 LISTBOX_SetFont( wnd
, descr
, (HFONT
)wParam
);
2759 if (lParam
) InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
2762 descr
->in_focus
= TRUE
;
2763 descr
->caret_on
= TRUE
;
2764 if (descr
->focus_item
!= -1)
2765 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2766 SEND_NOTIFICATION( wnd
, descr
, LBN_SETFOCUS
);
2769 descr
->in_focus
= FALSE
;
2770 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2771 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2772 SEND_NOTIFICATION( wnd
, descr
, LBN_KILLFOCUS
);
2775 return LISTBOX_HandleHScroll( wnd
, descr
, wParam
, lParam
);
2777 return LISTBOX_HandleVScroll( wnd
, descr
, wParam
, lParam
);
2778 case WM_MOUSEACTIVATE
:
2779 return MA_NOACTIVATE
;
2781 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2782 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2783 return LISTBOX_HandleMouseWheel( wnd
, descr
, wParam
, lParam
);
2784 case WM_LBUTTONDOWN
:
2785 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2786 (INT16
)LOWORD(lParam
),
2787 (INT16
)HIWORD(lParam
) );
2788 case WM_LBUTTONDBLCLK
:
2789 if (descr
->style
& LBS_NOTIFY
)
2790 SEND_NOTIFICATION( wnd
, descr
, LBN_DBLCLK
);
2793 if (GetCapture() == hwnd
)
2794 LISTBOX_HandleMouseMove( wnd
, descr
, (INT16
)LOWORD(lParam
),
2795 (INT16
)HIWORD(lParam
) );
2798 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2800 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2802 return LISTBOX_HandleChar( wnd
, descr
, wParam
);
2804 return LISTBOX_HandleSystemTimer( wnd
, descr
);
2806 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
2809 HBRUSH hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
2810 wParam
, (LPARAM
)wnd
->hwndSelf
);
2811 GetClientRect(hwnd
, &rect
);
2812 if (hbrush
) FillRect( (HDC
)wParam
, &rect
, hbrush
);
2817 return SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2821 case WM_QUERYDROPOBJECT
:
2826 LPDRAGINFO dragInfo
= (LPDRAGINFO
)PTR_SEG_TO_LIN( (SEGPTR
)lParam
);
2827 dragInfo
->l
= LISTBOX_GetItemFromPoint( wnd
, descr
, dragInfo
->pt
.x
,
2829 return SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2834 if ((msg
>= WM_USER
) && (msg
< 0xc000))
2835 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2836 hwnd
, msg
, wParam
, lParam
);
2837 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2842 /***********************************************************************
2845 * This is just a wrapper for the real wndproc, it only does window locking
2848 LRESULT WINAPI
ListBoxWndProc( HWND hwnd
, UINT msg
,
2849 WPARAM wParam
, LPARAM lParam
)
2851 WND
* wndPtr
= WIN_FindWndPtr( hwnd
);
2852 LRESULT res
= ListBoxWndProc_locked(wndPtr
,msg
,wParam
,lParam
);
2854 WIN_ReleaseWndPtr(wndPtr
);
2858 /***********************************************************************
2861 LRESULT
COMBO_Directory( LPHEADCOMBO lphc
, UINT attrib
, LPSTR dir
, BOOL bLong
)
2863 WND
*wnd
= WIN_FindWndPtr( lphc
->hWndLBox
);
2867 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2870 LRESULT lRet
= LISTBOX_Directory( wnd
, descr
, attrib
, dir
, bLong
);
2872 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0,
2873 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
2874 WIN_ReleaseWndPtr(wnd
);
2877 WIN_ReleaseWndPtr(wnd
);
2882 /***********************************************************************
2883 * ComboLBWndProc_locked
2885 * The real combo listbox wndproc, but called with locked WND struct.
2887 static inline LRESULT WINAPI
ComboLBWndProc_locked( WND
* wnd
, UINT msg
,
2888 WPARAM wParam
, LPARAM lParam
)
2891 HWND hwnd
= wnd
->hwndSelf
;
2895 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2897 TRACE_(combo
)("[%04x]: msg %s wp %08x lp %08lx\n",
2898 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2900 if( descr
|| msg
== WM_CREATE
)
2902 LPHEADCOMBO lphc
= (descr
) ? descr
->lphc
: NULL
;
2907 #define lpcs ((LPCREATESTRUCTA)lParam)
2908 TRACE_(combo
)("\tpassed parent handle = 0x%08x\n",
2909 (UINT
)lpcs
->lpCreateParams
);
2911 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
2913 return LISTBOX_Create( wnd
, lphc
);
2915 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
2916 (CB_GETTYPE(lphc
) != CBS_SIMPLE
) )
2922 mousePos
.x
= (INT16
)LOWORD(lParam
);
2923 mousePos
.y
= (INT16
)HIWORD(lParam
);
2926 * If we are in a dropdown combobox, we simulate that
2927 * the mouse is captured to show the tracking of the item.
2929 GetClientRect(hwnd
, &clientRect
);
2931 if (PtInRect( &clientRect
, mousePos
))
2933 captured
= descr
->captured
;
2934 descr
->captured
= TRUE
;
2936 LISTBOX_HandleMouseMove( wnd
, descr
,
2937 mousePos
.x
, mousePos
.y
);
2939 descr
->captured
= captured
;
2944 LISTBOX_HandleMouseMove( wnd
, descr
,
2945 mousePos
.x
, mousePos
.y
);
2954 * If we are in Win3.1 look, go with the default behavior.
2956 return ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2959 if (TWEAK_WineLook
> WIN31_LOOK
)
2965 * If the mouse button "up" is not in the listbox,
2966 * we make sure there is no selection by re-selecting the
2967 * item that was selected when the listbox was made visible.
2969 mousePos
.x
= (INT16
)LOWORD(lParam
);
2970 mousePos
.y
= (INT16
)HIWORD(lParam
);
2972 GetClientRect(hwnd
, &clientRect
);
2975 * When the user clicks outside the combobox and the focus
2976 * is lost, the owning combobox will send a fake buttonup with
2977 * 0xFFFFFFF as the mouse location, we must also revert the
2978 * selection to the original selection.
2980 if ( (lParam
== 0xFFFFFFFF) ||
2981 (!PtInRect( &clientRect
, mousePos
)) )
2983 LISTBOX_MoveCaret( wnd
,
2989 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2990 case WM_LBUTTONDBLCLK
:
2991 case WM_LBUTTONDOWN
:
2992 return LISTBOX_HandleLButtonDownCombo(wnd
, descr
, msg
, wParam
,
2993 (INT16
)LOWORD(lParam
),
2994 (INT16
)HIWORD(lParam
) );
2995 case WM_MOUSEACTIVATE
:
2996 return MA_NOACTIVATE
;
3000 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3002 /* for some reason(?) Windows makes it possible to
3003 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3005 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3006 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3007 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3009 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3013 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
3015 case LB_SETCURSEL16
:
3017 lRet
= ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
3018 lRet
=(lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
3021 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3026 return ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
3029 lRet
= DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3031 TRACE_(combo
)("\t default on msg [%04x]\n", (UINT16
)msg
);
3036 /***********************************************************************
3039 * NOTE: in Windows, winproc address of the ComboLBox is the same
3040 * as that of the Listbox.
3042 * This is just a wrapper for the real wndproc, it only does window locking
3045 LRESULT WINAPI
ComboLBWndProc( HWND hwnd
, UINT msg
,
3046 WPARAM wParam
, LPARAM lParam
)
3048 WND
*wnd
= WIN_FindWndPtr( hwnd
);
3049 LRESULT res
= ComboLBWndProc_locked(wnd
,msg
,wParam
,lParam
);
3051 WIN_ReleaseWndPtr(wnd
);