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
))
353 if(descr
->item_height
!= 0)
354 remaining
= descr
->height
% descr
->item_height
;
357 if ((descr
->height
> descr
->item_height
) && remaining
)
359 if (!(wnd
->flags
& WIN_ISWIN32
))
360 { /* give a margin for error to 16 bits programs - if we need
361 less than the height of the nonclient area, round to the
362 *next* number of items */
363 int ncheight
= wnd
->rectWindow
.bottom
- wnd
->rectWindow
.top
- descr
->height
;
364 if ((descr
->item_height
- remaining
) <= ncheight
)
365 remaining
= remaining
- descr
->item_height
;
367 TRACE("[%04x]: changing height %d -> %d\n",
368 wnd
->hwndSelf
, descr
->height
,
369 descr
->height
- remaining
);
370 SetWindowPos( wnd
->hwndSelf
, 0, 0, 0,
371 wnd
->rectWindow
.right
- wnd
->rectWindow
.left
,
372 wnd
->rectWindow
.bottom
- wnd
->rectWindow
.top
- remaining
,
373 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
377 TRACE("[%04x]: new size = %d,%d\n",
378 wnd
->hwndSelf
, descr
->width
, descr
->height
);
379 LISTBOX_UpdatePage( wnd
, descr
);
380 LISTBOX_UpdateScroll( wnd
, descr
);
384 /***********************************************************************
385 * LISTBOX_GetItemRect
387 * Get the rectangle enclosing an item, in listbox client coordinates.
388 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
390 static LRESULT
LISTBOX_GetItemRect( WND
*wnd
, LB_DESCR
*descr
, INT index
,
393 /* Index <= 0 is legal even on empty listboxes */
394 if (index
&& (index
>= descr
->nb_items
)) return -1;
395 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
396 if (descr
->style
& LBS_MULTICOLUMN
)
398 INT col
= (index
/ descr
->page_size
) -
399 (descr
->top_item
/ descr
->page_size
);
400 rect
->left
+= col
* descr
->column_width
;
401 rect
->right
= rect
->left
+ descr
->column_width
;
402 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
403 rect
->bottom
= rect
->top
+ descr
->item_height
;
405 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
408 rect
->right
+= descr
->horz_pos
;
409 if ((index
>= 0) && (index
< descr
->nb_items
))
411 if (index
< descr
->top_item
)
413 for (i
= descr
->top_item
-1; i
>= index
; i
--)
414 rect
->top
-= descr
->items
[i
].height
;
418 for (i
= descr
->top_item
; i
< index
; i
++)
419 rect
->top
+= descr
->items
[i
].height
;
421 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
427 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
428 rect
->bottom
= rect
->top
+ descr
->item_height
;
429 rect
->right
+= descr
->horz_pos
;
432 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
433 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
437 /***********************************************************************
438 * LISTBOX_GetItemFromPoint
440 * Return the item nearest from point (x,y) (in client coordinates).
442 static INT
LISTBOX_GetItemFromPoint( WND
*wnd
, LB_DESCR
*descr
,
445 INT index
= descr
->top_item
;
447 if (!descr
->nb_items
) return -1; /* No items */
448 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
453 while (index
< descr
->nb_items
)
455 if ((pos
+= descr
->items
[index
].height
) > y
) break;
464 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
468 else if (descr
->style
& LBS_MULTICOLUMN
)
470 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
471 if (y
>= 0) index
+= y
/ descr
->item_height
;
472 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
473 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
477 index
+= (y
/ descr
->item_height
);
479 if (index
< 0) return 0;
480 if (index
>= descr
->nb_items
) return -1;
485 /***********************************************************************
490 static void LISTBOX_PaintItem( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
,
491 const RECT
*rect
, INT index
, UINT action
)
493 LB_ITEMDATA
*item
= NULL
;
494 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
496 if (IS_OWNERDRAW(descr
))
504 if (action
== ODA_FOCUS
)
505 DrawFocusRect( hdc
, rect
);
507 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
511 /* some programs mess with the clipping region when
512 drawing the item, *and* restore the previous region
513 after they are done, so a region has better to exist
514 else everything ends clipped */
515 GetClientRect(wnd
->hwndSelf
, &r
);
516 hrgn
= CreateRectRgnIndirect(&r
);
517 SelectClipRgn( hdc
, hrgn
);
518 DeleteObject( hrgn
);
520 dis
.CtlType
= ODT_LISTBOX
;
521 dis
.CtlID
= wnd
->wIDmenu
;
522 dis
.hwndItem
= wnd
->hwndSelf
;
523 dis
.itemAction
= action
;
527 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
528 if ((descr
->focus_item
== index
) &&
530 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
531 if (wnd
->dwStyle
& WS_DISABLED
) dis
.itemState
|= ODS_DISABLED
;
532 dis
.itemData
= item
? item
->data
: 0;
534 TRACE("[%04x]: drawitem %d (%s) action=%02x "
535 "state=%02x rect=%d,%d-%d,%d\n",
536 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
537 dis
.itemState
, rect
->left
, rect
->top
,
538 rect
->right
, rect
->bottom
);
539 SendMessageA(descr
->owner
, WM_DRAWITEM
, wnd
->wIDmenu
, (LPARAM
)&dis
);
543 COLORREF oldText
= 0, oldBk
= 0;
545 if (action
== ODA_FOCUS
)
547 DrawFocusRect( hdc
, rect
);
550 if (item
&& item
->selected
)
552 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
553 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
556 TRACE("[%04x]: painting %d (%s) action=%02x "
557 "rect=%d,%d-%d,%d\n",
558 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
559 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
561 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
,
562 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
563 else if (!(descr
->style
& LBS_USETABSTOPS
))
564 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
,
565 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
566 strlen(item
->str
), NULL
);
569 /* Output empty string to paint background in the full width. */
570 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
,
571 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
572 TabbedTextOutA( hdc
, rect
->left
+ 1 , rect
->top
,
573 item
->str
, strlen(item
->str
),
574 descr
->nb_tabs
, descr
->tabs
, 0);
576 if (item
&& item
->selected
)
578 SetBkColor( hdc
, oldBk
);
579 SetTextColor( hdc
, oldText
);
581 if ((descr
->focus_item
== index
) &&
583 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
588 /***********************************************************************
591 * Change the redraw flag.
593 static void LISTBOX_SetRedraw( WND
*wnd
, LB_DESCR
*descr
, BOOL on
)
597 if (!(descr
->style
& LBS_NOREDRAW
)) return;
598 descr
->style
&= ~LBS_NOREDRAW
;
599 if (descr
->style
& LBS_DISPLAYCHANGED
)
600 { /* page was changed while setredraw false, refresh automatically */
601 InvalidateRect(wnd
->hwndSelf
, NULL
, TRUE
);
602 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
603 { /* reset top of page if less than number of items/page */
604 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
605 if (descr
->top_item
< 0) descr
->top_item
= 0;
607 descr
->style
&= ~LBS_DISPLAYCHANGED
;
609 LISTBOX_UpdateScroll( wnd
, descr
);
611 else descr
->style
|= LBS_NOREDRAW
;
615 /***********************************************************************
616 * LISTBOX_RepaintItem
618 * Repaint a single item synchronously.
620 static void LISTBOX_RepaintItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
626 HBRUSH hbrush
, oldBrush
= 0;
628 /* Do not repaint the item if the item is not visible */
629 if (!IsWindowVisible(wnd
->hwndSelf
)) return;
630 if (descr
->style
& LBS_NOREDRAW
)
632 descr
->style
|= LBS_DISPLAYCHANGED
;
635 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) != 1) return;
636 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
))) return;
637 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
638 hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
639 hdc
, (LPARAM
)wnd
->hwndSelf
);
640 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
641 if (wnd
->dwStyle
& WS_DISABLED
)
642 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
643 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
644 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, index
, action
);
645 if (oldFont
) SelectObject( hdc
, oldFont
);
646 if (oldBrush
) SelectObject( hdc
, oldBrush
);
647 ReleaseDC( wnd
->hwndSelf
, hdc
);
651 /***********************************************************************
652 * LISTBOX_InitStorage
654 static LRESULT
LISTBOX_InitStorage( WND
*wnd
, LB_DESCR
*descr
, INT nb_items
,
659 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
660 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
662 nb_items
+= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
663 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
664 nb_items
* sizeof(LB_ITEMDATA
) )))
666 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
674 /***********************************************************************
675 * LISTBOX_SetTabStops
677 static BOOL
LISTBOX_SetTabStops( WND
*wnd
, LB_DESCR
*descr
, INT count
,
678 LPINT tabs
, BOOL short_ints
)
680 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
681 if (descr
->tabs
) HeapFree( descr
->heap
, 0, descr
->tabs
);
682 if (!(descr
->nb_tabs
= count
))
687 /* FIXME: count = 1 */
688 if (!(descr
->tabs
= (INT
*)HeapAlloc( descr
->heap
, 0,
689 descr
->nb_tabs
* sizeof(INT
) )))
694 LPINT16 p
= (LPINT16
)tabs
;
696 TRACE("[%04x]: settabstops ", wnd
->hwndSelf
);
697 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
698 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
699 if (TRACE_ON(listbox
)) DPRINTF("%hd ", descr
->tabs
[i
]);
701 if (TRACE_ON(listbox
)) DPRINTF("\n");
703 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
704 /* FIXME: repaint the window? */
709 /***********************************************************************
712 static LRESULT
LISTBOX_GetText( WND
*wnd
, LB_DESCR
*descr
, INT index
,
715 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
716 if (HAS_STRINGS(descr
))
719 return strlen(descr
->items
[index
].str
);
720 strcpy( buffer
, descr
->items
[index
].str
);
721 return strlen(buffer
);
724 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
725 return sizeof(DWORD
);
730 /***********************************************************************
731 * LISTBOX_FindStringPos
733 * Find the nearest string located before a given string in sort order.
734 * If 'exact' is TRUE, return an error if we don't get an exact match.
736 static INT
LISTBOX_FindStringPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
,
739 INT index
, min
, max
, res
= -1;
741 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
743 max
= descr
->nb_items
;
746 index
= (min
+ max
) / 2;
747 if (HAS_STRINGS(descr
))
748 res
= lstrcmpiA( descr
->items
[index
].str
, str
);
751 COMPAREITEMSTRUCT cis
;
753 cis
.CtlType
= ODT_LISTBOX
;
754 cis
.CtlID
= wnd
->wIDmenu
;
755 cis
.hwndItem
= wnd
->hwndSelf
;
757 cis
.itemData1
= descr
->items
[index
].data
;
759 cis
.itemData2
= (DWORD
)str
;
760 cis
.dwLocaleId
= descr
->locale
;
761 res
= SendMessageA( descr
->owner
, WM_COMPAREITEM
,
762 wnd
->wIDmenu
, (LPARAM
)&cis
);
764 if (!res
) return index
;
765 if (res
> 0) max
= index
;
766 else min
= index
+ 1;
768 return exact
? -1 : max
;
772 /***********************************************************************
773 * LISTBOX_FindFileStrPos
775 * Find the nearest string located before a given string in directory
776 * sort order (i.e. first files, then directories, then drives).
778 static INT
LISTBOX_FindFileStrPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
)
780 INT min
, max
, res
= -1;
782 if (!HAS_STRINGS(descr
))
783 return LISTBOX_FindStringPos( wnd
, descr
, str
, FALSE
);
785 max
= descr
->nb_items
;
788 INT index
= (min
+ max
) / 2;
789 const char *p
= descr
->items
[index
].str
;
790 if (*p
== '[') /* drive or directory */
792 if (*str
!= '[') res
= -1;
793 else if (p
[1] == '-') /* drive */
795 if (str
[1] == '-') res
= str
[2] - p
[2];
800 if (str
[1] == '-') res
= 1;
801 else res
= lstrcmpiA( str
, p
);
806 if (*str
== '[') res
= 1;
807 else res
= lstrcmpiA( str
, p
);
809 if (!res
) return index
;
810 if (res
< 0) max
= index
;
811 else min
= index
+ 1;
817 /***********************************************************************
820 * Find the item beginning with a given string.
822 static INT
LISTBOX_FindString( WND
*wnd
, LB_DESCR
*descr
, INT start
,
823 LPCSTR str
, BOOL exact
)
828 if (start
>= descr
->nb_items
) start
= -1;
829 item
= descr
->items
+ start
+ 1;
830 if (HAS_STRINGS(descr
))
832 if (!str
|| ! str
[0] ) return LB_ERR
;
835 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
836 if (!lstrcmpiA( str
, item
->str
)) return i
;
837 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
838 if (!lstrcmpiA( str
, item
->str
)) return i
;
842 /* Special case for drives and directories: ignore prefix */
843 #define CHECK_DRIVE(item) \
844 if ((item)->str[0] == '[') \
846 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
847 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
851 INT len
= strlen(str
);
852 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
854 if (!lstrncmpiA( str
, item
->str
, len
)) return i
;
857 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
859 if (!lstrncmpiA( str
, item
->str
, len
)) return i
;
867 if (exact
&& (descr
->style
& LBS_SORT
))
868 /* If sorted, use a WM_COMPAREITEM binary search */
869 return LISTBOX_FindStringPos( wnd
, descr
, str
, TRUE
);
871 /* Otherwise use a linear search */
872 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
873 if (item
->data
== (DWORD
)str
) return i
;
874 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
875 if (item
->data
== (DWORD
)str
) return i
;
881 /***********************************************************************
882 * LISTBOX_GetSelCount
884 static LRESULT
LISTBOX_GetSelCount( WND
*wnd
, LB_DESCR
*descr
)
887 LB_ITEMDATA
*item
= descr
->items
;
889 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
890 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
891 if (item
->selected
) count
++;
896 /***********************************************************************
897 * LISTBOX_GetSelItems16
899 static LRESULT
LISTBOX_GetSelItems16( WND
*wnd
, LB_DESCR
*descr
, INT16 max
,
903 LB_ITEMDATA
*item
= descr
->items
;
905 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
906 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
907 if (item
->selected
) array
[count
++] = (INT16
)i
;
912 /***********************************************************************
913 * LISTBOX_GetSelItems32
915 static LRESULT
LISTBOX_GetSelItems( WND
*wnd
, LB_DESCR
*descr
, INT max
,
919 LB_ITEMDATA
*item
= descr
->items
;
921 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
922 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
923 if (item
->selected
) array
[count
++] = i
;
928 /***********************************************************************
931 static LRESULT
LISTBOX_Paint( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
)
933 INT i
, col_pos
= descr
->page_size
- 1;
935 RECT focusRect
= {-1, -1, -1, -1};
937 HBRUSH hbrush
, oldBrush
= 0;
940 if (descr
->style
& LBS_NOREDRAW
) return 0;
942 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
943 if (descr
->style
& LBS_MULTICOLUMN
)
944 rect
.right
= rect
.left
+ descr
->column_width
;
945 else if (descr
->horz_pos
)
947 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
948 rect
.right
+= descr
->horz_pos
;
951 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
952 hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
953 hdc
, (LPARAM
)wnd
->hwndSelf
);
954 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
955 if (wnd
->dwStyle
& WS_DISABLED
)
956 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
958 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
961 /* Special case for empty listbox: paint focus rect */
962 rect
.bottom
= rect
.top
+ descr
->item_height
;
963 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, descr
->focus_item
,
965 rect
.top
= rect
.bottom
;
968 /* Paint all the item, regarding the selection
969 Focus state will be painted after */
970 focusItem
= descr
->focus_item
;
971 descr
->focus_item
= -1;
973 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
975 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
976 rect
.bottom
= rect
.top
+ descr
->item_height
;
978 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
982 /* keep the focus rect, to paint the focus item after */
983 focusRect
.left
= rect
.left
;
984 focusRect
.right
= rect
.right
;
985 focusRect
.top
= rect
.top
;
986 focusRect
.bottom
= rect
.bottom
;
988 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
);
989 rect
.top
= rect
.bottom
;
991 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
993 if (!IS_OWNERDRAW(descr
))
995 /* Clear the bottom of the column */
996 if (rect
.top
< descr
->height
)
998 rect
.bottom
= descr
->height
;
999 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1000 &rect
, NULL
, 0, NULL
);
1004 /* Go to the next column */
1005 rect
.left
+= descr
->column_width
;
1006 rect
.right
+= descr
->column_width
;
1008 col_pos
= descr
->page_size
- 1;
1013 if (rect
.top
>= descr
->height
) break;
1017 /* Paint the focus item now */
1018 descr
->focus_item
= focusItem
;
1019 if (focusRect
.top
!= focusRect
.bottom
&& descr
->caret_on
)
1020 LISTBOX_PaintItem( wnd
, descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
);
1022 if (!IS_OWNERDRAW(descr
))
1024 /* Clear the remainder of the client area */
1025 if (rect
.top
< descr
->height
)
1027 rect
.bottom
= descr
->height
;
1028 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1029 &rect
, NULL
, 0, NULL
);
1031 if (rect
.right
< descr
->width
)
1033 rect
.left
= rect
.right
;
1034 rect
.right
= descr
->width
;
1036 rect
.bottom
= descr
->height
;
1037 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1038 &rect
, NULL
, 0, NULL
);
1041 if (oldFont
) SelectObject( hdc
, oldFont
);
1042 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1047 /***********************************************************************
1048 * LISTBOX_InvalidateItems
1050 * Invalidate all items from a given item. If the specified item is not
1051 * visible, nothing happens.
1053 static void LISTBOX_InvalidateItems( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1057 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) == 1)
1059 if (descr
->style
& LBS_NOREDRAW
)
1061 descr
->style
|= LBS_DISPLAYCHANGED
;
1064 rect
.bottom
= descr
->height
;
1065 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
1066 if (descr
->style
& LBS_MULTICOLUMN
)
1068 /* Repaint the other columns */
1069 rect
.left
= rect
.right
;
1070 rect
.right
= descr
->width
;
1072 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
1078 /***********************************************************************
1079 * LISTBOX_GetItemHeight
1081 static LRESULT
LISTBOX_GetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1083 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1085 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1086 return descr
->items
[index
].height
;
1088 else return descr
->item_height
;
1092 /***********************************************************************
1093 * LISTBOX_SetItemHeight
1095 static LRESULT
LISTBOX_SetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1098 if (!height
) height
= 1;
1100 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1102 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1103 TRACE("[%04x]: item %d height = %d\n",
1104 wnd
->hwndSelf
, index
, height
);
1105 descr
->items
[index
].height
= height
;
1106 LISTBOX_UpdateScroll( wnd
, descr
);
1107 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1109 else if (height
!= descr
->item_height
)
1111 TRACE("[%04x]: new height = %d\n",
1112 wnd
->hwndSelf
, height
);
1113 descr
->item_height
= height
;
1114 LISTBOX_UpdatePage( wnd
, descr
);
1115 LISTBOX_UpdateScroll( wnd
, descr
);
1116 InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
1122 /***********************************************************************
1123 * LISTBOX_SetHorizontalPos
1125 static void LISTBOX_SetHorizontalPos( WND
*wnd
, LB_DESCR
*descr
, INT pos
)
1129 if (pos
> descr
->horz_extent
- descr
->width
)
1130 pos
= descr
->horz_extent
- descr
->width
;
1131 if (pos
< 0) pos
= 0;
1132 if (!(diff
= descr
->horz_pos
- pos
)) return;
1133 TRACE("[%04x]: new horz pos = %d\n",
1134 wnd
->hwndSelf
, pos
);
1135 descr
->horz_pos
= pos
;
1136 LISTBOX_UpdateScroll( wnd
, descr
);
1137 if (abs(diff
) < descr
->width
)
1138 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
1139 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1141 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1145 /***********************************************************************
1146 * LISTBOX_SetHorizontalExtent
1148 static LRESULT
LISTBOX_SetHorizontalExtent( WND
*wnd
, LB_DESCR
*descr
,
1151 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1153 if (extent
<= 0) extent
= 1;
1154 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1155 TRACE("[%04x]: new horz extent = %d\n",
1156 wnd
->hwndSelf
, extent
);
1157 descr
->horz_extent
= extent
;
1158 if (descr
->horz_pos
> extent
- descr
->width
)
1159 LISTBOX_SetHorizontalPos( wnd
, descr
, extent
- descr
->width
);
1161 LISTBOX_UpdateScroll( wnd
, descr
);
1166 /***********************************************************************
1167 * LISTBOX_SetColumnWidth
1169 static LRESULT
LISTBOX_SetColumnWidth( WND
*wnd
, LB_DESCR
*descr
, UINT width
)
1171 if (width
== descr
->column_width
) return LB_OKAY
;
1172 TRACE("[%04x]: new column width = %d\n",
1173 wnd
->hwndSelf
, width
);
1174 descr
->column_width
= width
;
1175 LISTBOX_UpdatePage( wnd
, descr
);
1180 /***********************************************************************
1183 * Returns the item height.
1185 static INT
LISTBOX_SetFont( WND
*wnd
, LB_DESCR
*descr
, HFONT font
)
1193 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
)))
1195 ERR("unable to get DC.\n" );
1198 if (font
) oldFont
= SelectObject( hdc
, font
);
1199 GetTextMetricsA( hdc
, &tm
);
1200 if (oldFont
) SelectObject( hdc
, oldFont
);
1201 ReleaseDC( wnd
->hwndSelf
, hdc
);
1202 if (!IS_OWNERDRAW(descr
))
1203 LISTBOX_SetItemHeight( wnd
, descr
, 0, tm
.tmHeight
);
1204 return tm
.tmHeight
;
1208 /***********************************************************************
1209 * LISTBOX_MakeItemVisible
1211 * Make sure that a given item is partially or fully visible.
1213 static void LISTBOX_MakeItemVisible( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1218 if (index
<= descr
->top_item
) top
= index
;
1219 else if (descr
->style
& LBS_MULTICOLUMN
)
1221 INT cols
= descr
->width
;
1222 if (!fully
) cols
+= descr
->column_width
- 1;
1223 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1225 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1226 top
= index
- descr
->page_size
* (cols
- 1);
1228 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1230 INT height
= fully
? descr
->items
[index
].height
: 1;
1231 for (top
= index
; top
> descr
->top_item
; top
--)
1232 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1236 if (index
< descr
->top_item
+ descr
->page_size
) return;
1237 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1238 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1239 top
= index
- descr
->page_size
+ 1;
1241 LISTBOX_SetTopItem( wnd
, descr
, top
, TRUE
);
1244 /***********************************************************************
1245 * LISTBOX_SetCaretIndex
1248 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1251 static LRESULT
LISTBOX_SetCaretIndex( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1252 BOOL fully_visible
)
1254 INT oldfocus
= descr
->focus_item
;
1256 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1257 if (index
== oldfocus
) return LB_OKAY
;
1258 descr
->focus_item
= index
;
1259 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1260 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1262 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1263 if (descr
->caret_on
&& (descr
->in_focus
))
1264 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1270 /***********************************************************************
1271 * LISTBOX_SelectItemRange
1273 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1275 static LRESULT
LISTBOX_SelectItemRange( WND
*wnd
, LB_DESCR
*descr
, INT first
,
1280 /* A few sanity checks */
1282 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1283 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1284 if (last
== -1) last
= descr
->nb_items
- 1;
1285 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1286 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1287 /* selected_item reflects last selected/unselected item on multiple sel */
1288 descr
->selected_item
= last
;
1290 if (on
) /* Turn selection on */
1292 for (i
= first
; i
<= last
; i
++)
1294 if (descr
->items
[i
].selected
) continue;
1295 descr
->items
[i
].selected
= TRUE
;
1296 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1298 LISTBOX_SetCaretIndex( wnd
, descr
, last
, TRUE
);
1300 else /* Turn selection off */
1302 for (i
= first
; i
<= last
; i
++)
1304 if (!descr
->items
[i
].selected
) continue;
1305 descr
->items
[i
].selected
= FALSE
;
1306 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1312 /***********************************************************************
1313 * LISTBOX_SetSelection
1315 static LRESULT
LISTBOX_SetSelection( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1316 BOOL on
, BOOL send_notify
)
1318 TRACE( "index=%d notify=%s\n", index
, send_notify
? "YES" : "NO" );
1320 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1321 if (descr
->style
& LBS_MULTIPLESEL
)
1323 if (index
== -1) /* Select all items */
1324 return LISTBOX_SelectItemRange( wnd
, descr
, 0, -1, on
);
1325 else /* Only one item */
1326 return LISTBOX_SelectItemRange( wnd
, descr
, index
, index
, on
);
1330 INT oldsel
= descr
->selected_item
;
1331 if (index
== oldsel
) return LB_OKAY
;
1332 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1333 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1334 descr
->selected_item
= index
;
1335 if (oldsel
!= -1) LISTBOX_RepaintItem( wnd
, descr
, oldsel
, ODA_SELECT
);
1336 if (index
!= -1) LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_SELECT
);
1337 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( wnd
, descr
,
1338 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1340 if( descr
->lphc
) /* set selection change flag for parent combo */
1341 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1347 /***********************************************************************
1350 * Change the caret position and extend the selection to the new caret.
1352 static void LISTBOX_MoveCaret( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1353 BOOL fully_visible
)
1355 INT oldfocus
= descr
->focus_item
;
1357 if ((index
< 0) || (index
>= descr
->nb_items
))
1360 /* Important, repaint needs to be done in this order if
1361 you want to mimic Windows behavior:
1362 1. Remove the focus and paint the item
1363 2. Remove the selection and paint the item(s)
1364 3. Set the selection and repaint the item(s)
1365 4. Set the focus to 'index' and repaint the item */
1367 /* 1. remove the focus and repaint the item */
1368 descr
->focus_item
= -1;
1369 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1370 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1372 /* 2. then turn off the previous selection */
1373 /* 3. repaint the new selected item */
1374 if (descr
->style
& LBS_EXTENDEDSEL
)
1376 if (descr
->anchor_item
!= -1)
1378 INT first
= min( index
, descr
->anchor_item
);
1379 INT last
= max( index
, descr
->anchor_item
);
1381 LISTBOX_SelectItemRange( wnd
, descr
, 0, first
- 1, FALSE
);
1382 LISTBOX_SelectItemRange( wnd
, descr
, last
+ 1, -1, FALSE
);
1383 LISTBOX_SelectItemRange( wnd
, descr
, first
, last
, TRUE
);
1386 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1388 /* Set selection to new caret item */
1389 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
1392 /* 4. repaint the new item with the focus */
1393 descr
->focus_item
= index
;
1394 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1395 if (descr
->caret_on
&& (descr
->in_focus
))
1396 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1400 /***********************************************************************
1401 * LISTBOX_InsertItem
1403 static LRESULT
LISTBOX_InsertItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1404 LPSTR str
, DWORD data
)
1408 INT oldfocus
= descr
->focus_item
;
1410 if (index
== -1) index
= descr
->nb_items
;
1411 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1412 if (!descr
->items
) max_items
= 0;
1413 else max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
1414 if (descr
->nb_items
== max_items
)
1416 /* We need to grow the array */
1417 max_items
+= LB_ARRAY_GRANULARITY
;
1418 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1419 max_items
* sizeof(LB_ITEMDATA
) )))
1421 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1424 descr
->items
= item
;
1427 /* Insert the item structure */
1429 item
= &descr
->items
[index
];
1430 if (index
< descr
->nb_items
)
1431 RtlMoveMemory( item
+ 1, item
,
1432 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1436 item
->selected
= FALSE
;
1439 /* Get item height */
1441 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1443 MEASUREITEMSTRUCT mis
;
1445 mis
.CtlType
= ODT_LISTBOX
;
1446 mis
.CtlID
= wnd
->wIDmenu
;
1448 mis
.itemData
= descr
->items
[index
].data
;
1449 mis
.itemHeight
= descr
->item_height
;
1450 SendMessageA( descr
->owner
, WM_MEASUREITEM
, wnd
->wIDmenu
, (LPARAM
)&mis
);
1451 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1452 TRACE("[%04x]: measure item %d (%s) = %d\n",
1453 wnd
->hwndSelf
, index
, str
? str
: "", item
->height
);
1456 /* Repaint the items */
1458 LISTBOX_UpdateScroll( wnd
, descr
);
1459 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1461 /* Move selection and focused item */
1462 /* If listbox was empty, set focus to the first item */
1463 if (descr
->nb_items
== 1)
1464 LISTBOX_SetCaretIndex( wnd
, descr
, 0, FALSE
);
1465 /* single select don't change selection index in win31 */
1466 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1468 descr
->selected_item
++;
1469 LISTBOX_SetSelection( wnd
, descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1473 if (index
<= descr
->selected_item
)
1475 descr
->selected_item
++;
1476 descr
->focus_item
= oldfocus
; /* focus not changed */
1483 /***********************************************************************
1484 * LISTBOX_InsertString
1486 static LRESULT
LISTBOX_InsertString( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1489 LPSTR new_str
= NULL
;
1493 if (HAS_STRINGS(descr
))
1496 if (!(new_str
= HEAP_strdupA( descr
->heap
, 0, str
)))
1498 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1502 else data
= (DWORD
)str
;
1504 if (index
== -1) index
= descr
->nb_items
;
1505 if ((ret
= LISTBOX_InsertItem( wnd
, descr
, index
, new_str
, data
)) != 0)
1507 if (new_str
) HeapFree( descr
->heap
, 0, new_str
);
1511 TRACE("[%04x]: added item %d '%s'\n",
1512 wnd
->hwndSelf
, index
, HAS_STRINGS(descr
) ? new_str
: "" );
1517 /***********************************************************************
1518 * LISTBOX_DeleteItem
1520 * Delete the content of an item. 'index' must be a valid index.
1522 static void LISTBOX_DeleteItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1524 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1525 * while Win95 sends it for all items with user data.
1526 * It's probably better to send it too often than not
1527 * often enough, so this is what we do here.
1529 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1531 DELETEITEMSTRUCT dis
;
1533 dis
.CtlType
= ODT_LISTBOX
;
1534 dis
.CtlID
= wnd
->wIDmenu
;
1536 dis
.hwndItem
= wnd
->hwndSelf
;
1537 dis
.itemData
= descr
->items
[index
].data
;
1538 SendMessageA( descr
->owner
, WM_DELETEITEM
, wnd
->wIDmenu
, (LPARAM
)&dis
);
1540 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1541 HeapFree( descr
->heap
, 0, descr
->items
[index
].str
);
1545 /***********************************************************************
1546 * LISTBOX_RemoveItem
1548 * Remove an item from the listbox and delete its content.
1550 static LRESULT
LISTBOX_RemoveItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1555 if (index
== -1) index
= descr
->nb_items
- 1;
1556 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1558 /* We need to invalidate the original rect instead of the updated one. */
1559 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1561 LISTBOX_DeleteItem( wnd
, descr
, index
);
1563 /* Remove the item */
1565 item
= &descr
->items
[index
];
1566 if (index
< descr
->nb_items
-1)
1567 RtlMoveMemory( item
, item
+ 1,
1568 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1570 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1572 /* Shrink the item array if possible */
1574 max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1575 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1577 max_items
-= LB_ARRAY_GRANULARITY
;
1578 item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1579 max_items
* sizeof(LB_ITEMDATA
) );
1580 if (item
) descr
->items
= item
;
1582 /* Repaint the items */
1584 LISTBOX_UpdateScroll( wnd
, descr
);
1585 /* if we removed the scrollbar, reset the top of the list
1586 (correct for owner-drawn ???) */
1587 if (descr
->nb_items
== descr
->page_size
)
1588 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1590 /* Move selection and focused item */
1591 if (!IS_MULTISELECT(descr
))
1593 if (index
== descr
->selected_item
)
1594 descr
->selected_item
= -1;
1595 else if (index
< descr
->selected_item
)
1597 descr
->selected_item
--;
1598 if (ISWIN31
) /* win 31 do not change the selected item number */
1599 LISTBOX_SetSelection( wnd
, descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1603 if (descr
->focus_item
>= descr
->nb_items
)
1605 descr
->focus_item
= descr
->nb_items
- 1;
1606 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1612 /***********************************************************************
1613 * LISTBOX_ResetContent
1615 static void LISTBOX_ResetContent( WND
*wnd
, LB_DESCR
*descr
)
1619 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( wnd
, descr
, i
);
1620 if (descr
->items
) HeapFree( descr
->heap
, 0, descr
->items
);
1621 descr
->nb_items
= 0;
1622 descr
->top_item
= 0;
1623 descr
->selected_item
= -1;
1624 descr
->focus_item
= 0;
1625 descr
->anchor_item
= -1;
1626 descr
->items
= NULL
;
1627 LISTBOX_UpdateScroll( wnd
, descr
);
1628 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1632 /***********************************************************************
1635 static LRESULT
LISTBOX_SetCount( WND
*wnd
, LB_DESCR
*descr
, INT count
)
1639 if (HAS_STRINGS(descr
)) return LB_ERR
;
1640 /* FIXME: this is far from optimal... */
1641 if (count
> descr
->nb_items
)
1643 while (count
> descr
->nb_items
)
1644 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, 0 )) < 0)
1647 else if (count
< descr
->nb_items
)
1649 while (count
< descr
->nb_items
)
1650 if ((ret
= LISTBOX_RemoveItem( wnd
, descr
, -1 )) < 0)
1657 /***********************************************************************
1660 static LRESULT
LISTBOX_Directory( WND
*wnd
, LB_DESCR
*descr
, UINT attrib
,
1661 LPCSTR filespec
, BOOL long_names
)
1664 LRESULT ret
= LB_OKAY
;
1665 WIN32_FIND_DATAA entry
;
1668 if ((handle
= FindFirstFileA(filespec
,&entry
)) == INVALID_HANDLE_VALUE
)
1670 if (GetLastError() != ERROR_NO_MORE_FILES
) return LB_ERR
;
1677 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1679 if (!(attrib
& DDL_DIRECTORY
) ||
1680 !strcmp( entry
.cAlternateFileName
, "." )) continue;
1681 if (long_names
) sprintf( buffer
, "[%s]", entry
.cFileName
);
1682 else sprintf( buffer
, "[%s]", entry
.cAlternateFileName
);
1684 else /* not a directory */
1686 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1687 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1689 if ((attrib
& DDL_EXCLUSIVE
) &&
1690 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1693 if (long_names
) strcpy( buffer
, entry
.cFileName
);
1694 else strcpy( buffer
, entry
.cAlternateFileName
);
1696 if (!long_names
) CharLowerA( buffer
);
1697 pos
= LISTBOX_FindFileStrPos( wnd
, descr
, buffer
);
1698 if ((ret
= LISTBOX_InsertString( wnd
, descr
, pos
, buffer
)) < 0)
1700 } while (FindNextFileA( handle
, &entry
));
1701 FindClose( handle
);
1704 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1706 char buffer
[] = "[-a-]";
1708 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++, buffer
[2]++)
1710 if (!DRIVE_IsValid(drive
)) continue;
1711 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, buffer
)) < 0)
1719 /***********************************************************************
1720 * LISTBOX_HandleVScroll
1722 static LRESULT
LISTBOX_HandleVScroll( WND
*wnd
, LB_DESCR
*descr
,
1723 WPARAM wParam
, LPARAM lParam
)
1727 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1728 switch(LOWORD(wParam
))
1731 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
- 1, TRUE
);
1734 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ 1, TRUE
);
1737 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-
1738 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1741 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+
1742 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1744 case SB_THUMBPOSITION
:
1745 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
), TRUE
);
1748 info
.cbSize
= sizeof(info
);
1749 info
.fMask
= SIF_TRACKPOS
;
1750 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1751 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
, TRUE
);
1754 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1757 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1764 /***********************************************************************
1765 * LISTBOX_HandleHScroll
1767 static LRESULT
LISTBOX_HandleHScroll( WND
*wnd
, LB_DESCR
*descr
,
1768 WPARAM wParam
, LPARAM lParam
)
1773 if (descr
->style
& LBS_MULTICOLUMN
)
1775 switch(LOWORD(wParam
))
1778 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-descr
->page_size
,
1782 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+descr
->page_size
,
1786 page
= descr
->width
/ descr
->column_width
;
1787 if (page
< 1) page
= 1;
1788 LISTBOX_SetTopItem( wnd
, descr
,
1789 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1792 page
= descr
->width
/ descr
->column_width
;
1793 if (page
< 1) page
= 1;
1794 LISTBOX_SetTopItem( wnd
, descr
,
1795 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1797 case SB_THUMBPOSITION
:
1798 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1802 info
.cbSize
= sizeof(info
);
1803 info
.fMask
= SIF_TRACKPOS
;
1804 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1805 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1809 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1812 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1816 else if (descr
->horz_extent
)
1818 switch(LOWORD(wParam
))
1821 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
- 1 );
1824 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
+ 1 );
1827 LISTBOX_SetHorizontalPos( wnd
, descr
,
1828 descr
->horz_pos
- descr
->width
);
1831 LISTBOX_SetHorizontalPos( wnd
, descr
,
1832 descr
->horz_pos
+ descr
->width
);
1834 case SB_THUMBPOSITION
:
1835 LISTBOX_SetHorizontalPos( wnd
, descr
, HIWORD(wParam
) );
1838 info
.cbSize
= sizeof(info
);
1839 info
.fMask
= SIF_TRACKPOS
;
1840 GetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
);
1841 LISTBOX_SetHorizontalPos( wnd
, descr
, info
.nTrackPos
);
1844 LISTBOX_SetHorizontalPos( wnd
, descr
, 0 );
1847 LISTBOX_SetHorizontalPos( wnd
, descr
,
1848 descr
->horz_extent
- descr
->width
);
1855 static LRESULT
LISTBOX_HandleMouseWheel(WND
*wnd
, LB_DESCR
*descr
,WPARAM wParam
, LPARAM lParam
)
1857 short gcWheelDelta
= 0;
1858 UINT pulScrollLines
= 3;
1860 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1862 gcWheelDelta
-= (short) HIWORD(wParam
);
1864 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
1866 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
1867 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
1868 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ cLineScroll
, TRUE
);
1873 /***********************************************************************
1874 * LISTBOX_HandleLButtonDown
1876 static LRESULT
LISTBOX_HandleLButtonDown( WND
*wnd
, LB_DESCR
*descr
,
1877 WPARAM wParam
, INT x
, INT y
)
1879 INT index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1880 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1881 wnd
->hwndSelf
, x
, y
, index
);
1882 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
1884 if (!descr
->in_focus
)
1886 if( !descr
->lphc
) SetFocus( wnd
->hwndSelf
);
1887 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
1888 : descr
->lphc
->self
->hwndSelf
);
1893 if (descr
->style
& LBS_EXTENDEDSEL
)
1895 /* we should perhaps make sure that all items are deselected
1896 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1897 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1898 LISTBOX_SetSelection( wnd, descr, -1, FALSE, FALSE);
1901 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1902 if (wParam
& MK_CONTROL
)
1904 LISTBOX_SetCaretIndex( wnd
, descr
, index
, FALSE
);
1905 LISTBOX_SetSelection( wnd
, descr
, index
,
1906 !descr
->items
[index
].selected
,
1907 (descr
->style
& LBS_NOTIFY
) != 0);
1909 else LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1913 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1914 LISTBOX_SetSelection( wnd
, descr
, index
,
1915 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1916 !descr
->items
[index
].selected
),
1917 (descr
->style
& LBS_NOTIFY
) != 0 );
1921 descr
->captured
= TRUE
;
1922 SetCapture( wnd
->hwndSelf
);
1923 if (index
!= -1 && !descr
->lphc
)
1925 if (descr
->style
& LBS_NOTIFY
)
1926 SendMessageA( descr
->owner
, WM_LBTRACKPOINT
, index
,
1927 MAKELPARAM( x
, y
) );
1928 if (wnd
->dwExStyle
& WS_EX_DRAGDETECT
)
1935 if (DragDetect( wnd
->hwndSelf
, pt
))
1936 SendMessageA( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
1943 /*************************************************************************
1944 * LISTBOX_HandleLButtonDownCombo [Internal]
1946 * Process LButtonDown message for the ComboListBox
1949 * pWnd [I] The windows internal structure
1950 * pDescr [I] The ListBox internal structure
1951 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
1952 * x [I] X Mouse Coordinate
1953 * y [I] Y Mouse Coordinate
1956 * 0 since we are processing the WM_LBUTTONDOWN Message
1959 * This function is only to be used when a ListBox is a ComboListBox
1962 static LRESULT
LISTBOX_HandleLButtonDownCombo( WND
*pWnd
, LB_DESCR
*pDescr
,
1963 UINT msg
, WPARAM wParam
, INT x
, INT y
)
1965 RECT clientRect
, screenRect
;
1971 GetClientRect(pWnd
->hwndSelf
, &clientRect
);
1973 if(PtInRect(&clientRect
, mousePos
))
1975 /* MousePos is in client, resume normal processing */
1976 if (msg
== WM_LBUTTONDOWN
)
1978 pDescr
->lphc
->droppedIndex
= pDescr
->nb_items
? pDescr
->selected_item
: -1;
1979 return LISTBOX_HandleLButtonDown( pWnd
, pDescr
, wParam
, x
, y
);
1981 else if (pDescr
->style
& LBS_NOTIFY
)
1982 SEND_NOTIFICATION( pWnd
, pDescr
, LBN_DBLCLK
);
1987 POINT screenMousePos
;
1988 HWND hWndOldCapture
;
1990 /* Check the Non-Client Area */
1991 screenMousePos
= mousePos
;
1992 hWndOldCapture
= GetCapture();
1994 GetWindowRect(pWnd
->hwndSelf
, &screenRect
);
1995 ClientToScreen(pWnd
->hwndSelf
, &screenMousePos
);
1997 if(!PtInRect(&screenRect
, screenMousePos
))
1999 LISTBOX_SetSelection( pWnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2000 COMBO_FlipListbox( pDescr
->lphc
, FALSE
, FALSE
);
2005 /* Check to see the NC is a scrollbar */
2007 /* Check Vertical scroll bar */
2008 if (pWnd
->dwStyle
& WS_VSCROLL
)
2010 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2011 if (PtInRect( &clientRect
, mousePos
))
2013 nHitTestType
= HTVSCROLL
;
2016 /* Check horizontal scroll bar */
2017 if (pWnd
->dwStyle
& WS_HSCROLL
)
2019 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2020 if (PtInRect( &clientRect
, mousePos
))
2022 nHitTestType
= HTHSCROLL
;
2025 /* Windows sends this message when a scrollbar is clicked
2028 if(nHitTestType
!= 0)
2030 SendMessageA(pWnd
->hwndSelf
, WM_NCLBUTTONDOWN
, nHitTestType
,
2031 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2033 /* Resume the Capture after scrolling is complete
2035 if(hWndOldCapture
!= 0)
2037 SetCapture(hWndOldCapture
);
2044 /***********************************************************************
2045 * LISTBOX_HandleLButtonUp
2047 static LRESULT
LISTBOX_HandleLButtonUp( WND
*wnd
, LB_DESCR
*descr
)
2049 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2050 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2051 LISTBOX_Timer
= LB_TIMER_NONE
;
2052 if (descr
->captured
)
2054 descr
->captured
= FALSE
;
2055 if (GetCapture() == wnd
->hwndSelf
) ReleaseCapture();
2056 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2057 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2063 /***********************************************************************
2064 * LISTBOX_HandleTimer
2066 * Handle scrolling upon a timer event.
2067 * Return TRUE if scrolling should continue.
2069 static LRESULT
LISTBOX_HandleTimer( WND
*wnd
, LB_DESCR
*descr
,
2070 INT index
, TIMER_DIRECTION dir
)
2075 if (descr
->top_item
) index
= descr
->top_item
- 1;
2079 if (descr
->top_item
) index
-= descr
->page_size
;
2082 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( wnd
, descr
);
2083 if (index
== descr
->focus_item
) index
++;
2084 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2086 case LB_TIMER_RIGHT
:
2087 if (index
+ descr
->page_size
< descr
->nb_items
)
2088 index
+= descr
->page_size
;
2093 if (index
== descr
->focus_item
) return FALSE
;
2094 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
2099 /***********************************************************************
2100 * LISTBOX_HandleSystemTimer
2102 * WM_SYSTIMER handler.
2104 static LRESULT
LISTBOX_HandleSystemTimer( WND
*wnd
, LB_DESCR
*descr
)
2106 if (!LISTBOX_HandleTimer( wnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
2108 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2109 LISTBOX_Timer
= LB_TIMER_NONE
;
2115 /***********************************************************************
2116 * LISTBOX_HandleMouseMove
2118 * WM_MOUSEMOVE handler.
2120 static void LISTBOX_HandleMouseMove( WND
*wnd
, LB_DESCR
*descr
,
2124 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2126 if (!descr
->captured
) return;
2128 if (descr
->style
& LBS_MULTICOLUMN
)
2131 else if (y
>= descr
->item_height
* descr
->page_size
)
2132 y
= descr
->item_height
* descr
->page_size
- 1;
2136 dir
= LB_TIMER_LEFT
;
2139 else if (x
>= descr
->width
)
2141 dir
= LB_TIMER_RIGHT
;
2142 x
= descr
->width
- 1;
2147 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2148 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2151 index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
2152 if (index
== -1) index
= descr
->focus_item
;
2153 if (!LISTBOX_HandleTimer( wnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2155 /* Start/stop the system timer */
2157 if (dir
!= LB_TIMER_NONE
)
2158 SetSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2159 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2160 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2161 LISTBOX_Timer
= dir
;
2165 /***********************************************************************
2166 * LISTBOX_HandleKeyDown
2168 static LRESULT
LISTBOX_HandleKeyDown( WND
*wnd
, LB_DESCR
*descr
, WPARAM wParam
)
2171 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2172 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2173 bForceSelection
= FALSE
; /* only for single select list */
2175 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2177 caret
= SendMessageA( descr
->owner
, WM_VKEYTOITEM
,
2178 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2180 if (caret
== -2) return 0;
2182 if (caret
== -1) switch(wParam
)
2185 if (descr
->style
& LBS_MULTICOLUMN
)
2187 bForceSelection
= FALSE
;
2188 if (descr
->focus_item
>= descr
->page_size
)
2189 caret
= descr
->focus_item
- descr
->page_size
;
2194 caret
= descr
->focus_item
- 1;
2195 if (caret
< 0) caret
= 0;
2198 if (descr
->style
& LBS_MULTICOLUMN
)
2200 bForceSelection
= FALSE
;
2201 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2202 caret
= descr
->focus_item
+ descr
->page_size
;
2207 caret
= descr
->focus_item
+ 1;
2208 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2212 if (descr
->style
& LBS_MULTICOLUMN
)
2214 INT page
= descr
->width
/ descr
->column_width
;
2215 if (page
< 1) page
= 1;
2216 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2218 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(wnd
,descr
)+1;
2219 if (caret
< 0) caret
= 0;
2222 if (descr
->style
& LBS_MULTICOLUMN
)
2224 INT page
= descr
->width
/ descr
->column_width
;
2225 if (page
< 1) page
= 1;
2226 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2228 else caret
= descr
->focus_item
+LISTBOX_GetCurrentPageSize(wnd
,descr
)-1;
2229 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2235 caret
= descr
->nb_items
- 1;
2238 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2239 else if (descr
->style
& LBS_MULTIPLESEL
)
2241 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
,
2242 !descr
->items
[descr
->focus_item
].selected
,
2243 (descr
->style
& LBS_NOTIFY
) != 0 );
2247 bForceSelection
= FALSE
;
2249 if (bForceSelection
) /* focused item is used instead of key */
2250 caret
= descr
->focus_item
;
2253 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
2254 !(GetKeyState( VK_SHIFT
) & 0x8000))
2255 descr
->anchor_item
= caret
;
2256 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2257 LISTBOX_SetSelection( wnd
, descr
, caret
, TRUE
, FALSE
);
2258 if (descr
->style
& LBS_NOTIFY
)
2260 if( descr
->lphc
&& CB_GETTYPE(descr
->lphc
) != CBS_SIMPLE
)
2262 /* make sure that combo parent doesn't hide us */
2263 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2265 if (descr
->nb_items
) SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2272 /***********************************************************************
2273 * LISTBOX_HandleChar
2275 static LRESULT
LISTBOX_HandleChar( WND
*wnd
, LB_DESCR
*descr
,
2281 str
[0] = wParam
& 0xff;
2284 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2286 caret
= SendMessageA( descr
->owner
, WM_CHARTOITEM
,
2287 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2289 if (caret
== -2) return 0;
2292 caret
= LISTBOX_FindString( wnd
, descr
, descr
->focus_item
, str
, FALSE
);
2295 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2296 LISTBOX_SetSelection( wnd
, descr
, caret
, TRUE
, FALSE
);
2297 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2298 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2299 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2305 /***********************************************************************
2308 static BOOL
LISTBOX_Create( WND
*wnd
, LPHEADCOMBO lphc
)
2311 MEASUREITEMSTRUCT mis
;
2314 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2316 if (!(descr
->heap
= HeapCreate( 0, 0x10000, 0 )))
2318 HeapFree( GetProcessHeap(), 0, descr
);
2321 GetClientRect( wnd
->hwndSelf
, &rect
);
2322 descr
->owner
= GetParent( wnd
->hwndSelf
);
2323 descr
->style
= wnd
->dwStyle
;
2324 descr
->width
= rect
.right
- rect
.left
;
2325 descr
->height
= rect
.bottom
- rect
.top
;
2326 descr
->items
= NULL
;
2327 descr
->nb_items
= 0;
2328 descr
->top_item
= 0;
2329 descr
->selected_item
= -1;
2330 descr
->focus_item
= 0;
2331 descr
->anchor_item
= -1;
2332 descr
->item_height
= 1;
2333 descr
->page_size
= 1;
2334 descr
->column_width
= 150;
2335 descr
->horz_extent
= (wnd
->dwStyle
& WS_HSCROLL
) ? 1 : 0;
2336 descr
->horz_pos
= 0;
2339 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2340 descr
->in_focus
= FALSE
;
2341 descr
->captured
= FALSE
;
2343 descr
->locale
= 0; /* FIXME */
2346 if( ( GetExpWinVer16( wnd
->hInstance
) & 0xFF00 ) == 0x0300
2347 && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2349 /* Win95 document "List Box Differences" from MSDN:
2350 If a list box in a version 3.x application has either the
2351 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2352 horizontal and vertical scroll bars.
2354 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2359 TRACE_(combo
)("[%04x]: resetting owner %04x -> %04x\n",
2360 wnd
->hwndSelf
, descr
->owner
, lphc
->self
->hwndSelf
);
2361 descr
->owner
= lphc
->self
->hwndSelf
;
2364 *(LB_DESCR
**)wnd
->wExtra
= descr
;
2366 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2368 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2369 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2370 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2371 descr
->item_height
= LISTBOX_SetFont( wnd
, descr
, 0 );
2373 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2375 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2377 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2378 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2382 mis
.CtlType
= ODT_LISTBOX
;
2383 mis
.CtlID
= wnd
->wIDmenu
;
2387 mis
.itemHeight
= descr
->item_height
;
2388 SendMessageA( descr
->owner
, WM_MEASUREITEM
, wnd
->wIDmenu
, (LPARAM
)&mis
);
2389 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2397 /***********************************************************************
2400 static BOOL
LISTBOX_Destroy( WND
*wnd
, LB_DESCR
*descr
)
2402 LISTBOX_ResetContent( wnd
, descr
);
2403 HeapDestroy( descr
->heap
);
2404 HeapFree( GetProcessHeap(), 0, descr
);
2410 /***********************************************************************
2413 static inline LRESULT WINAPI
ListBoxWndProc_locked( WND
* wnd
, UINT msg
,
2414 WPARAM wParam
, LPARAM lParam
)
2418 HWND hwnd
= wnd
->hwndSelf
;
2421 if (!(descr
= *(LB_DESCR
**)wnd
->wExtra
))
2427 if (!LISTBOX_Create( wnd
, NULL
))
2429 TRACE("creating wnd=%04x descr=%p\n",
2430 hwnd
, *(LB_DESCR
**)wnd
->wExtra
);
2436 * When a listbox is not in a combobox and the look
2437 * is win95, the WS_BORDER style is replaced with
2438 * the WS_EX_CLIENTEDGE style.
2440 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
2441 (wnd
->dwStyle
& WS_BORDER
) )
2443 wnd
->dwExStyle
|= WS_EX_CLIENTEDGE
;
2444 wnd
->dwStyle
&= ~ WS_BORDER
;
2449 /* Ignore all other messages before we get a WM_CREATE */
2450 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2453 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2454 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2457 case LB_RESETCONTENT16
:
2458 case LB_RESETCONTENT
:
2459 LISTBOX_ResetContent( wnd
, descr
);
2462 case LB_ADDSTRING16
:
2463 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2466 wParam
= LISTBOX_FindStringPos( wnd
, descr
, (LPCSTR
)lParam
, FALSE
);
2467 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2469 case LB_INSERTSTRING16
:
2470 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2471 wParam
= (INT
)(INT16
)wParam
;
2473 case LB_INSERTSTRING
:
2474 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2477 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2480 wParam
= LISTBOX_FindFileStrPos( wnd
, descr
, (LPCSTR
)lParam
);
2481 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2483 case LB_DELETESTRING16
:
2484 case LB_DELETESTRING
:
2485 if (LISTBOX_RemoveItem( wnd
, descr
, wParam
) != LB_ERR
)
2486 return descr
->nb_items
;
2490 case LB_GETITEMDATA16
:
2491 case LB_GETITEMDATA
:
2492 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2494 return descr
->items
[wParam
].data
;
2496 case LB_SETITEMDATA16
:
2497 case LB_SETITEMDATA
:
2498 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2500 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2505 return descr
->nb_items
;
2508 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2511 return LISTBOX_GetText( wnd
, descr
, wParam
, (LPSTR
)lParam
);
2513 case LB_GETTEXTLEN16
:
2516 if (wParam
>= descr
->nb_items
)
2518 return (HAS_STRINGS(descr
) ? strlen(descr
->items
[wParam
].str
)
2521 case LB_GETCURSEL16
:
2523 if (descr
->nb_items
==0)
2525 if (!IS_MULTISELECT(descr
))
2526 return descr
->selected_item
;
2528 if (descr
->selected_item
!=-1)
2529 return descr
->selected_item
;
2531 return descr
->focus_item
;
2532 /* otherwise, if the user tries to move the selection with the */
2533 /* arrow keys, we will give the application something to choke on */
2534 case LB_GETTOPINDEX16
:
2535 case LB_GETTOPINDEX
:
2536 return descr
->top_item
;
2538 case LB_GETITEMHEIGHT16
:
2539 case LB_GETITEMHEIGHT
:
2540 return LISTBOX_GetItemHeight( wnd
, descr
, wParam
);
2542 case LB_SETITEMHEIGHT16
:
2543 lParam
= LOWORD(lParam
);
2545 case LB_SETITEMHEIGHT
:
2546 return LISTBOX_SetItemHeight( wnd
, descr
, wParam
, lParam
);
2548 case LB_ITEMFROMPOINT
:
2553 pt
.x
= LOWORD(lParam
);
2554 pt
.y
= HIWORD(lParam
);
2557 rect
.right
= descr
->width
;
2558 rect
.bottom
= descr
->height
;
2560 return MAKELONG( LISTBOX_GetItemFromPoint(wnd
, descr
, pt
.x
, pt
.y
),
2561 !PtInRect( &rect
, pt
) );
2564 case LB_SETCARETINDEX16
:
2565 case LB_SETCARETINDEX
:
2566 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2567 if (LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, !lParam
) == LB_ERR
)
2574 case LB_GETCARETINDEX16
:
2575 case LB_GETCARETINDEX
:
2576 return descr
->focus_item
;
2578 case LB_SETTOPINDEX16
:
2579 case LB_SETTOPINDEX
:
2580 return LISTBOX_SetTopItem( wnd
, descr
, wParam
, TRUE
);
2582 case LB_SETCOLUMNWIDTH16
:
2583 case LB_SETCOLUMNWIDTH
:
2584 return LISTBOX_SetColumnWidth( wnd
, descr
, wParam
);
2586 case LB_GETITEMRECT16
:
2589 ret
= LISTBOX_GetItemRect( wnd
, descr
, (INT16
)wParam
, &rect
);
2590 CONV_RECT32TO16( &rect
, (RECT16
*)PTR_SEG_TO_LIN(lParam
) );
2594 case LB_GETITEMRECT
:
2595 return LISTBOX_GetItemRect( wnd
, descr
, wParam
, (RECT
*)lParam
);
2597 case LB_FINDSTRING16
:
2598 wParam
= (INT
)(INT16
)wParam
;
2599 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2602 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, FALSE
);
2604 case LB_FINDSTRINGEXACT16
:
2605 wParam
= (INT
)(INT16
)wParam
;
2606 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2608 case LB_FINDSTRINGEXACT
:
2609 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2611 case LB_SELECTSTRING16
:
2612 wParam
= (INT
)(INT16
)wParam
;
2613 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2615 case LB_SELECTSTRING
:
2617 INT index
= LISTBOX_FindString( wnd
, descr
, wParam
,
2618 (LPCSTR
)lParam
, FALSE
);
2619 if (index
== LB_ERR
)
2621 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
2626 wParam
= (INT
)(INT16
)wParam
;
2629 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2631 return descr
->items
[wParam
].selected
;
2634 lParam
= (INT
)(INT16
)lParam
;
2637 return LISTBOX_SetSelection( wnd
, descr
, lParam
, wParam
, FALSE
);
2639 case LB_SETCURSEL16
:
2640 wParam
= (INT
)(INT16
)wParam
;
2643 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2644 LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, TRUE
);
2645 return LISTBOX_SetSelection( wnd
, descr
, wParam
, TRUE
, FALSE
);
2647 case LB_GETSELCOUNT16
:
2648 case LB_GETSELCOUNT
:
2649 return LISTBOX_GetSelCount( wnd
, descr
);
2651 case LB_GETSELITEMS16
:
2652 return LISTBOX_GetSelItems16( wnd
, descr
, wParam
,
2653 (LPINT16
)PTR_SEG_TO_LIN(lParam
) );
2655 case LB_GETSELITEMS
:
2656 return LISTBOX_GetSelItems( wnd
, descr
, wParam
, (LPINT
)lParam
);
2658 case LB_SELITEMRANGE16
:
2659 case LB_SELITEMRANGE
:
2660 if (LOWORD(lParam
) <= HIWORD(lParam
))
2661 return LISTBOX_SelectItemRange( wnd
, descr
, LOWORD(lParam
),
2662 HIWORD(lParam
), wParam
);
2664 return LISTBOX_SelectItemRange( wnd
, descr
, HIWORD(lParam
),
2665 LOWORD(lParam
), wParam
);
2667 case LB_SELITEMRANGEEX16
:
2668 case LB_SELITEMRANGEEX
:
2669 if ((INT
)lParam
>= (INT
)wParam
)
2670 return LISTBOX_SelectItemRange( wnd
, descr
, wParam
, lParam
, TRUE
);
2672 return LISTBOX_SelectItemRange( wnd
, descr
, lParam
, wParam
, FALSE
);
2674 case LB_GETHORIZONTALEXTENT16
:
2675 case LB_GETHORIZONTALEXTENT
:
2676 return descr
->horz_extent
;
2678 case LB_SETHORIZONTALEXTENT16
:
2679 case LB_SETHORIZONTALEXTENT
:
2680 return LISTBOX_SetHorizontalExtent( wnd
, descr
, wParam
);
2682 case LB_GETANCHORINDEX16
:
2683 case LB_GETANCHORINDEX
:
2684 return descr
->anchor_item
;
2686 case LB_SETANCHORINDEX16
:
2687 wParam
= (INT
)(INT16
)wParam
;
2689 case LB_SETANCHORINDEX
:
2690 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2692 descr
->anchor_item
= (INT
)wParam
;
2696 return LISTBOX_Directory( wnd
, descr
, wParam
,
2697 (LPCSTR
)PTR_SEG_TO_LIN(lParam
), FALSE
);
2700 return LISTBOX_Directory( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2703 return descr
->locale
;
2706 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2709 case LB_INITSTORAGE
:
2710 return LISTBOX_InitStorage( wnd
, descr
, wParam
, (DWORD
)lParam
);
2713 return LISTBOX_SetCount( wnd
, descr
, (INT
)wParam
);
2715 case LB_SETTABSTOPS16
:
2716 return LISTBOX_SetTabStops( wnd
, descr
, (INT
)(INT16
)wParam
,
2717 (LPINT
)PTR_SEG_TO_LIN(lParam
), TRUE
);
2719 case LB_SETTABSTOPS
:
2720 return LISTBOX_SetTabStops( wnd
, descr
, wParam
, (LPINT
)lParam
, FALSE
);
2724 if (descr
->caret_on
)
2726 descr
->caret_on
= TRUE
;
2727 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2728 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2733 if (!descr
->caret_on
)
2735 descr
->caret_on
= FALSE
;
2736 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2737 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2741 return LISTBOX_Destroy( wnd
, descr
);
2744 InvalidateRect( hwnd
, NULL
, TRUE
);
2748 LISTBOX_SetRedraw( wnd
, descr
, wParam
!= 0 );
2752 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2757 HDC hdc
= ( wParam
) ? ((HDC
)wParam
)
2758 : BeginPaint( hwnd
, &ps
);
2759 ret
= LISTBOX_Paint( wnd
, descr
, hdc
);
2760 if( !wParam
) EndPaint( hwnd
, &ps
);
2764 LISTBOX_UpdateSize( wnd
, descr
);
2769 LISTBOX_SetFont( wnd
, descr
, (HFONT
)wParam
);
2770 if (lParam
) InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
2773 descr
->in_focus
= TRUE
;
2774 descr
->caret_on
= TRUE
;
2775 if (descr
->focus_item
!= -1)
2776 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2777 SEND_NOTIFICATION( wnd
, descr
, LBN_SETFOCUS
);
2780 descr
->in_focus
= FALSE
;
2781 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2782 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2783 SEND_NOTIFICATION( wnd
, descr
, LBN_KILLFOCUS
);
2786 return LISTBOX_HandleHScroll( wnd
, descr
, wParam
, lParam
);
2788 return LISTBOX_HandleVScroll( wnd
, descr
, wParam
, lParam
);
2789 case WM_MOUSEACTIVATE
:
2790 return MA_NOACTIVATE
;
2792 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2793 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2794 return LISTBOX_HandleMouseWheel( wnd
, descr
, wParam
, lParam
);
2795 case WM_LBUTTONDOWN
:
2796 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2797 (INT16
)LOWORD(lParam
),
2798 (INT16
)HIWORD(lParam
) );
2799 case WM_LBUTTONDBLCLK
:
2800 if (descr
->style
& LBS_NOTIFY
)
2801 SEND_NOTIFICATION( wnd
, descr
, LBN_DBLCLK
);
2804 if (GetCapture() == hwnd
)
2805 LISTBOX_HandleMouseMove( wnd
, descr
, (INT16
)LOWORD(lParam
),
2806 (INT16
)HIWORD(lParam
) );
2809 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2811 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2813 return LISTBOX_HandleChar( wnd
, descr
, wParam
);
2815 return LISTBOX_HandleSystemTimer( wnd
, descr
);
2817 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
2820 HBRUSH hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
2821 wParam
, (LPARAM
)wnd
->hwndSelf
);
2822 GetClientRect(hwnd
, &rect
);
2823 if (hbrush
) FillRect( (HDC
)wParam
, &rect
, hbrush
);
2828 return SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2832 case WM_QUERYDROPOBJECT
:
2837 LPDRAGINFO dragInfo
= (LPDRAGINFO
)PTR_SEG_TO_LIN( (SEGPTR
)lParam
);
2838 dragInfo
->l
= LISTBOX_GetItemFromPoint( wnd
, descr
, dragInfo
->pt
.x
,
2840 return SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2845 if ((msg
>= WM_USER
) && (msg
< 0xc000))
2846 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2847 hwnd
, msg
, wParam
, lParam
);
2848 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2853 /***********************************************************************
2856 * This is just a wrapper for the real wndproc, it only does window locking
2859 LRESULT WINAPI
ListBoxWndProc( HWND hwnd
, UINT msg
,
2860 WPARAM wParam
, LPARAM lParam
)
2862 WND
* wndPtr
= WIN_FindWndPtr( hwnd
);
2863 LRESULT res
= ListBoxWndProc_locked(wndPtr
,msg
,wParam
,lParam
);
2865 WIN_ReleaseWndPtr(wndPtr
);
2869 /***********************************************************************
2872 LRESULT
COMBO_Directory( LPHEADCOMBO lphc
, UINT attrib
, LPSTR dir
, BOOL bLong
)
2874 WND
*wnd
= WIN_FindWndPtr( lphc
->hWndLBox
);
2878 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2881 LRESULT lRet
= LISTBOX_Directory( wnd
, descr
, attrib
, dir
, bLong
);
2883 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0,
2884 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
2885 WIN_ReleaseWndPtr(wnd
);
2888 WIN_ReleaseWndPtr(wnd
);
2893 /***********************************************************************
2894 * ComboLBWndProc_locked
2896 * The real combo listbox wndproc, but called with locked WND struct.
2898 static inline LRESULT WINAPI
ComboLBWndProc_locked( WND
* wnd
, UINT msg
,
2899 WPARAM wParam
, LPARAM lParam
)
2902 HWND hwnd
= wnd
->hwndSelf
;
2906 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2908 TRACE_(combo
)("[%04x]: msg %s wp %08x lp %08lx\n",
2909 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2911 if( descr
|| msg
== WM_CREATE
)
2913 LPHEADCOMBO lphc
= (descr
) ? descr
->lphc
: NULL
;
2918 #define lpcs ((LPCREATESTRUCTA)lParam)
2919 TRACE_(combo
)("\tpassed parent handle = 0x%08x\n",
2920 (UINT
)lpcs
->lpCreateParams
);
2922 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
2924 return LISTBOX_Create( wnd
, lphc
);
2926 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
2927 (CB_GETTYPE(lphc
) != CBS_SIMPLE
) )
2933 mousePos
.x
= (INT16
)LOWORD(lParam
);
2934 mousePos
.y
= (INT16
)HIWORD(lParam
);
2937 * If we are in a dropdown combobox, we simulate that
2938 * the mouse is captured to show the tracking of the item.
2940 GetClientRect(hwnd
, &clientRect
);
2942 if (PtInRect( &clientRect
, mousePos
))
2944 captured
= descr
->captured
;
2945 descr
->captured
= TRUE
;
2947 LISTBOX_HandleMouseMove( wnd
, descr
,
2948 mousePos
.x
, mousePos
.y
);
2950 descr
->captured
= captured
;
2955 LISTBOX_HandleMouseMove( wnd
, descr
,
2956 mousePos
.x
, mousePos
.y
);
2965 * If we are in Win3.1 look, go with the default behavior.
2967 return ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2970 if (TWEAK_WineLook
> WIN31_LOOK
)
2976 * If the mouse button "up" is not in the listbox,
2977 * we make sure there is no selection by re-selecting the
2978 * item that was selected when the listbox was made visible.
2980 mousePos
.x
= (INT16
)LOWORD(lParam
);
2981 mousePos
.y
= (INT16
)HIWORD(lParam
);
2983 GetClientRect(hwnd
, &clientRect
);
2986 * When the user clicks outside the combobox and the focus
2987 * is lost, the owning combobox will send a fake buttonup with
2988 * 0xFFFFFFF as the mouse location, we must also revert the
2989 * selection to the original selection.
2991 if ( (lParam
== 0xFFFFFFFF) ||
2992 (!PtInRect( &clientRect
, mousePos
)) )
2994 LISTBOX_MoveCaret( wnd
,
3000 return LISTBOX_HandleLButtonUp( wnd
, descr
);
3001 case WM_LBUTTONDBLCLK
:
3002 case WM_LBUTTONDOWN
:
3003 return LISTBOX_HandleLButtonDownCombo(wnd
, descr
, msg
, wParam
,
3004 (INT16
)LOWORD(lParam
),
3005 (INT16
)HIWORD(lParam
) );
3006 case WM_MOUSEACTIVATE
:
3007 return MA_NOACTIVATE
;
3011 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3013 /* for some reason(?) Windows makes it possible to
3014 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3016 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3017 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3018 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3020 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3024 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
3026 case LB_SETCURSEL16
:
3028 lRet
= ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
3029 lRet
=(lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
3032 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3037 return ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
3040 lRet
= DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3042 TRACE_(combo
)("\t default on msg [%04x]\n", (UINT16
)msg
);
3047 /***********************************************************************
3050 * NOTE: in Windows, winproc address of the ComboLBox is the same
3051 * as that of the Listbox.
3053 * This is just a wrapper for the real wndproc, it only does window locking
3056 LRESULT WINAPI
ComboLBWndProc( HWND hwnd
, UINT msg
,
3057 WPARAM wParam
, LPARAM lParam
)
3059 WND
*wnd
= WIN_FindWndPtr( hwnd
);
3060 LRESULT res
= ComboLBWndProc_locked(wnd
,msg
,wParam
,lParam
);
3062 WIN_ReleaseWndPtr(wnd
);