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
, BOOL ignoreFocus
)
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 (!ignoreFocus
&& (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 (!ignoreFocus
&& (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
, FALSE
);
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 (!strncasecmp( str, (item)->str+1, len )) return i; \
847 if (((item)->str[1] == '-') && !strncasecmp(str,(item)->str+2,len)) \
851 INT len
= strlen(str
);
852 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
854 if (!strncasecmp( str
, item
->str
, len
)) return i
;
857 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
859 if (!strncasecmp( 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;
939 if (descr
->style
& LBS_NOREDRAW
) return 0;
941 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
942 if (descr
->style
& LBS_MULTICOLUMN
)
943 rect
.right
= rect
.left
+ descr
->column_width
;
944 else if (descr
->horz_pos
)
946 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
947 rect
.right
+= descr
->horz_pos
;
950 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
951 hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
952 hdc
, (LPARAM
)wnd
->hwndSelf
);
953 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
954 if (wnd
->dwStyle
& WS_DISABLED
)
955 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
957 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
960 /* Special case for empty listbox: paint focus rect */
961 rect
.bottom
= rect
.top
+ descr
->item_height
;
962 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, descr
->focus_item
,
964 rect
.top
= rect
.bottom
;
967 /* Paint all the item, regarding the selection
968 Focus state will be painted after */
970 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
972 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
973 rect
.bottom
= rect
.top
+ descr
->item_height
;
975 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
977 if (i
== descr
->focus_item
)
979 /* keep the focus rect, to paint the focus item after */
980 focusRect
.left
= rect
.left
;
981 focusRect
.right
= rect
.right
;
982 focusRect
.top
= rect
.top
;
983 focusRect
.bottom
= rect
.bottom
;
985 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
986 rect
.top
= rect
.bottom
;
988 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
990 if (!IS_OWNERDRAW(descr
))
992 /* Clear the bottom of the column */
993 if (rect
.top
< descr
->height
)
995 rect
.bottom
= descr
->height
;
996 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
997 &rect
, NULL
, 0, NULL
);
1001 /* Go to the next column */
1002 rect
.left
+= descr
->column_width
;
1003 rect
.right
+= descr
->column_width
;
1005 col_pos
= descr
->page_size
- 1;
1010 if (rect
.top
>= descr
->height
) break;
1014 /* Paint the focus item now */
1015 if (focusRect
.top
!= focusRect
.bottom
&& descr
->caret_on
)
1016 LISTBOX_PaintItem( wnd
, descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1018 if (!IS_OWNERDRAW(descr
))
1020 /* Clear the remainder of the client area */
1021 if (rect
.top
< descr
->height
)
1023 rect
.bottom
= descr
->height
;
1024 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1025 &rect
, NULL
, 0, NULL
);
1027 if (rect
.right
< descr
->width
)
1029 rect
.left
= rect
.right
;
1030 rect
.right
= descr
->width
;
1032 rect
.bottom
= descr
->height
;
1033 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1034 &rect
, NULL
, 0, NULL
);
1037 if (oldFont
) SelectObject( hdc
, oldFont
);
1038 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1043 /***********************************************************************
1044 * LISTBOX_InvalidateItems
1046 * Invalidate all items from a given item. If the specified item is not
1047 * visible, nothing happens.
1049 static void LISTBOX_InvalidateItems( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1053 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) == 1)
1055 if (descr
->style
& LBS_NOREDRAW
)
1057 descr
->style
|= LBS_DISPLAYCHANGED
;
1060 rect
.bottom
= descr
->height
;
1061 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
1062 if (descr
->style
& LBS_MULTICOLUMN
)
1064 /* Repaint the other columns */
1065 rect
.left
= rect
.right
;
1066 rect
.right
= descr
->width
;
1068 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
1074 /***********************************************************************
1075 * LISTBOX_GetItemHeight
1077 static LRESULT
LISTBOX_GetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1079 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1081 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1082 return descr
->items
[index
].height
;
1084 else return descr
->item_height
;
1088 /***********************************************************************
1089 * LISTBOX_SetItemHeight
1091 static LRESULT
LISTBOX_SetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1094 if (!height
) height
= 1;
1096 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1098 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1099 TRACE("[%04x]: item %d height = %d\n",
1100 wnd
->hwndSelf
, index
, height
);
1101 descr
->items
[index
].height
= height
;
1102 LISTBOX_UpdateScroll( wnd
, descr
);
1103 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1105 else if (height
!= descr
->item_height
)
1107 TRACE("[%04x]: new height = %d\n",
1108 wnd
->hwndSelf
, height
);
1109 descr
->item_height
= height
;
1110 LISTBOX_UpdatePage( wnd
, descr
);
1111 LISTBOX_UpdateScroll( wnd
, descr
);
1112 InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
1118 /***********************************************************************
1119 * LISTBOX_SetHorizontalPos
1121 static void LISTBOX_SetHorizontalPos( WND
*wnd
, LB_DESCR
*descr
, INT pos
)
1125 if (pos
> descr
->horz_extent
- descr
->width
)
1126 pos
= descr
->horz_extent
- descr
->width
;
1127 if (pos
< 0) pos
= 0;
1128 if (!(diff
= descr
->horz_pos
- pos
)) return;
1129 TRACE("[%04x]: new horz pos = %d\n",
1130 wnd
->hwndSelf
, pos
);
1131 descr
->horz_pos
= pos
;
1132 LISTBOX_UpdateScroll( wnd
, descr
);
1133 if (abs(diff
) < descr
->width
)
1134 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
1135 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1137 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1141 /***********************************************************************
1142 * LISTBOX_SetHorizontalExtent
1144 static LRESULT
LISTBOX_SetHorizontalExtent( WND
*wnd
, LB_DESCR
*descr
,
1147 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1149 if (extent
<= 0) extent
= 1;
1150 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1151 TRACE("[%04x]: new horz extent = %d\n",
1152 wnd
->hwndSelf
, extent
);
1153 descr
->horz_extent
= extent
;
1154 if (descr
->horz_pos
> extent
- descr
->width
)
1155 LISTBOX_SetHorizontalPos( wnd
, descr
, extent
- descr
->width
);
1157 LISTBOX_UpdateScroll( wnd
, descr
);
1162 /***********************************************************************
1163 * LISTBOX_SetColumnWidth
1165 static LRESULT
LISTBOX_SetColumnWidth( WND
*wnd
, LB_DESCR
*descr
, UINT width
)
1167 if (width
== descr
->column_width
) return LB_OKAY
;
1168 TRACE("[%04x]: new column width = %d\n",
1169 wnd
->hwndSelf
, width
);
1170 descr
->column_width
= width
;
1171 LISTBOX_UpdatePage( wnd
, descr
);
1176 /***********************************************************************
1179 * Returns the item height.
1181 static INT
LISTBOX_SetFont( WND
*wnd
, LB_DESCR
*descr
, HFONT font
)
1189 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
)))
1191 ERR("unable to get DC.\n" );
1194 if (font
) oldFont
= SelectObject( hdc
, font
);
1195 GetTextMetricsA( hdc
, &tm
);
1196 if (oldFont
) SelectObject( hdc
, oldFont
);
1197 ReleaseDC( wnd
->hwndSelf
, hdc
);
1198 if (!IS_OWNERDRAW(descr
))
1199 LISTBOX_SetItemHeight( wnd
, descr
, 0, tm
.tmHeight
);
1200 return tm
.tmHeight
;
1204 /***********************************************************************
1205 * LISTBOX_MakeItemVisible
1207 * Make sure that a given item is partially or fully visible.
1209 static void LISTBOX_MakeItemVisible( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1214 if (index
<= descr
->top_item
) top
= index
;
1215 else if (descr
->style
& LBS_MULTICOLUMN
)
1217 INT cols
= descr
->width
;
1218 if (!fully
) cols
+= descr
->column_width
- 1;
1219 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1221 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1222 top
= index
- descr
->page_size
* (cols
- 1);
1224 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1226 INT height
= fully
? descr
->items
[index
].height
: 1;
1227 for (top
= index
; top
> descr
->top_item
; top
--)
1228 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1232 if (index
< descr
->top_item
+ descr
->page_size
) return;
1233 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1234 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1235 top
= index
- descr
->page_size
+ 1;
1237 LISTBOX_SetTopItem( wnd
, descr
, top
, TRUE
);
1240 /***********************************************************************
1241 * LISTBOX_SetCaretIndex
1244 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1247 static LRESULT
LISTBOX_SetCaretIndex( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1248 BOOL fully_visible
)
1250 INT oldfocus
= descr
->focus_item
;
1252 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1253 if (index
== oldfocus
) return LB_OKAY
;
1254 descr
->focus_item
= index
;
1255 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1256 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1258 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1259 if (descr
->caret_on
&& (descr
->in_focus
))
1260 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1266 /***********************************************************************
1267 * LISTBOX_SelectItemRange
1269 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1271 static LRESULT
LISTBOX_SelectItemRange( WND
*wnd
, LB_DESCR
*descr
, INT first
,
1276 /* A few sanity checks */
1278 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1279 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1280 if (last
== -1) last
= descr
->nb_items
- 1;
1281 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1282 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1283 /* selected_item reflects last selected/unselected item on multiple sel */
1284 descr
->selected_item
= last
;
1286 if (on
) /* Turn selection on */
1288 for (i
= first
; i
<= last
; i
++)
1290 if (descr
->items
[i
].selected
) continue;
1291 descr
->items
[i
].selected
= TRUE
;
1292 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1294 LISTBOX_SetCaretIndex( wnd
, descr
, last
, TRUE
);
1296 else /* Turn selection off */
1298 for (i
= first
; i
<= last
; i
++)
1300 if (!descr
->items
[i
].selected
) continue;
1301 descr
->items
[i
].selected
= FALSE
;
1302 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1308 /***********************************************************************
1309 * LISTBOX_SetSelection
1311 static LRESULT
LISTBOX_SetSelection( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1312 BOOL on
, BOOL send_notify
)
1314 TRACE( "index=%d notify=%s\n", index
, send_notify
? "YES" : "NO" );
1316 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1317 if (descr
->style
& LBS_MULTIPLESEL
)
1319 if (index
== -1) /* Select all items */
1320 return LISTBOX_SelectItemRange( wnd
, descr
, 0, -1, on
);
1321 else /* Only one item */
1322 return LISTBOX_SelectItemRange( wnd
, descr
, index
, index
, on
);
1326 INT oldsel
= descr
->selected_item
;
1327 if (index
== oldsel
) return LB_OKAY
;
1328 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1329 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1330 descr
->selected_item
= index
;
1331 if (oldsel
!= -1) LISTBOX_RepaintItem( wnd
, descr
, oldsel
, ODA_SELECT
);
1332 if (index
!= -1) LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_SELECT
);
1333 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( wnd
, descr
,
1334 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1336 if( descr
->lphc
) /* set selection change flag for parent combo */
1337 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1343 /***********************************************************************
1346 * Change the caret position and extend the selection to the new caret.
1348 static void LISTBOX_MoveCaret( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1349 BOOL fully_visible
)
1351 INT oldfocus
= descr
->focus_item
;
1353 if ((index
< 0) || (index
>= descr
->nb_items
))
1356 /* Important, repaint needs to be done in this order if
1357 you want to mimic Windows behavior:
1358 1. Remove the focus and paint the item
1359 2. Remove the selection and paint the item(s)
1360 3. Set the selection and repaint the item(s)
1361 4. Set the focus to 'index' and repaint the item */
1363 /* 1. remove the focus and repaint the item */
1364 descr
->focus_item
= -1;
1365 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1366 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1368 /* 2. then turn off the previous selection */
1369 /* 3. repaint the new selected item */
1370 if (descr
->style
& LBS_EXTENDEDSEL
)
1372 if (descr
->anchor_item
!= -1)
1374 INT first
= min( index
, descr
->anchor_item
);
1375 INT last
= max( index
, descr
->anchor_item
);
1377 LISTBOX_SelectItemRange( wnd
, descr
, 0, first
- 1, FALSE
);
1378 LISTBOX_SelectItemRange( wnd
, descr
, last
+ 1, -1, FALSE
);
1379 LISTBOX_SelectItemRange( wnd
, descr
, first
, last
, TRUE
);
1382 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1384 /* Set selection to new caret item */
1385 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
1388 /* 4. repaint the new item with the focus */
1389 descr
->focus_item
= index
;
1390 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1391 if (descr
->caret_on
&& (descr
->in_focus
))
1392 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1396 /***********************************************************************
1397 * LISTBOX_InsertItem
1399 static LRESULT
LISTBOX_InsertItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1400 LPSTR str
, DWORD data
)
1404 INT oldfocus
= descr
->focus_item
;
1406 if (index
== -1) index
= descr
->nb_items
;
1407 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1408 if (!descr
->items
) max_items
= 0;
1409 else max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
1410 if (descr
->nb_items
== max_items
)
1412 /* We need to grow the array */
1413 max_items
+= LB_ARRAY_GRANULARITY
;
1414 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1415 max_items
* sizeof(LB_ITEMDATA
) )))
1417 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1420 descr
->items
= item
;
1423 /* Insert the item structure */
1425 item
= &descr
->items
[index
];
1426 if (index
< descr
->nb_items
)
1427 RtlMoveMemory( item
+ 1, item
,
1428 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1432 item
->selected
= FALSE
;
1435 /* Get item height */
1437 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1439 MEASUREITEMSTRUCT mis
;
1441 mis
.CtlType
= ODT_LISTBOX
;
1442 mis
.CtlID
= wnd
->wIDmenu
;
1444 mis
.itemData
= descr
->items
[index
].data
;
1445 mis
.itemHeight
= descr
->item_height
;
1446 SendMessageA( descr
->owner
, WM_MEASUREITEM
, wnd
->wIDmenu
, (LPARAM
)&mis
);
1447 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1448 TRACE("[%04x]: measure item %d (%s) = %d\n",
1449 wnd
->hwndSelf
, index
, str
? str
: "", item
->height
);
1452 /* Repaint the items */
1454 LISTBOX_UpdateScroll( wnd
, descr
);
1455 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1457 /* Move selection and focused item */
1458 /* If listbox was empty, set focus to the first item */
1459 if (descr
->nb_items
== 1)
1460 LISTBOX_SetCaretIndex( wnd
, descr
, 0, FALSE
);
1461 /* single select don't change selection index in win31 */
1462 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1464 descr
->selected_item
++;
1465 LISTBOX_SetSelection( wnd
, descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1469 if (index
<= descr
->selected_item
)
1471 descr
->selected_item
++;
1472 descr
->focus_item
= oldfocus
; /* focus not changed */
1479 /***********************************************************************
1480 * LISTBOX_InsertString
1482 static LRESULT
LISTBOX_InsertString( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1485 LPSTR new_str
= NULL
;
1489 if (HAS_STRINGS(descr
))
1492 if (!(new_str
= HEAP_strdupA( descr
->heap
, 0, str
)))
1494 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1498 else data
= (DWORD
)str
;
1500 if (index
== -1) index
= descr
->nb_items
;
1501 if ((ret
= LISTBOX_InsertItem( wnd
, descr
, index
, new_str
, data
)) != 0)
1503 if (new_str
) HeapFree( descr
->heap
, 0, new_str
);
1507 TRACE("[%04x]: added item %d '%s'\n",
1508 wnd
->hwndSelf
, index
, HAS_STRINGS(descr
) ? new_str
: "" );
1513 /***********************************************************************
1514 * LISTBOX_DeleteItem
1516 * Delete the content of an item. 'index' must be a valid index.
1518 static void LISTBOX_DeleteItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1520 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1521 * while Win95 sends it for all items with user data.
1522 * It's probably better to send it too often than not
1523 * often enough, so this is what we do here.
1525 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1527 DELETEITEMSTRUCT dis
;
1529 dis
.CtlType
= ODT_LISTBOX
;
1530 dis
.CtlID
= wnd
->wIDmenu
;
1532 dis
.hwndItem
= wnd
->hwndSelf
;
1533 dis
.itemData
= descr
->items
[index
].data
;
1534 SendMessageA( descr
->owner
, WM_DELETEITEM
, wnd
->wIDmenu
, (LPARAM
)&dis
);
1536 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1537 HeapFree( descr
->heap
, 0, descr
->items
[index
].str
);
1541 /***********************************************************************
1542 * LISTBOX_RemoveItem
1544 * Remove an item from the listbox and delete its content.
1546 static LRESULT
LISTBOX_RemoveItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1551 if (index
== -1) index
= descr
->nb_items
- 1;
1552 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1554 /* We need to invalidate the original rect instead of the updated one. */
1555 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1557 LISTBOX_DeleteItem( wnd
, descr
, index
);
1559 /* Remove the item */
1561 item
= &descr
->items
[index
];
1562 if (index
< descr
->nb_items
-1)
1563 RtlMoveMemory( item
, item
+ 1,
1564 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1566 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1568 /* Shrink the item array if possible */
1570 max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1571 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1573 max_items
-= LB_ARRAY_GRANULARITY
;
1574 item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1575 max_items
* sizeof(LB_ITEMDATA
) );
1576 if (item
) descr
->items
= item
;
1578 /* Repaint the items */
1580 LISTBOX_UpdateScroll( wnd
, descr
);
1581 /* if we removed the scrollbar, reset the top of the list
1582 (correct for owner-drawn ???) */
1583 if (descr
->nb_items
== descr
->page_size
)
1584 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1586 /* Move selection and focused item */
1587 if (!IS_MULTISELECT(descr
))
1589 if (index
== descr
->selected_item
)
1590 descr
->selected_item
= -1;
1591 else if (index
< descr
->selected_item
)
1593 descr
->selected_item
--;
1594 if (ISWIN31
) /* win 31 do not change the selected item number */
1595 LISTBOX_SetSelection( wnd
, descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1599 if (descr
->focus_item
>= descr
->nb_items
)
1601 descr
->focus_item
= descr
->nb_items
- 1;
1602 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1608 /***********************************************************************
1609 * LISTBOX_ResetContent
1611 static void LISTBOX_ResetContent( WND
*wnd
, LB_DESCR
*descr
)
1615 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( wnd
, descr
, i
);
1616 if (descr
->items
) HeapFree( descr
->heap
, 0, descr
->items
);
1617 descr
->nb_items
= 0;
1618 descr
->top_item
= 0;
1619 descr
->selected_item
= -1;
1620 descr
->focus_item
= 0;
1621 descr
->anchor_item
= -1;
1622 descr
->items
= NULL
;
1626 /***********************************************************************
1629 static LRESULT
LISTBOX_SetCount( WND
*wnd
, LB_DESCR
*descr
, INT count
)
1633 if (HAS_STRINGS(descr
)) return LB_ERR
;
1634 /* FIXME: this is far from optimal... */
1635 if (count
> descr
->nb_items
)
1637 while (count
> descr
->nb_items
)
1638 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, 0 )) < 0)
1641 else if (count
< descr
->nb_items
)
1643 while (count
< descr
->nb_items
)
1644 if ((ret
= LISTBOX_RemoveItem( wnd
, descr
, -1 )) < 0)
1651 /***********************************************************************
1654 static LRESULT
LISTBOX_Directory( WND
*wnd
, LB_DESCR
*descr
, UINT attrib
,
1655 LPCSTR filespec
, BOOL long_names
)
1658 LRESULT ret
= LB_OKAY
;
1659 WIN32_FIND_DATAA entry
;
1662 /* don't scan directory if we just want drives exclusively */
1663 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1664 /* scan directory */
1665 if ((handle
= FindFirstFileA(filespec
,&entry
)) == INVALID_HANDLE_VALUE
)
1667 if (GetLastError() != ERROR_NO_MORE_FILES
) return LB_ERR
;
1674 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1676 if (!(attrib
& DDL_DIRECTORY
) ||
1677 !strcmp( entry
.cAlternateFileName
, "." )) continue;
1678 if (long_names
) sprintf( buffer
, "[%s]", entry
.cFileName
);
1679 else sprintf( buffer
, "[%s]", entry
.cAlternateFileName
);
1681 else /* not a directory */
1683 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1684 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1686 if ((attrib
& DDL_EXCLUSIVE
) &&
1687 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1690 if (long_names
) strcpy( buffer
, entry
.cFileName
);
1691 else strcpy( buffer
, entry
.cAlternateFileName
);
1693 if (!long_names
) CharLowerA( buffer
);
1694 pos
= LISTBOX_FindFileStrPos( wnd
, descr
, buffer
);
1695 if ((ret
= LISTBOX_InsertString( wnd
, descr
, pos
, buffer
)) < 0)
1697 } while (FindNextFileA( handle
, &entry
));
1698 FindClose( handle
);
1703 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1705 char buffer
[] = "[-a-]";
1707 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++, buffer
[2]++)
1709 if (!DRIVE_IsValid(drive
)) continue;
1710 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, buffer
)) < 0)
1718 /***********************************************************************
1719 * LISTBOX_HandleVScroll
1721 static LRESULT
LISTBOX_HandleVScroll( WND
*wnd
, LB_DESCR
*descr
,
1722 WPARAM wParam
, LPARAM lParam
)
1726 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1727 switch(LOWORD(wParam
))
1730 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
- 1, TRUE
);
1733 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ 1, TRUE
);
1736 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-
1737 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1740 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+
1741 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1743 case SB_THUMBPOSITION
:
1744 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
), TRUE
);
1747 info
.cbSize
= sizeof(info
);
1748 info
.fMask
= SIF_TRACKPOS
;
1749 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1750 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
, TRUE
);
1753 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1756 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1763 /***********************************************************************
1764 * LISTBOX_HandleHScroll
1766 static LRESULT
LISTBOX_HandleHScroll( WND
*wnd
, LB_DESCR
*descr
,
1767 WPARAM wParam
, LPARAM lParam
)
1772 if (descr
->style
& LBS_MULTICOLUMN
)
1774 switch(LOWORD(wParam
))
1777 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-descr
->page_size
,
1781 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+descr
->page_size
,
1785 page
= descr
->width
/ descr
->column_width
;
1786 if (page
< 1) page
= 1;
1787 LISTBOX_SetTopItem( wnd
, descr
,
1788 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1791 page
= descr
->width
/ descr
->column_width
;
1792 if (page
< 1) page
= 1;
1793 LISTBOX_SetTopItem( wnd
, descr
,
1794 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1796 case SB_THUMBPOSITION
:
1797 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1801 info
.cbSize
= sizeof(info
);
1802 info
.fMask
= SIF_TRACKPOS
;
1803 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1804 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1808 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1811 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1815 else if (descr
->horz_extent
)
1817 switch(LOWORD(wParam
))
1820 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
- 1 );
1823 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
+ 1 );
1826 LISTBOX_SetHorizontalPos( wnd
, descr
,
1827 descr
->horz_pos
- descr
->width
);
1830 LISTBOX_SetHorizontalPos( wnd
, descr
,
1831 descr
->horz_pos
+ descr
->width
);
1833 case SB_THUMBPOSITION
:
1834 LISTBOX_SetHorizontalPos( wnd
, descr
, HIWORD(wParam
) );
1837 info
.cbSize
= sizeof(info
);
1838 info
.fMask
= SIF_TRACKPOS
;
1839 GetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
);
1840 LISTBOX_SetHorizontalPos( wnd
, descr
, info
.nTrackPos
);
1843 LISTBOX_SetHorizontalPos( wnd
, descr
, 0 );
1846 LISTBOX_SetHorizontalPos( wnd
, descr
,
1847 descr
->horz_extent
- descr
->width
);
1854 static LRESULT
LISTBOX_HandleMouseWheel(WND
*wnd
, LB_DESCR
*descr
,WPARAM wParam
, LPARAM lParam
)
1856 short gcWheelDelta
= 0;
1857 UINT pulScrollLines
= 3;
1859 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1861 gcWheelDelta
-= (short) HIWORD(wParam
);
1863 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
1865 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
1866 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
1867 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ cLineScroll
, TRUE
);
1872 /***********************************************************************
1873 * LISTBOX_HandleLButtonDown
1875 static LRESULT
LISTBOX_HandleLButtonDown( WND
*wnd
, LB_DESCR
*descr
,
1876 WPARAM wParam
, INT x
, INT y
)
1878 INT index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1879 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1880 wnd
->hwndSelf
, x
, y
, index
);
1881 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
1883 if (!descr
->in_focus
)
1885 if( !descr
->lphc
) SetFocus( wnd
->hwndSelf
);
1886 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
1887 : descr
->lphc
->self
->hwndSelf
);
1892 if (descr
->style
& LBS_EXTENDEDSEL
)
1894 /* we should perhaps make sure that all items are deselected
1895 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1896 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1897 LISTBOX_SetSelection( wnd, descr, -1, FALSE, FALSE);
1900 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1901 if (wParam
& MK_CONTROL
)
1903 LISTBOX_SetCaretIndex( wnd
, descr
, index
, FALSE
);
1904 LISTBOX_SetSelection( wnd
, descr
, index
,
1905 !descr
->items
[index
].selected
,
1906 (descr
->style
& LBS_NOTIFY
) != 0);
1908 else LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1912 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1913 LISTBOX_SetSelection( wnd
, descr
, index
,
1914 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1915 !descr
->items
[index
].selected
),
1916 (descr
->style
& LBS_NOTIFY
) != 0 );
1920 descr
->captured
= TRUE
;
1921 SetCapture( wnd
->hwndSelf
);
1922 if (index
!= -1 && !descr
->lphc
)
1924 if (descr
->style
& LBS_NOTIFY
)
1925 SendMessageA( descr
->owner
, WM_LBTRACKPOINT
, index
,
1926 MAKELPARAM( x
, y
) );
1927 if (wnd
->dwExStyle
& WS_EX_DRAGDETECT
)
1934 if (DragDetect( wnd
->hwndSelf
, pt
))
1935 SendMessageA( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
1942 /*************************************************************************
1943 * LISTBOX_HandleLButtonDownCombo [Internal]
1945 * Process LButtonDown message for the ComboListBox
1948 * pWnd [I] The windows internal structure
1949 * pDescr [I] The ListBox internal structure
1950 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
1951 * x [I] X Mouse Coordinate
1952 * y [I] Y Mouse Coordinate
1955 * 0 since we are processing the WM_LBUTTONDOWN Message
1958 * This function is only to be used when a ListBox is a ComboListBox
1961 static LRESULT
LISTBOX_HandleLButtonDownCombo( WND
*pWnd
, LB_DESCR
*pDescr
,
1962 UINT msg
, WPARAM wParam
, INT x
, INT y
)
1964 RECT clientRect
, screenRect
;
1970 GetClientRect(pWnd
->hwndSelf
, &clientRect
);
1972 if(PtInRect(&clientRect
, mousePos
))
1974 /* MousePos is in client, resume normal processing */
1975 if (msg
== WM_LBUTTONDOWN
)
1977 pDescr
->lphc
->droppedIndex
= pDescr
->nb_items
? pDescr
->selected_item
: -1;
1978 return LISTBOX_HandleLButtonDown( pWnd
, pDescr
, wParam
, x
, y
);
1980 else if (pDescr
->style
& LBS_NOTIFY
)
1981 SEND_NOTIFICATION( pWnd
, pDescr
, LBN_DBLCLK
);
1986 POINT screenMousePos
;
1987 HWND hWndOldCapture
;
1989 /* Check the Non-Client Area */
1990 screenMousePos
= mousePos
;
1991 hWndOldCapture
= GetCapture();
1993 GetWindowRect(pWnd
->hwndSelf
, &screenRect
);
1994 ClientToScreen(pWnd
->hwndSelf
, &screenMousePos
);
1996 if(!PtInRect(&screenRect
, screenMousePos
))
1998 LISTBOX_SetSelection( pWnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
, FALSE
);
1999 COMBO_FlipListbox( pDescr
->lphc
, FALSE
, FALSE
);
2004 /* Check to see the NC is a scrollbar */
2006 /* Check Vertical scroll bar */
2007 if (pWnd
->dwStyle
& WS_VSCROLL
)
2009 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2010 if (PtInRect( &clientRect
, mousePos
))
2012 nHitTestType
= HTVSCROLL
;
2015 /* Check horizontal scroll bar */
2016 if (pWnd
->dwStyle
& WS_HSCROLL
)
2018 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2019 if (PtInRect( &clientRect
, mousePos
))
2021 nHitTestType
= HTHSCROLL
;
2024 /* Windows sends this message when a scrollbar is clicked
2027 if(nHitTestType
!= 0)
2029 SendMessageA(pWnd
->hwndSelf
, WM_NCLBUTTONDOWN
, nHitTestType
,
2030 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2032 /* Resume the Capture after scrolling is complete
2034 if(hWndOldCapture
!= 0)
2036 SetCapture(hWndOldCapture
);
2043 /***********************************************************************
2044 * LISTBOX_HandleLButtonUp
2046 static LRESULT
LISTBOX_HandleLButtonUp( WND
*wnd
, LB_DESCR
*descr
)
2048 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2049 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2050 LISTBOX_Timer
= LB_TIMER_NONE
;
2051 if (descr
->captured
)
2053 descr
->captured
= FALSE
;
2054 if (GetCapture() == wnd
->hwndSelf
) ReleaseCapture();
2055 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2056 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2062 /***********************************************************************
2063 * LISTBOX_HandleTimer
2065 * Handle scrolling upon a timer event.
2066 * Return TRUE if scrolling should continue.
2068 static LRESULT
LISTBOX_HandleTimer( WND
*wnd
, LB_DESCR
*descr
,
2069 INT index
, TIMER_DIRECTION dir
)
2074 if (descr
->top_item
) index
= descr
->top_item
- 1;
2078 if (descr
->top_item
) index
-= descr
->page_size
;
2081 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( wnd
, descr
);
2082 if (index
== descr
->focus_item
) index
++;
2083 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2085 case LB_TIMER_RIGHT
:
2086 if (index
+ descr
->page_size
< descr
->nb_items
)
2087 index
+= descr
->page_size
;
2092 if (index
== descr
->focus_item
) return FALSE
;
2093 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
2098 /***********************************************************************
2099 * LISTBOX_HandleSystemTimer
2101 * WM_SYSTIMER handler.
2103 static LRESULT
LISTBOX_HandleSystemTimer( WND
*wnd
, LB_DESCR
*descr
)
2105 if (!LISTBOX_HandleTimer( wnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
2107 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2108 LISTBOX_Timer
= LB_TIMER_NONE
;
2114 /***********************************************************************
2115 * LISTBOX_HandleMouseMove
2117 * WM_MOUSEMOVE handler.
2119 static void LISTBOX_HandleMouseMove( WND
*wnd
, LB_DESCR
*descr
,
2123 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2125 if (!descr
->captured
) return;
2127 if (descr
->style
& LBS_MULTICOLUMN
)
2130 else if (y
>= descr
->item_height
* descr
->page_size
)
2131 y
= descr
->item_height
* descr
->page_size
- 1;
2135 dir
= LB_TIMER_LEFT
;
2138 else if (x
>= descr
->width
)
2140 dir
= LB_TIMER_RIGHT
;
2141 x
= descr
->width
- 1;
2146 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2147 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2150 index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
2151 if (index
== -1) index
= descr
->focus_item
;
2152 if (!LISTBOX_HandleTimer( wnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2154 /* Start/stop the system timer */
2156 if (dir
!= LB_TIMER_NONE
)
2157 SetSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2158 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2159 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2160 LISTBOX_Timer
= dir
;
2164 /***********************************************************************
2165 * LISTBOX_HandleKeyDown
2167 static LRESULT
LISTBOX_HandleKeyDown( WND
*wnd
, LB_DESCR
*descr
, WPARAM wParam
)
2170 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2171 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2172 bForceSelection
= FALSE
; /* only for single select list */
2174 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2176 caret
= SendMessageA( descr
->owner
, WM_VKEYTOITEM
,
2177 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2179 if (caret
== -2) return 0;
2181 if (caret
== -1) switch(wParam
)
2184 if (descr
->style
& LBS_MULTICOLUMN
)
2186 bForceSelection
= FALSE
;
2187 if (descr
->focus_item
>= descr
->page_size
)
2188 caret
= descr
->focus_item
- descr
->page_size
;
2193 caret
= descr
->focus_item
- 1;
2194 if (caret
< 0) caret
= 0;
2197 if (descr
->style
& LBS_MULTICOLUMN
)
2199 bForceSelection
= FALSE
;
2200 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2201 caret
= descr
->focus_item
+ descr
->page_size
;
2206 caret
= descr
->focus_item
+ 1;
2207 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
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
< 0) caret
= 0;
2221 if (descr
->style
& LBS_MULTICOLUMN
)
2223 INT page
= descr
->width
/ descr
->column_width
;
2224 if (page
< 1) page
= 1;
2225 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2227 else caret
= descr
->focus_item
+LISTBOX_GetCurrentPageSize(wnd
,descr
)-1;
2228 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2234 caret
= descr
->nb_items
- 1;
2237 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2238 else if (descr
->style
& LBS_MULTIPLESEL
)
2240 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
,
2241 !descr
->items
[descr
->focus_item
].selected
,
2242 (descr
->style
& LBS_NOTIFY
) != 0 );
2246 bForceSelection
= FALSE
;
2248 if (bForceSelection
) /* focused item is used instead of key */
2249 caret
= descr
->focus_item
;
2252 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
2253 !(GetKeyState( VK_SHIFT
) & 0x8000))
2254 descr
->anchor_item
= caret
;
2255 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2256 LISTBOX_SetSelection( wnd
, descr
, caret
, TRUE
, FALSE
);
2257 if (descr
->style
& LBS_NOTIFY
)
2259 if( descr
->lphc
&& CB_GETTYPE(descr
->lphc
) != CBS_SIMPLE
)
2261 /* make sure that combo parent doesn't hide us */
2262 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2264 if (descr
->nb_items
) SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2271 /***********************************************************************
2272 * LISTBOX_HandleChar
2274 static LRESULT
LISTBOX_HandleChar( WND
*wnd
, LB_DESCR
*descr
,
2280 str
[0] = wParam
& 0xff;
2283 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2285 caret
= SendMessageA( descr
->owner
, WM_CHARTOITEM
,
2286 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2288 if (caret
== -2) return 0;
2291 caret
= LISTBOX_FindString( wnd
, descr
, descr
->focus_item
, str
, FALSE
);
2294 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2295 LISTBOX_SetSelection( wnd
, descr
, caret
, TRUE
, FALSE
);
2296 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2297 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2298 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2304 /***********************************************************************
2307 static BOOL
LISTBOX_Create( WND
*wnd
, LPHEADCOMBO lphc
)
2310 MEASUREITEMSTRUCT mis
;
2313 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2315 if (!(descr
->heap
= HeapCreate( 0, 0x10000, 0 )))
2317 HeapFree( GetProcessHeap(), 0, descr
);
2320 GetClientRect( wnd
->hwndSelf
, &rect
);
2321 descr
->owner
= GetParent( wnd
->hwndSelf
);
2322 descr
->style
= wnd
->dwStyle
;
2323 descr
->width
= rect
.right
- rect
.left
;
2324 descr
->height
= rect
.bottom
- rect
.top
;
2325 descr
->items
= NULL
;
2326 descr
->nb_items
= 0;
2327 descr
->top_item
= 0;
2328 descr
->selected_item
= -1;
2329 descr
->focus_item
= 0;
2330 descr
->anchor_item
= -1;
2331 descr
->item_height
= 1;
2332 descr
->page_size
= 1;
2333 descr
->column_width
= 150;
2334 descr
->horz_extent
= (wnd
->dwStyle
& WS_HSCROLL
) ? 1 : 0;
2335 descr
->horz_pos
= 0;
2338 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2339 descr
->in_focus
= FALSE
;
2340 descr
->captured
= FALSE
;
2342 descr
->locale
= 0; /* FIXME */
2345 if( ( GetExpWinVer16( wnd
->hInstance
) & 0xFF00 ) == 0x0300
2346 && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2348 /* Win95 document "List Box Differences" from MSDN:
2349 If a list box in a version 3.x application has either the
2350 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2351 horizontal and vertical scroll bars.
2353 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2358 TRACE_(combo
)("[%04x]: resetting owner %04x -> %04x\n",
2359 wnd
->hwndSelf
, descr
->owner
, lphc
->self
->hwndSelf
);
2360 descr
->owner
= lphc
->self
->hwndSelf
;
2363 *(LB_DESCR
**)wnd
->wExtra
= descr
;
2365 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2367 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2368 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2369 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2370 descr
->item_height
= LISTBOX_SetFont( wnd
, descr
, 0 );
2372 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2374 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2376 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2377 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2381 mis
.CtlType
= ODT_LISTBOX
;
2382 mis
.CtlID
= wnd
->wIDmenu
;
2386 mis
.itemHeight
= descr
->item_height
;
2387 SendMessageA( descr
->owner
, WM_MEASUREITEM
, wnd
->wIDmenu
, (LPARAM
)&mis
);
2388 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2396 /***********************************************************************
2399 static BOOL
LISTBOX_Destroy( WND
*wnd
, LB_DESCR
*descr
)
2401 LISTBOX_ResetContent( wnd
, descr
);
2402 HeapDestroy( descr
->heap
);
2403 HeapFree( GetProcessHeap(), 0, descr
);
2409 /***********************************************************************
2412 static inline LRESULT WINAPI
ListBoxWndProc_locked( WND
* wnd
, UINT msg
,
2413 WPARAM wParam
, LPARAM lParam
)
2417 HWND hwnd
= wnd
->hwndSelf
;
2420 if (!(descr
= *(LB_DESCR
**)wnd
->wExtra
))
2426 if (!LISTBOX_Create( wnd
, NULL
))
2428 TRACE("creating wnd=%04x descr=%p\n",
2429 hwnd
, *(LB_DESCR
**)wnd
->wExtra
);
2435 * When a listbox is not in a combobox and the look
2436 * is win95, the WS_BORDER style is replaced with
2437 * the WS_EX_CLIENTEDGE style.
2439 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
2440 (wnd
->dwStyle
& WS_BORDER
) )
2442 wnd
->dwExStyle
|= WS_EX_CLIENTEDGE
;
2443 wnd
->dwStyle
&= ~ WS_BORDER
;
2448 /* Ignore all other messages before we get a WM_CREATE */
2449 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2452 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2453 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2456 case LB_RESETCONTENT16
:
2457 case LB_RESETCONTENT
:
2458 LISTBOX_ResetContent( wnd
, descr
);
2459 LISTBOX_UpdateScroll( wnd
, descr
);
2460 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
2463 case LB_ADDSTRING16
:
2464 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2467 wParam
= LISTBOX_FindStringPos( wnd
, descr
, (LPCSTR
)lParam
, FALSE
);
2468 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2470 case LB_INSERTSTRING16
:
2471 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2472 wParam
= (INT
)(INT16
)wParam
;
2474 case LB_INSERTSTRING
:
2475 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2478 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2481 wParam
= LISTBOX_FindFileStrPos( wnd
, descr
, (LPCSTR
)lParam
);
2482 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2484 case LB_DELETESTRING16
:
2485 case LB_DELETESTRING
:
2486 if (LISTBOX_RemoveItem( wnd
, descr
, wParam
) != LB_ERR
)
2487 return descr
->nb_items
;
2491 case LB_GETITEMDATA16
:
2492 case LB_GETITEMDATA
:
2493 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2495 return descr
->items
[wParam
].data
;
2497 case LB_SETITEMDATA16
:
2498 case LB_SETITEMDATA
:
2499 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2501 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2506 return descr
->nb_items
;
2509 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2512 return LISTBOX_GetText( wnd
, descr
, wParam
, (LPSTR
)lParam
);
2514 case LB_GETTEXTLEN16
:
2517 if (wParam
>= descr
->nb_items
)
2519 return (HAS_STRINGS(descr
) ? strlen(descr
->items
[wParam
].str
)
2522 case LB_GETCURSEL16
:
2524 if (descr
->nb_items
==0)
2526 if (!IS_MULTISELECT(descr
))
2527 return descr
->selected_item
;
2529 if (descr
->selected_item
!=-1)
2530 return descr
->selected_item
;
2532 return descr
->focus_item
;
2533 /* otherwise, if the user tries to move the selection with the */
2534 /* arrow keys, we will give the application something to choke on */
2535 case LB_GETTOPINDEX16
:
2536 case LB_GETTOPINDEX
:
2537 return descr
->top_item
;
2539 case LB_GETITEMHEIGHT16
:
2540 case LB_GETITEMHEIGHT
:
2541 return LISTBOX_GetItemHeight( wnd
, descr
, wParam
);
2543 case LB_SETITEMHEIGHT16
:
2544 lParam
= LOWORD(lParam
);
2546 case LB_SETITEMHEIGHT
:
2547 return LISTBOX_SetItemHeight( wnd
, descr
, wParam
, lParam
);
2549 case LB_ITEMFROMPOINT
:
2554 pt
.x
= LOWORD(lParam
);
2555 pt
.y
= HIWORD(lParam
);
2558 rect
.right
= descr
->width
;
2559 rect
.bottom
= descr
->height
;
2561 return MAKELONG( LISTBOX_GetItemFromPoint(wnd
, descr
, pt
.x
, pt
.y
),
2562 !PtInRect( &rect
, pt
) );
2565 case LB_SETCARETINDEX16
:
2566 case LB_SETCARETINDEX
:
2567 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2568 if (LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, !lParam
) == LB_ERR
)
2575 case LB_GETCARETINDEX16
:
2576 case LB_GETCARETINDEX
:
2577 return descr
->focus_item
;
2579 case LB_SETTOPINDEX16
:
2580 case LB_SETTOPINDEX
:
2581 return LISTBOX_SetTopItem( wnd
, descr
, wParam
, TRUE
);
2583 case LB_SETCOLUMNWIDTH16
:
2584 case LB_SETCOLUMNWIDTH
:
2585 return LISTBOX_SetColumnWidth( wnd
, descr
, wParam
);
2587 case LB_GETITEMRECT16
:
2590 ret
= LISTBOX_GetItemRect( wnd
, descr
, (INT16
)wParam
, &rect
);
2591 CONV_RECT32TO16( &rect
, (RECT16
*)PTR_SEG_TO_LIN(lParam
) );
2595 case LB_GETITEMRECT
:
2596 return LISTBOX_GetItemRect( wnd
, descr
, wParam
, (RECT
*)lParam
);
2598 case LB_FINDSTRING16
:
2599 wParam
= (INT
)(INT16
)wParam
;
2600 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2603 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, FALSE
);
2605 case LB_FINDSTRINGEXACT16
:
2606 wParam
= (INT
)(INT16
)wParam
;
2607 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2609 case LB_FINDSTRINGEXACT
:
2610 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2612 case LB_SELECTSTRING16
:
2613 wParam
= (INT
)(INT16
)wParam
;
2614 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2616 case LB_SELECTSTRING
:
2618 INT index
= LISTBOX_FindString( wnd
, descr
, wParam
,
2619 (LPCSTR
)lParam
, FALSE
);
2620 if (index
== LB_ERR
)
2622 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
2627 wParam
= (INT
)(INT16
)wParam
;
2630 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2632 return descr
->items
[wParam
].selected
;
2635 lParam
= (INT
)(INT16
)lParam
;
2638 return LISTBOX_SetSelection( wnd
, descr
, lParam
, wParam
, FALSE
);
2640 case LB_SETCURSEL16
:
2641 wParam
= (INT
)(INT16
)wParam
;
2644 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2645 LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, TRUE
);
2646 return LISTBOX_SetSelection( wnd
, descr
, wParam
, TRUE
, FALSE
);
2648 case LB_GETSELCOUNT16
:
2649 case LB_GETSELCOUNT
:
2650 return LISTBOX_GetSelCount( wnd
, descr
);
2652 case LB_GETSELITEMS16
:
2653 return LISTBOX_GetSelItems16( wnd
, descr
, wParam
,
2654 (LPINT16
)PTR_SEG_TO_LIN(lParam
) );
2656 case LB_GETSELITEMS
:
2657 return LISTBOX_GetSelItems( wnd
, descr
, wParam
, (LPINT
)lParam
);
2659 case LB_SELITEMRANGE16
:
2660 case LB_SELITEMRANGE
:
2661 if (LOWORD(lParam
) <= HIWORD(lParam
))
2662 return LISTBOX_SelectItemRange( wnd
, descr
, LOWORD(lParam
),
2663 HIWORD(lParam
), wParam
);
2665 return LISTBOX_SelectItemRange( wnd
, descr
, HIWORD(lParam
),
2666 LOWORD(lParam
), wParam
);
2668 case LB_SELITEMRANGEEX16
:
2669 case LB_SELITEMRANGEEX
:
2670 if ((INT
)lParam
>= (INT
)wParam
)
2671 return LISTBOX_SelectItemRange( wnd
, descr
, wParam
, lParam
, TRUE
);
2673 return LISTBOX_SelectItemRange( wnd
, descr
, lParam
, wParam
, FALSE
);
2675 case LB_GETHORIZONTALEXTENT16
:
2676 case LB_GETHORIZONTALEXTENT
:
2677 return descr
->horz_extent
;
2679 case LB_SETHORIZONTALEXTENT16
:
2680 case LB_SETHORIZONTALEXTENT
:
2681 return LISTBOX_SetHorizontalExtent( wnd
, descr
, wParam
);
2683 case LB_GETANCHORINDEX16
:
2684 case LB_GETANCHORINDEX
:
2685 return descr
->anchor_item
;
2687 case LB_SETANCHORINDEX16
:
2688 wParam
= (INT
)(INT16
)wParam
;
2690 case LB_SETANCHORINDEX
:
2691 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2693 descr
->anchor_item
= (INT
)wParam
;
2697 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2698 * be set automatically (this is different in Win32) */
2699 if (wParam
& DDL_DRIVES
) wParam
|= DDL_EXCLUSIVE
;
2700 return LISTBOX_Directory( wnd
, descr
, wParam
,
2701 (LPCSTR
)PTR_SEG_TO_LIN(lParam
), FALSE
);
2704 return LISTBOX_Directory( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2707 return descr
->locale
;
2710 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2713 case LB_INITSTORAGE
:
2714 return LISTBOX_InitStorage( wnd
, descr
, wParam
, (DWORD
)lParam
);
2717 return LISTBOX_SetCount( wnd
, descr
, (INT
)wParam
);
2719 case LB_SETTABSTOPS16
:
2720 return LISTBOX_SetTabStops( wnd
, descr
, (INT
)(INT16
)wParam
,
2721 (LPINT
)PTR_SEG_TO_LIN(lParam
), TRUE
);
2723 case LB_SETTABSTOPS
:
2724 return LISTBOX_SetTabStops( wnd
, descr
, wParam
, (LPINT
)lParam
, FALSE
);
2728 if (descr
->caret_on
)
2730 descr
->caret_on
= TRUE
;
2731 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2732 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2737 if (!descr
->caret_on
)
2739 descr
->caret_on
= FALSE
;
2740 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2741 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2745 return LISTBOX_Destroy( wnd
, descr
);
2748 InvalidateRect( hwnd
, NULL
, TRUE
);
2752 LISTBOX_SetRedraw( wnd
, descr
, wParam
!= 0 );
2756 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2761 HDC hdc
= ( wParam
) ? ((HDC
)wParam
)
2762 : BeginPaint( hwnd
, &ps
);
2763 ret
= LISTBOX_Paint( wnd
, descr
, hdc
);
2764 if( !wParam
) EndPaint( hwnd
, &ps
);
2768 LISTBOX_UpdateSize( wnd
, descr
);
2773 LISTBOX_SetFont( wnd
, descr
, (HFONT
)wParam
);
2774 if (lParam
) InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
2777 descr
->in_focus
= TRUE
;
2778 descr
->caret_on
= TRUE
;
2779 if (descr
->focus_item
!= -1)
2780 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2781 SEND_NOTIFICATION( wnd
, descr
, LBN_SETFOCUS
);
2784 descr
->in_focus
= FALSE
;
2785 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2786 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2787 SEND_NOTIFICATION( wnd
, descr
, LBN_KILLFOCUS
);
2790 return LISTBOX_HandleHScroll( wnd
, descr
, wParam
, lParam
);
2792 return LISTBOX_HandleVScroll( wnd
, descr
, wParam
, lParam
);
2793 case WM_MOUSEACTIVATE
:
2794 return MA_NOACTIVATE
;
2796 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2797 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2798 return LISTBOX_HandleMouseWheel( wnd
, descr
, wParam
, lParam
);
2799 case WM_LBUTTONDOWN
:
2800 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2801 (INT16
)LOWORD(lParam
),
2802 (INT16
)HIWORD(lParam
) );
2803 case WM_LBUTTONDBLCLK
:
2804 if (descr
->style
& LBS_NOTIFY
)
2805 SEND_NOTIFICATION( wnd
, descr
, LBN_DBLCLK
);
2808 if (GetCapture() == hwnd
)
2809 LISTBOX_HandleMouseMove( wnd
, descr
, (INT16
)LOWORD(lParam
),
2810 (INT16
)HIWORD(lParam
) );
2813 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2815 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2817 return LISTBOX_HandleChar( wnd
, descr
, wParam
);
2819 return LISTBOX_HandleSystemTimer( wnd
, descr
);
2821 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
2824 HBRUSH hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
2825 wParam
, (LPARAM
)wnd
->hwndSelf
);
2826 GetClientRect(hwnd
, &rect
);
2827 if (hbrush
) FillRect( (HDC
)wParam
, &rect
, hbrush
);
2832 return SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2836 case WM_QUERYDROPOBJECT
:
2841 LPDRAGINFO16 dragInfo
= (LPDRAGINFO16
)PTR_SEG_TO_LIN( (SEGPTR
)lParam
);
2842 dragInfo
->l
= LISTBOX_GetItemFromPoint( wnd
, descr
, dragInfo
->pt
.x
,
2844 return SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2849 if ((msg
>= WM_USER
) && (msg
< 0xc000))
2850 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2851 hwnd
, msg
, wParam
, lParam
);
2852 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2857 /***********************************************************************
2860 * This is just a wrapper for the real wndproc, it only does window locking
2863 LRESULT WINAPI
ListBoxWndProc( HWND hwnd
, UINT msg
,
2864 WPARAM wParam
, LPARAM lParam
)
2866 WND
* wndPtr
= WIN_FindWndPtr( hwnd
);
2867 LRESULT res
= ListBoxWndProc_locked(wndPtr
,msg
,wParam
,lParam
);
2869 WIN_ReleaseWndPtr(wndPtr
);
2873 /***********************************************************************
2876 LRESULT
COMBO_Directory( LPHEADCOMBO lphc
, UINT attrib
, LPSTR dir
, BOOL bLong
)
2878 WND
*wnd
= WIN_FindWndPtr( lphc
->hWndLBox
);
2882 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2885 LRESULT lRet
= LISTBOX_Directory( wnd
, descr
, attrib
, dir
, bLong
);
2887 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0,
2888 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
2889 WIN_ReleaseWndPtr(wnd
);
2892 WIN_ReleaseWndPtr(wnd
);
2897 /***********************************************************************
2898 * ComboLBWndProc_locked
2900 * The real combo listbox wndproc, but called with locked WND struct.
2902 static inline LRESULT WINAPI
ComboLBWndProc_locked( WND
* wnd
, UINT msg
,
2903 WPARAM wParam
, LPARAM lParam
)
2906 HWND hwnd
= wnd
->hwndSelf
;
2910 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2912 TRACE_(combo
)("[%04x]: msg %s wp %08x lp %08lx\n",
2913 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2915 if( descr
|| msg
== WM_CREATE
)
2917 LPHEADCOMBO lphc
= (descr
) ? descr
->lphc
: NULL
;
2922 #define lpcs ((LPCREATESTRUCTA)lParam)
2923 TRACE_(combo
)("\tpassed parent handle = 0x%08x\n",
2924 (UINT
)lpcs
->lpCreateParams
);
2926 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
2928 return LISTBOX_Create( wnd
, lphc
);
2930 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
2931 (CB_GETTYPE(lphc
) != CBS_SIMPLE
) )
2937 mousePos
.x
= (INT16
)LOWORD(lParam
);
2938 mousePos
.y
= (INT16
)HIWORD(lParam
);
2941 * If we are in a dropdown combobox, we simulate that
2942 * the mouse is captured to show the tracking of the item.
2944 GetClientRect(hwnd
, &clientRect
);
2946 if (PtInRect( &clientRect
, mousePos
))
2948 captured
= descr
->captured
;
2949 descr
->captured
= TRUE
;
2951 LISTBOX_HandleMouseMove( wnd
, descr
,
2952 mousePos
.x
, mousePos
.y
);
2954 descr
->captured
= captured
;
2959 LISTBOX_HandleMouseMove( wnd
, descr
,
2960 mousePos
.x
, mousePos
.y
);
2969 * If we are in Win3.1 look, go with the default behavior.
2971 return ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2974 if (TWEAK_WineLook
> WIN31_LOOK
)
2980 * If the mouse button "up" is not in the listbox,
2981 * we make sure there is no selection by re-selecting the
2982 * item that was selected when the listbox was made visible.
2984 mousePos
.x
= (INT16
)LOWORD(lParam
);
2985 mousePos
.y
= (INT16
)HIWORD(lParam
);
2987 GetClientRect(hwnd
, &clientRect
);
2990 * When the user clicks outside the combobox and the focus
2991 * is lost, the owning combobox will send a fake buttonup with
2992 * 0xFFFFFFF as the mouse location, we must also revert the
2993 * selection to the original selection.
2995 if ( (lParam
== 0xFFFFFFFF) ||
2996 (!PtInRect( &clientRect
, mousePos
)) )
2998 LISTBOX_MoveCaret( wnd
,
3004 return LISTBOX_HandleLButtonUp( wnd
, descr
);
3005 case WM_LBUTTONDBLCLK
:
3006 case WM_LBUTTONDOWN
:
3007 return LISTBOX_HandleLButtonDownCombo(wnd
, descr
, msg
, wParam
,
3008 (INT16
)LOWORD(lParam
),
3009 (INT16
)HIWORD(lParam
) );
3010 case WM_MOUSEACTIVATE
:
3011 return MA_NOACTIVATE
;
3015 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3017 /* for some reason(?) Windows makes it possible to
3018 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3020 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3021 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3022 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3024 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3028 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
3030 case LB_SETCURSEL16
:
3032 lRet
= ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
3033 lRet
=(lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
3036 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3041 return ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
3044 lRet
= DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3046 TRACE_(combo
)("\t default on msg [%04x]\n", (UINT16
)msg
);
3051 /***********************************************************************
3054 * NOTE: in Windows, winproc address of the ComboLBox is the same
3055 * as that of the Listbox.
3057 * This is just a wrapper for the real wndproc, it only does window locking
3060 LRESULT WINAPI
ComboLBWndProc( HWND hwnd
, UINT msg
,
3061 WPARAM wParam
, LPARAM lParam
)
3063 WND
*wnd
= WIN_FindWndPtr( hwnd
);
3064 LRESULT res
= ComboLBWndProc_locked(wnd
,msg
,wParam
,lParam
);
3066 WIN_ReleaseWndPtr(wnd
);