4 * Copyright 1996 Alexandre Julliard
12 #include "wine/winuser16.h"
13 #include "wine/winbase16.h"
19 #include "selectors.h"
22 #include "debugtools.h"
25 DEFAULT_DEBUG_CHANNEL(listbox
);
26 DECLARE_DEBUG_CHANNEL(combo
);
35 /* Items array granularity */
36 #define LB_ARRAY_GRANULARITY 16
38 /* Scrolling timeout in ms */
39 #define LB_SCROLL_TIMEOUT 50
41 /* Listbox system timer id */
44 /* flag listbox changed while setredraw false - internal style */
45 #define LBS_DISPLAYCHANGED 0x80000000
50 LPSTR str
; /* Item text */
51 BOOL selected
; /* Is item selected? */
52 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
53 DWORD data
; /* User data */
56 /* Listbox structure */
59 HANDLE heap
; /* Heap for this listbox */
60 HWND owner
; /* Owner window to send notifications to */
61 UINT style
; /* Window style */
62 INT width
; /* Window width */
63 INT height
; /* Window height */
64 LB_ITEMDATA
*items
; /* Array of items */
65 INT nb_items
; /* Number of items */
66 INT top_item
; /* Top visible item */
67 INT selected_item
; /* Selected item */
68 INT focus_item
; /* Item that has the focus */
69 INT anchor_item
; /* Anchor item for extended selection */
70 INT item_height
; /* Default item height */
71 INT page_size
; /* Items per listbox page */
72 INT column_width
; /* Column width for multi-column listboxes */
73 INT horz_extent
; /* Horizontal extent (0 if no hscroll) */
74 INT horz_pos
; /* Horizontal position */
75 INT nb_tabs
; /* Number of tabs in array */
76 INT
*tabs
; /* Array of tabs */
77 BOOL caret_on
; /* Is caret on? */
78 BOOL captured
; /* Is mouse captured? */
80 HFONT font
; /* Current font */
81 LCID locale
; /* Current locale for string comparisons */
82 LPHEADCOMBO lphc
; /* ComboLBox */
86 #define IS_OWNERDRAW(descr) \
87 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
89 #define HAS_STRINGS(descr) \
90 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
93 #define IS_MULTISELECT(descr) \
94 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
96 #define SEND_NOTIFICATION(wnd,descr,code) \
97 (SendMessageA( (descr)->owner, WM_COMMAND, \
98 MAKEWPARAM((wnd)->wIDmenu, (code)), (wnd)->hwndSelf ))
100 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
102 /* Current timer status */
112 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
115 /***********************************************************************
118 void LISTBOX_Dump( WND
*wnd
)
122 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
124 TRACE( "Listbox:\n" );
125 TRACE( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
126 wnd
->hwndSelf
, (UINT
)descr
, descr
->heap
, descr
->nb_items
,
128 for (i
= 0, item
= descr
->items
; i
< descr
->nb_items
; i
++, item
++)
130 TRACE( "%4d: %-40s %d %08lx %3d\n",
131 i
, item
->str
, item
->selected
, item
->data
, item
->height
);
136 /***********************************************************************
137 * LISTBOX_GetCurrentPageSize
139 * Return the current page size
141 static INT
LISTBOX_GetCurrentPageSize( WND
*wnd
, LB_DESCR
*descr
)
144 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
145 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
147 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
149 if (i
== descr
->top_item
) return 1;
150 else return i
- descr
->top_item
;
154 /***********************************************************************
155 * LISTBOX_GetMaxTopIndex
157 * Return the maximum possible index for the top of the listbox.
159 static INT
LISTBOX_GetMaxTopIndex( WND
*wnd
, LB_DESCR
*descr
)
163 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
165 page
= descr
->height
;
166 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
167 if ((page
-= descr
->items
[max
].height
) < 0) break;
168 if (max
< descr
->nb_items
- 1) max
++;
170 else if (descr
->style
& LBS_MULTICOLUMN
)
172 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
173 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
174 max
= (max
- page
) * descr
->page_size
;
178 max
= descr
->nb_items
- descr
->page_size
;
180 if (max
< 0) max
= 0;
185 /***********************************************************************
186 * LISTBOX_UpdateScroll
188 * Update the scrollbars. Should be called whenever the content
189 * of the listbox changes.
191 static void LISTBOX_UpdateScroll( WND
*wnd
, LB_DESCR
*descr
)
195 /* Check the listbox scroll bar flags individually before we call
196 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
197 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
198 scroll bar when we do not need one.
199 if (!(descr->style & WS_VSCROLL)) return;
202 /* It is important that we check descr->style, and not wnd->dwStyle,
203 for WS_VSCROLL, as the former is exactly the one passed in
204 argument to CreateWindow.
205 In Windows (and from now on in Wine :) a listbox created
206 with such a style (no WS_SCROLL) does not update
207 the scrollbar with listbox-related data, thus letting
208 the programmer use it for his/her own purposes. */
210 if (descr
->style
& LBS_NOREDRAW
) return;
211 info
.cbSize
= sizeof(info
);
213 if (descr
->style
& LBS_MULTICOLUMN
)
216 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
217 info
.nPos
= descr
->top_item
/ descr
->page_size
;
218 info
.nPage
= descr
->width
/ descr
->column_width
;
219 if (info
.nPage
< 1) info
.nPage
= 1;
220 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
221 if (descr
->style
& LBS_DISABLENOSCROLL
)
222 info
.fMask
|= SIF_DISABLENOSCROLL
;
223 if (descr
->style
& WS_HSCROLL
)
224 SetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
226 info
.fMask
= SIF_RANGE
;
227 if (descr
->style
& WS_VSCROLL
)
228 SetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
233 info
.nMax
= descr
->nb_items
- 1;
234 info
.nPos
= descr
->top_item
;
235 info
.nPage
= LISTBOX_GetCurrentPageSize( wnd
, descr
);
236 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
237 if (descr
->style
& LBS_DISABLENOSCROLL
)
238 info
.fMask
|= SIF_DISABLENOSCROLL
;
239 if (descr
->style
& WS_VSCROLL
)
240 SetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
242 if (descr
->horz_extent
)
245 info
.nMax
= descr
->horz_extent
- 1;
246 info
.nPos
= descr
->horz_pos
;
247 info
.nPage
= descr
->width
;
248 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
249 if (descr
->style
& LBS_DISABLENOSCROLL
)
250 info
.fMask
|= SIF_DISABLENOSCROLL
;
251 if (descr
->style
& WS_HSCROLL
)
252 SetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
259 /***********************************************************************
262 * Set the top item of the listbox, scrolling up or down if necessary.
264 static LRESULT
LISTBOX_SetTopItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
267 INT max
= LISTBOX_GetMaxTopIndex( wnd
, descr
);
268 if (index
> max
) index
= max
;
269 if (index
< 0) index
= 0;
270 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
271 if (descr
->top_item
== index
) return LB_OKAY
;
272 if (descr
->style
& LBS_MULTICOLUMN
)
274 INT diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
275 if (scroll
&& (abs(diff
) < descr
->width
))
276 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
277 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
285 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
289 if (index
> descr
->top_item
)
291 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
292 diff
-= descr
->items
[i
].height
;
296 for (i
= index
; i
< descr
->top_item
; i
++)
297 diff
+= descr
->items
[i
].height
;
301 diff
= (descr
->top_item
- index
) * descr
->item_height
;
303 if (abs(diff
) < descr
->height
)
304 ScrollWindowEx( wnd
->hwndSelf
, 0, diff
, NULL
, NULL
, 0, NULL
,
305 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
309 if (!scroll
) InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
310 descr
->top_item
= index
;
311 LISTBOX_UpdateScroll( wnd
, descr
);
316 /***********************************************************************
319 * Update the page size. Should be called when the size of
320 * the client area or the item height changes.
322 static void LISTBOX_UpdatePage( WND
*wnd
, LB_DESCR
*descr
)
326 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
328 if (page_size
== descr
->page_size
) return;
329 descr
->page_size
= page_size
;
330 if (descr
->style
& LBS_MULTICOLUMN
)
331 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
332 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
, FALSE
);
336 /***********************************************************************
339 * Update the size of the listbox. Should be called when the size of
340 * the client area changes.
342 static void LISTBOX_UpdateSize( WND
*wnd
, LB_DESCR
*descr
)
346 GetClientRect( wnd
->hwndSelf
, &rect
);
347 descr
->width
= rect
.right
- rect
.left
;
348 descr
->height
= rect
.bottom
- rect
.top
;
349 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
351 if ((descr
->height
> descr
->item_height
) &&
352 (descr
->height
% descr
->item_height
))
354 TRACE("[%04x]: changing height %d -> %d\n",
355 wnd
->hwndSelf
, descr
->height
,
356 descr
->height
- descr
->height
%descr
->item_height
);
357 SetWindowPos( wnd
->hwndSelf
, 0, 0, 0,
358 wnd
->rectWindow
.right
- wnd
->rectWindow
.left
,
359 wnd
->rectWindow
.bottom
- wnd
->rectWindow
.top
-
360 (descr
->height
% descr
->item_height
),
361 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
365 TRACE("[%04x]: new size = %d,%d\n",
366 wnd
->hwndSelf
, descr
->width
, descr
->height
);
367 LISTBOX_UpdatePage( wnd
, descr
);
368 LISTBOX_UpdateScroll( wnd
, descr
);
372 /***********************************************************************
373 * LISTBOX_GetItemRect
375 * Get the rectangle enclosing an item, in listbox client coordinates.
376 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
378 static LRESULT
LISTBOX_GetItemRect( WND
*wnd
, LB_DESCR
*descr
, INT index
,
381 /* Index <= 0 is legal even on empty listboxes */
382 if (index
&& (index
>= descr
->nb_items
)) return -1;
383 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
384 if (descr
->style
& LBS_MULTICOLUMN
)
386 INT col
= (index
/ descr
->page_size
) -
387 (descr
->top_item
/ descr
->page_size
);
388 rect
->left
+= col
* descr
->column_width
;
389 rect
->right
= rect
->left
+ descr
->column_width
;
390 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
391 rect
->bottom
= rect
->top
+ descr
->item_height
;
393 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
396 rect
->right
+= descr
->horz_pos
;
397 if ((index
>= 0) && (index
< descr
->nb_items
))
399 if (index
< descr
->top_item
)
401 for (i
= descr
->top_item
-1; i
>= index
; i
--)
402 rect
->top
-= descr
->items
[i
].height
;
406 for (i
= descr
->top_item
; i
< index
; i
++)
407 rect
->top
+= descr
->items
[i
].height
;
409 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
415 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
416 rect
->bottom
= rect
->top
+ descr
->item_height
;
417 rect
->right
+= descr
->horz_pos
;
420 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
421 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
425 /***********************************************************************
426 * LISTBOX_GetItemFromPoint
428 * Return the item nearest from point (x,y) (in client coordinates).
430 static INT
LISTBOX_GetItemFromPoint( WND
*wnd
, LB_DESCR
*descr
,
433 INT index
= descr
->top_item
;
435 if (!descr
->nb_items
) return -1; /* No items */
436 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
441 while (index
< descr
->nb_items
)
443 if ((pos
+= descr
->items
[index
].height
) > y
) break;
452 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
456 else if (descr
->style
& LBS_MULTICOLUMN
)
458 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
459 if (y
>= 0) index
+= y
/ descr
->item_height
;
460 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
461 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
465 index
+= (y
/ descr
->item_height
);
467 if (index
< 0) return 0;
468 if (index
>= descr
->nb_items
) return -1;
473 /***********************************************************************
478 static void LISTBOX_PaintItem( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
,
479 const RECT
*rect
, INT index
, UINT action
)
481 LB_ITEMDATA
*item
= NULL
;
482 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
484 if (IS_OWNERDRAW(descr
))
490 if (action
== ODA_FOCUS
)
491 DrawFocusRect( hdc
, rect
);
493 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
496 dis
.CtlType
= ODT_LISTBOX
;
497 dis
.CtlID
= wnd
->wIDmenu
;
498 dis
.hwndItem
= wnd
->hwndSelf
;
499 dis
.itemAction
= action
;
503 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
504 if ((descr
->focus_item
== index
) &&
506 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
507 if (wnd
->dwStyle
& WS_DISABLED
) dis
.itemState
|= ODS_DISABLED
;
508 dis
.itemData
= item
? item
->data
: 0;
510 TRACE("[%04x]: drawitem %d (%s) action=%02x "
511 "state=%02x rect=%d,%d-%d,%d\n",
512 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
513 dis
.itemState
, rect
->left
, rect
->top
,
514 rect
->right
, rect
->bottom
);
515 SendMessageA(descr
->owner
, WM_DRAWITEM
, wnd
->wIDmenu
, (LPARAM
)&dis
);
519 COLORREF oldText
= 0, oldBk
= 0;
521 if (action
== ODA_FOCUS
)
523 DrawFocusRect( hdc
, rect
);
526 if (item
&& item
->selected
)
528 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
529 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
532 TRACE("[%04x]: painting %d (%s) action=%02x "
533 "rect=%d,%d-%d,%d\n",
534 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
535 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
537 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
,
538 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
539 else if (!(descr
->style
& LBS_USETABSTOPS
))
540 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
,
541 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
542 strlen(item
->str
), NULL
);
545 /* Output empty string to paint background in the full width. */
546 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
,
547 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
548 TabbedTextOutA( hdc
, rect
->left
+ 1 , rect
->top
,
549 item
->str
, strlen(item
->str
),
550 descr
->nb_tabs
, descr
->tabs
, 0);
552 if (item
&& item
->selected
)
554 SetBkColor( hdc
, oldBk
);
555 SetTextColor( hdc
, oldText
);
557 if ((descr
->focus_item
== index
) &&
559 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
564 /***********************************************************************
567 * Change the redraw flag.
569 static void LISTBOX_SetRedraw( WND
*wnd
, LB_DESCR
*descr
, BOOL on
)
573 if (!(descr
->style
& LBS_NOREDRAW
)) return;
574 descr
->style
&= ~LBS_NOREDRAW
;
575 if (descr
->style
& LBS_DISPLAYCHANGED
)
576 { /* page was changed while setredraw false, refresh automatically */
577 InvalidateRect(wnd
->hwndSelf
, NULL
, TRUE
);
578 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
579 { /* reset top of page if less than number of items/page */
580 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
581 if (descr
->top_item
< 0) descr
->top_item
= 0;
583 descr
->style
&= ~LBS_DISPLAYCHANGED
;
585 LISTBOX_UpdateScroll( wnd
, descr
);
587 else descr
->style
|= LBS_NOREDRAW
;
591 /***********************************************************************
592 * LISTBOX_RepaintItem
594 * Repaint a single item synchronously.
596 static void LISTBOX_RepaintItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
602 HBRUSH hbrush
, oldBrush
= 0;
604 /* Do not repaint the item if the item is not visible */
605 if (!IsWindowVisible(wnd
->hwndSelf
)) return;
606 if (descr
->style
& LBS_NOREDRAW
)
608 descr
->style
|= LBS_DISPLAYCHANGED
;
611 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) != 1) return;
612 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
))) return;
613 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
614 hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
615 hdc
, (LPARAM
)wnd
->hwndSelf
);
616 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
617 if (wnd
->dwStyle
& WS_DISABLED
)
618 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
619 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
620 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, index
, action
);
621 if (oldFont
) SelectObject( hdc
, oldFont
);
622 if (oldBrush
) SelectObject( hdc
, oldBrush
);
623 ReleaseDC( wnd
->hwndSelf
, hdc
);
627 /***********************************************************************
628 * LISTBOX_InitStorage
630 static LRESULT
LISTBOX_InitStorage( WND
*wnd
, LB_DESCR
*descr
, INT nb_items
,
635 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
636 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
638 nb_items
+= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
639 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
640 nb_items
* sizeof(LB_ITEMDATA
) )))
642 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
650 /***********************************************************************
651 * LISTBOX_SetTabStops
653 static BOOL
LISTBOX_SetTabStops( WND
*wnd
, LB_DESCR
*descr
, INT count
,
654 LPINT tabs
, BOOL short_ints
)
656 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
657 if (descr
->tabs
) HeapFree( descr
->heap
, 0, descr
->tabs
);
658 if (!(descr
->nb_tabs
= count
))
663 /* FIXME: count = 1 */
664 if (!(descr
->tabs
= (INT
*)HeapAlloc( descr
->heap
, 0,
665 descr
->nb_tabs
* sizeof(INT
) )))
670 LPINT16 p
= (LPINT16
)tabs
;
672 TRACE("[%04x]: settabstops ", wnd
->hwndSelf
);
673 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
674 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
675 if (TRACE_ON(listbox
)) DPRINTF("%hd ", descr
->tabs
[i
]);
677 if (TRACE_ON(listbox
)) DPRINTF("\n");
679 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
680 /* FIXME: repaint the window? */
685 /***********************************************************************
688 static LRESULT
LISTBOX_GetText( WND
*wnd
, LB_DESCR
*descr
, INT index
,
691 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
692 if (HAS_STRINGS(descr
))
695 return strlen(descr
->items
[index
].str
);
696 lstrcpyA( buffer
, descr
->items
[index
].str
);
697 return strlen(buffer
);
700 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
701 return sizeof(DWORD
);
706 /***********************************************************************
707 * LISTBOX_FindStringPos
709 * Find the nearest string located before a given string in sort order.
710 * If 'exact' is TRUE, return an error if we don't get an exact match.
712 static INT
LISTBOX_FindStringPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
,
715 INT index
, min
, max
, res
= -1;
717 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
719 max
= descr
->nb_items
;
722 index
= (min
+ max
) / 2;
723 if (HAS_STRINGS(descr
))
724 res
= lstrcmpiA( descr
->items
[index
].str
, str
);
727 COMPAREITEMSTRUCT cis
;
729 cis
.CtlType
= ODT_LISTBOX
;
730 cis
.CtlID
= wnd
->wIDmenu
;
731 cis
.hwndItem
= wnd
->hwndSelf
;
733 cis
.itemData1
= descr
->items
[index
].data
;
735 cis
.itemData2
= (DWORD
)str
;
736 cis
.dwLocaleId
= descr
->locale
;
737 res
= SendMessageA( descr
->owner
, WM_COMPAREITEM
,
738 wnd
->wIDmenu
, (LPARAM
)&cis
);
740 if (!res
) return index
;
741 if (res
> 0) max
= index
;
742 else min
= index
+ 1;
744 return exact
? -1 : max
;
748 /***********************************************************************
749 * LISTBOX_FindFileStrPos
751 * Find the nearest string located before a given string in directory
752 * sort order (i.e. first files, then directories, then drives).
754 static INT
LISTBOX_FindFileStrPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
)
756 INT min
, max
, res
= -1;
758 if (!HAS_STRINGS(descr
))
759 return LISTBOX_FindStringPos( wnd
, descr
, str
, FALSE
);
761 max
= descr
->nb_items
;
764 INT index
= (min
+ max
) / 2;
765 const char *p
= descr
->items
[index
].str
;
766 if (*p
== '[') /* drive or directory */
768 if (*str
!= '[') res
= -1;
769 else if (p
[1] == '-') /* drive */
771 if (str
[1] == '-') res
= str
[2] - p
[2];
776 if (str
[1] == '-') res
= 1;
777 else res
= lstrcmpiA( str
, p
);
782 if (*str
== '[') res
= 1;
783 else res
= lstrcmpiA( str
, p
);
785 if (!res
) return index
;
786 if (res
< 0) max
= index
;
787 else min
= index
+ 1;
793 /***********************************************************************
796 * Find the item beginning with a given string.
798 static INT
LISTBOX_FindString( WND
*wnd
, LB_DESCR
*descr
, INT start
,
799 LPCSTR str
, BOOL exact
)
804 if (start
>= descr
->nb_items
) start
= -1;
805 item
= descr
->items
+ start
+ 1;
806 if (HAS_STRINGS(descr
))
808 if (!str
|| ! str
[0] ) return LB_ERR
;
811 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
812 if (!lstrcmpiA( str
, item
->str
)) return i
;
813 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
814 if (!lstrcmpiA( str
, item
->str
)) return i
;
818 /* Special case for drives and directories: ignore prefix */
819 #define CHECK_DRIVE(item) \
820 if ((item)->str[0] == '[') \
822 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
823 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
827 INT len
= strlen(str
);
828 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
830 if (!lstrncmpiA( str
, item
->str
, len
)) return i
;
833 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
835 if (!lstrncmpiA( str
, item
->str
, len
)) return i
;
843 if (exact
&& (descr
->style
& LBS_SORT
))
844 /* If sorted, use a WM_COMPAREITEM binary search */
845 return LISTBOX_FindStringPos( wnd
, descr
, str
, TRUE
);
847 /* Otherwise use a linear search */
848 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
849 if (item
->data
== (DWORD
)str
) return i
;
850 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
851 if (item
->data
== (DWORD
)str
) return i
;
857 /***********************************************************************
858 * LISTBOX_GetSelCount
860 static LRESULT
LISTBOX_GetSelCount( WND
*wnd
, LB_DESCR
*descr
)
863 LB_ITEMDATA
*item
= descr
->items
;
865 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
866 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
867 if (item
->selected
) count
++;
872 /***********************************************************************
873 * LISTBOX_GetSelItems16
875 static LRESULT
LISTBOX_GetSelItems16( WND
*wnd
, LB_DESCR
*descr
, INT16 max
,
879 LB_ITEMDATA
*item
= descr
->items
;
881 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
882 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
883 if (item
->selected
) array
[count
++] = (INT16
)i
;
888 /***********************************************************************
889 * LISTBOX_GetSelItems32
891 static LRESULT
LISTBOX_GetSelItems( WND
*wnd
, LB_DESCR
*descr
, INT max
,
895 LB_ITEMDATA
*item
= descr
->items
;
897 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
898 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
899 if (item
->selected
) array
[count
++] = i
;
904 /***********************************************************************
907 static LRESULT
LISTBOX_Paint( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
)
909 INT i
, col_pos
= descr
->page_size
- 1;
911 RECT focusRect
= {-1, -1, -1, -1};
913 HBRUSH hbrush
, oldBrush
= 0;
916 if (descr
->style
& LBS_NOREDRAW
) return 0;
918 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
919 if (descr
->style
& LBS_MULTICOLUMN
)
920 rect
.right
= rect
.left
+ descr
->column_width
;
921 else if (descr
->horz_pos
)
923 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
924 rect
.right
+= descr
->horz_pos
;
927 if (IS_OWNERDRAW(descr
))
931 GetClientRect(wnd
->hwndSelf
, &r
);
932 hrgn
= CreateRectRgnIndirect(&r
);
933 SelectClipRgn( hdc
, hrgn
);
934 DeleteObject( hrgn
);
937 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
938 hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
939 hdc
, (LPARAM
)wnd
->hwndSelf
);
940 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
941 if (wnd
->dwStyle
& WS_DISABLED
)
942 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
944 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
947 /* Special case for empty listbox: paint focus rect */
948 rect
.bottom
= rect
.top
+ descr
->item_height
;
949 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, descr
->focus_item
,
951 rect
.top
= rect
.bottom
;
954 /* Paint all the item, regarding the selection
955 Focus state will be painted after */
956 focusItem
= descr
->focus_item
;
957 descr
->focus_item
= -1;
959 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
961 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
962 rect
.bottom
= rect
.top
+ descr
->item_height
;
964 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
968 /* keep the focus rect, to paint the focus item after */
969 focusRect
.left
= rect
.left
;
970 focusRect
.right
= rect
.right
;
971 focusRect
.top
= rect
.top
;
972 focusRect
.bottom
= rect
.bottom
;
974 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
);
975 rect
.top
= rect
.bottom
;
977 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
979 if (!IS_OWNERDRAW(descr
))
981 /* Clear the bottom of the column */
982 SetBkColor( hdc
, GetSysColor( COLOR_WINDOW
) );
983 if (rect
.top
< descr
->height
)
985 rect
.bottom
= descr
->height
;
986 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
987 &rect
, NULL
, 0, NULL
);
991 /* Go to the next column */
992 rect
.left
+= descr
->column_width
;
993 rect
.right
+= descr
->column_width
;
995 col_pos
= descr
->page_size
- 1;
1000 if (rect
.top
>= descr
->height
) break;
1004 /* Paint the focus item now */
1005 descr
->focus_item
= focusItem
;
1006 if (focusRect
.top
!= focusRect
.bottom
&& descr
->caret_on
)
1007 LISTBOX_PaintItem( wnd
, descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
);
1009 if (!IS_OWNERDRAW(descr
))
1011 /* Clear the remainder of the client area */
1012 SetBkColor( hdc
, GetSysColor( COLOR_WINDOW
) );
1013 if (rect
.top
< descr
->height
)
1015 rect
.bottom
= descr
->height
;
1016 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1017 &rect
, NULL
, 0, NULL
);
1019 if (rect
.right
< descr
->width
)
1021 rect
.left
= rect
.right
;
1022 rect
.right
= descr
->width
;
1024 rect
.bottom
= descr
->height
;
1025 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1026 &rect
, NULL
, 0, NULL
);
1029 if (oldFont
) SelectObject( hdc
, oldFont
);
1030 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1035 /***********************************************************************
1036 * LISTBOX_InvalidateItems
1038 * Invalidate all items from a given item. If the specified item is not
1039 * visible, nothing happens.
1041 static void LISTBOX_InvalidateItems( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1045 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) == 1)
1047 if (descr
->style
& LBS_NOREDRAW
)
1049 descr
->style
|= LBS_DISPLAYCHANGED
;
1052 rect
.bottom
= descr
->height
;
1053 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
1054 if (descr
->style
& LBS_MULTICOLUMN
)
1056 /* Repaint the other columns */
1057 rect
.left
= rect
.right
;
1058 rect
.right
= descr
->width
;
1060 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
1066 /***********************************************************************
1067 * LISTBOX_GetItemHeight
1069 static LRESULT
LISTBOX_GetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1071 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1073 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1074 return descr
->items
[index
].height
;
1076 else return descr
->item_height
;
1080 /***********************************************************************
1081 * LISTBOX_SetItemHeight
1083 static LRESULT
LISTBOX_SetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1086 if (!height
) height
= 1;
1088 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1090 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1091 TRACE("[%04x]: item %d height = %d\n",
1092 wnd
->hwndSelf
, index
, height
);
1093 descr
->items
[index
].height
= height
;
1094 LISTBOX_UpdateScroll( wnd
, descr
);
1095 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1097 else if (height
!= descr
->item_height
)
1099 TRACE("[%04x]: new height = %d\n",
1100 wnd
->hwndSelf
, height
);
1101 descr
->item_height
= height
;
1102 LISTBOX_UpdatePage( wnd
, descr
);
1103 LISTBOX_UpdateScroll( wnd
, descr
);
1104 InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
1110 /***********************************************************************
1111 * LISTBOX_SetHorizontalPos
1113 static void LISTBOX_SetHorizontalPos( WND
*wnd
, LB_DESCR
*descr
, INT pos
)
1117 if (pos
> descr
->horz_extent
- descr
->width
)
1118 pos
= descr
->horz_extent
- descr
->width
;
1119 if (pos
< 0) pos
= 0;
1120 if (!(diff
= descr
->horz_pos
- pos
)) return;
1121 TRACE("[%04x]: new horz pos = %d\n",
1122 wnd
->hwndSelf
, pos
);
1123 descr
->horz_pos
= pos
;
1124 LISTBOX_UpdateScroll( wnd
, descr
);
1125 if (abs(diff
) < descr
->width
)
1126 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
1127 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1129 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1133 /***********************************************************************
1134 * LISTBOX_SetHorizontalExtent
1136 static LRESULT
LISTBOX_SetHorizontalExtent( WND
*wnd
, LB_DESCR
*descr
,
1139 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1141 if (extent
<= 0) extent
= 1;
1142 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1143 TRACE("[%04x]: new horz extent = %d\n",
1144 wnd
->hwndSelf
, extent
);
1145 descr
->horz_extent
= extent
;
1146 if (descr
->horz_pos
> extent
- descr
->width
)
1147 LISTBOX_SetHorizontalPos( wnd
, descr
, extent
- descr
->width
);
1149 LISTBOX_UpdateScroll( wnd
, descr
);
1154 /***********************************************************************
1155 * LISTBOX_SetColumnWidth
1157 static LRESULT
LISTBOX_SetColumnWidth( WND
*wnd
, LB_DESCR
*descr
, UINT width
)
1159 if (width
== descr
->column_width
) return LB_OKAY
;
1160 TRACE("[%04x]: new column width = %d\n",
1161 wnd
->hwndSelf
, width
);
1162 descr
->column_width
= width
;
1163 LISTBOX_UpdatePage( wnd
, descr
);
1168 /***********************************************************************
1171 * Returns the item height.
1173 static INT
LISTBOX_SetFont( WND
*wnd
, LB_DESCR
*descr
, HFONT font
)
1181 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
)))
1183 ERR("unable to get DC.\n" );
1186 if (font
) oldFont
= SelectObject( hdc
, font
);
1187 GetTextMetricsA( hdc
, &tm
);
1188 if (oldFont
) SelectObject( hdc
, oldFont
);
1189 ReleaseDC( wnd
->hwndSelf
, hdc
);
1190 if (!IS_OWNERDRAW(descr
))
1191 LISTBOX_SetItemHeight( wnd
, descr
, 0, tm
.tmHeight
);
1192 return tm
.tmHeight
;
1196 /***********************************************************************
1197 * LISTBOX_MakeItemVisible
1199 * Make sure that a given item is partially or fully visible.
1201 static void LISTBOX_MakeItemVisible( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1206 if (index
<= descr
->top_item
) top
= index
;
1207 else if (descr
->style
& LBS_MULTICOLUMN
)
1209 INT cols
= descr
->width
;
1210 if (!fully
) cols
+= descr
->column_width
- 1;
1211 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1213 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1214 top
= index
- descr
->page_size
* (cols
- 1);
1216 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1218 INT height
= fully
? descr
->items
[index
].height
: 1;
1219 for (top
= index
; top
> descr
->top_item
; top
--)
1220 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1224 if (index
< descr
->top_item
+ descr
->page_size
) return;
1225 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1226 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1227 top
= index
- descr
->page_size
+ 1;
1229 LISTBOX_SetTopItem( wnd
, descr
, top
, TRUE
);
1233 /***********************************************************************
1234 * LISTBOX_SelectItemRange
1236 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1238 static LRESULT
LISTBOX_SelectItemRange( WND
*wnd
, LB_DESCR
*descr
, INT first
,
1243 /* A few sanity checks */
1245 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1246 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1247 if (last
== -1) last
= descr
->nb_items
- 1;
1248 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1249 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1250 /* selected_item reflects last selected/unselected item on multiple sel */
1251 descr
->selected_item
= last
;
1253 if (on
) /* Turn selection on */
1255 for (i
= first
; i
<= last
; i
++)
1257 if (descr
->items
[i
].selected
) continue;
1258 descr
->items
[i
].selected
= TRUE
;
1259 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1262 else /* Turn selection off */
1264 for (i
= first
; i
<= last
; i
++)
1266 if (!descr
->items
[i
].selected
) continue;
1267 descr
->items
[i
].selected
= FALSE
;
1268 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1275 /***********************************************************************
1276 * LISTBOX_SetCaretIndex
1279 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1282 static LRESULT
LISTBOX_SetCaretIndex( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1283 BOOL fully_visible
)
1285 INT oldfocus
= descr
->focus_item
;
1287 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1288 if (index
== oldfocus
) return LB_OKAY
;
1289 descr
->focus_item
= index
;
1290 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1291 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1293 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1294 if (descr
->caret_on
&& (descr
->in_focus
))
1295 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1301 /***********************************************************************
1302 * LISTBOX_SetSelection
1304 static LRESULT
LISTBOX_SetSelection( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1305 BOOL on
, BOOL send_notify
)
1307 TRACE( "index=%d notify=%s\n", index
, send_notify
? "YES" : "NO" );
1309 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1310 if (descr
->style
& LBS_MULTIPLESEL
)
1312 if (index
== -1) /* Select all items */
1313 return LISTBOX_SelectItemRange( wnd
, descr
, 0, -1, on
);
1314 else /* Only one item */
1315 return LISTBOX_SelectItemRange( wnd
, descr
, index
, index
, on
);
1319 INT oldsel
= descr
->selected_item
;
1320 if (index
== oldsel
) return LB_OKAY
;
1321 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1322 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1323 descr
->selected_item
= index
;
1324 if (oldsel
!= -1) LISTBOX_RepaintItem( wnd
, descr
, oldsel
, ODA_SELECT
);
1325 if (index
!= -1) LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_SELECT
);
1326 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( wnd
, descr
,
1327 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1329 if( descr
->lphc
) /* set selection change flag for parent combo */
1330 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1336 /***********************************************************************
1339 * Change the caret position and extend the selection to the new caret.
1341 static void LISTBOX_MoveCaret( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1342 BOOL fully_visible
)
1344 INT oldfocus
= descr
->focus_item
;
1346 if ((index
< 0) || (index
>= descr
->nb_items
))
1349 /* Important, repaint needs to be done in this order if
1350 you want to mimic Windows behavior:
1351 1. Remove the focus and paint the item
1352 2. Remove the selection and paint the item(s)
1353 3. Set the selection and repaint the item(s)
1354 4. Set the focus to 'index' and repaint the item */
1356 /* 1. remove the focus and repaint the item */
1357 descr
->focus_item
= -1;
1358 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1359 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1361 /* 2. then turn off the previous selection */
1362 /* 3. repaint the new selected item */
1363 if (descr
->style
& LBS_EXTENDEDSEL
)
1365 if (descr
->anchor_item
!= -1)
1367 INT first
= min( index
, descr
->anchor_item
);
1368 INT last
= max( index
, descr
->anchor_item
);
1370 LISTBOX_SelectItemRange( wnd
, descr
, 0, first
- 1, FALSE
);
1371 LISTBOX_SelectItemRange( wnd
, descr
, last
+ 1, -1, FALSE
);
1372 LISTBOX_SelectItemRange( wnd
, descr
, first
, last
, TRUE
);
1375 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1377 /* Set selection to new caret item */
1378 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
1381 /* 4. repaint the new item with the focus */
1382 descr
->focus_item
= index
;
1383 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1384 if (descr
->caret_on
&& (descr
->in_focus
))
1385 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1389 /***********************************************************************
1390 * LISTBOX_InsertItem
1392 static LRESULT
LISTBOX_InsertItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1393 LPSTR str
, DWORD data
)
1397 INT oldfocus
= descr
->focus_item
;
1399 if (index
== -1) index
= descr
->nb_items
;
1400 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1401 if (!descr
->items
) max_items
= 0;
1402 else max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
1403 if (descr
->nb_items
== max_items
)
1405 /* We need to grow the array */
1406 max_items
+= LB_ARRAY_GRANULARITY
;
1407 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1408 max_items
* sizeof(LB_ITEMDATA
) )))
1410 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1413 descr
->items
= item
;
1416 /* Insert the item structure */
1418 item
= &descr
->items
[index
];
1419 if (index
< descr
->nb_items
)
1420 RtlMoveMemory( item
+ 1, item
,
1421 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1425 item
->selected
= FALSE
;
1428 /* Get item height */
1430 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1432 MEASUREITEMSTRUCT mis
;
1434 mis
.CtlType
= ODT_LISTBOX
;
1435 mis
.CtlID
= wnd
->wIDmenu
;
1437 mis
.itemData
= descr
->items
[index
].data
;
1438 mis
.itemHeight
= descr
->item_height
;
1439 SendMessageA( descr
->owner
, WM_MEASUREITEM
, wnd
->wIDmenu
, (LPARAM
)&mis
);
1440 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1441 TRACE("[%04x]: measure item %d (%s) = %d\n",
1442 wnd
->hwndSelf
, index
, str
? str
: "", item
->height
);
1445 /* Repaint the items */
1447 LISTBOX_UpdateScroll( wnd
, descr
);
1448 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1450 /* Move selection and focused item */
1451 /* If listbox was empty, set focus to the first item */
1452 if (descr
->nb_items
== 1)
1453 LISTBOX_SetCaretIndex( wnd
, descr
, 0, FALSE
);
1454 /* single select don't change selection index in win31 */
1455 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1457 descr
->selected_item
++;
1458 LISTBOX_SetSelection( wnd
, descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1462 if (index
<= descr
->selected_item
)
1464 descr
->selected_item
++;
1465 descr
->focus_item
= oldfocus
; /* focus not changed */
1472 /***********************************************************************
1473 * LISTBOX_InsertString
1475 static LRESULT
LISTBOX_InsertString( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1478 LPSTR new_str
= NULL
;
1482 if (HAS_STRINGS(descr
))
1485 if (!(new_str
= HEAP_strdupA( descr
->heap
, 0, str
)))
1487 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1491 else data
= (DWORD
)str
;
1493 if (index
== -1) index
= descr
->nb_items
;
1494 if ((ret
= LISTBOX_InsertItem( wnd
, descr
, index
, new_str
, data
)) != 0)
1496 if (new_str
) HeapFree( descr
->heap
, 0, new_str
);
1500 TRACE("[%04x]: added item %d '%s'\n",
1501 wnd
->hwndSelf
, index
, HAS_STRINGS(descr
) ? new_str
: "" );
1506 /***********************************************************************
1507 * LISTBOX_DeleteItem
1509 * Delete the content of an item. 'index' must be a valid index.
1511 static void LISTBOX_DeleteItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1513 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1514 * while Win95 sends it for all items with user data.
1515 * It's probably better to send it too often than not
1516 * often enough, so this is what we do here.
1518 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1520 DELETEITEMSTRUCT dis
;
1522 dis
.CtlType
= ODT_LISTBOX
;
1523 dis
.CtlID
= wnd
->wIDmenu
;
1525 dis
.hwndItem
= wnd
->hwndSelf
;
1526 dis
.itemData
= descr
->items
[index
].data
;
1527 SendMessageA( descr
->owner
, WM_DELETEITEM
, wnd
->wIDmenu
, (LPARAM
)&dis
);
1529 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1530 HeapFree( descr
->heap
, 0, descr
->items
[index
].str
);
1534 /***********************************************************************
1535 * LISTBOX_RemoveItem
1537 * Remove an item from the listbox and delete its content.
1539 static LRESULT
LISTBOX_RemoveItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1544 if (index
== -1) index
= descr
->nb_items
- 1;
1545 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1547 /* We need to invalidate the original rect instead of the updated one. */
1548 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1550 LISTBOX_DeleteItem( wnd
, descr
, index
);
1552 /* Remove the item */
1554 item
= &descr
->items
[index
];
1555 if (index
< descr
->nb_items
-1)
1556 RtlMoveMemory( item
, item
+ 1,
1557 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1559 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1561 /* Shrink the item array if possible */
1563 max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1564 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1566 max_items
-= LB_ARRAY_GRANULARITY
;
1567 item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1568 max_items
* sizeof(LB_ITEMDATA
) );
1569 if (item
) descr
->items
= item
;
1571 /* Repaint the items */
1573 LISTBOX_UpdateScroll( wnd
, descr
);
1574 /* if we removed the scrollbar, reset the top of the list
1575 (correct for owner-drawn ???) */
1576 if (descr
->nb_items
== descr
->page_size
)
1577 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1579 /* Move selection and focused item */
1580 if (!IS_MULTISELECT(descr
))
1582 if (index
== descr
->selected_item
)
1583 descr
->selected_item
= -1;
1584 else if (index
< descr
->selected_item
)
1586 descr
->selected_item
--;
1587 if (ISWIN31
) /* win 31 do not change the selected item number */
1588 LISTBOX_SetSelection( wnd
, descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1592 if (descr
->focus_item
>= descr
->nb_items
)
1594 descr
->focus_item
= descr
->nb_items
- 1;
1595 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1601 /***********************************************************************
1602 * LISTBOX_ResetContent
1604 static void LISTBOX_ResetContent( WND
*wnd
, LB_DESCR
*descr
)
1608 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( wnd
, descr
, i
);
1609 if (descr
->items
) HeapFree( descr
->heap
, 0, descr
->items
);
1610 descr
->nb_items
= 0;
1611 descr
->top_item
= 0;
1612 descr
->selected_item
= -1;
1613 descr
->focus_item
= 0;
1614 descr
->anchor_item
= -1;
1615 descr
->items
= NULL
;
1616 LISTBOX_UpdateScroll( wnd
, descr
);
1617 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1621 /***********************************************************************
1624 static LRESULT
LISTBOX_SetCount( WND
*wnd
, LB_DESCR
*descr
, INT count
)
1628 if (HAS_STRINGS(descr
)) return LB_ERR
;
1629 /* FIXME: this is far from optimal... */
1630 if (count
> descr
->nb_items
)
1632 while (count
> descr
->nb_items
)
1633 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, 0 )) < 0)
1636 else if (count
< descr
->nb_items
)
1638 while (count
< descr
->nb_items
)
1639 if ((ret
= LISTBOX_RemoveItem( wnd
, descr
, -1 )) < 0)
1646 /***********************************************************************
1649 static LRESULT
LISTBOX_Directory( WND
*wnd
, LB_DESCR
*descr
, UINT attrib
,
1650 LPCSTR filespec
, BOOL long_names
)
1653 LRESULT ret
= LB_OKAY
;
1654 WIN32_FIND_DATAA entry
;
1657 if ((handle
= FindFirstFileA(filespec
,&entry
)) == INVALID_HANDLE_VALUE
)
1659 if (GetLastError() != ERROR_NO_MORE_FILES
) return LB_ERR
;
1666 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1668 if (!(attrib
& DDL_DIRECTORY
) ||
1669 !strcmp( entry
.cAlternateFileName
, "." )) continue;
1670 if (long_names
) sprintf( buffer
, "[%s]", entry
.cFileName
);
1671 else sprintf( buffer
, "[%s]", entry
.cAlternateFileName
);
1673 else /* not a directory */
1675 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1676 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1678 if ((attrib
& DDL_EXCLUSIVE
) &&
1679 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1682 if (long_names
) strcpy( buffer
, entry
.cFileName
);
1683 else strcpy( buffer
, entry
.cAlternateFileName
);
1685 if (!long_names
) CharLowerA( buffer
);
1686 pos
= LISTBOX_FindFileStrPos( wnd
, descr
, buffer
);
1687 if ((ret
= LISTBOX_InsertString( wnd
, descr
, pos
, buffer
)) < 0)
1689 } while (FindNextFileA( handle
, &entry
));
1690 FindClose( handle
);
1693 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1695 char buffer
[] = "[-a-]";
1697 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++, buffer
[2]++)
1699 if (!DRIVE_IsValid(drive
)) continue;
1700 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, buffer
)) < 0)
1708 /***********************************************************************
1709 * LISTBOX_HandleVScroll
1711 static LRESULT
LISTBOX_HandleVScroll( WND
*wnd
, LB_DESCR
*descr
,
1712 WPARAM wParam
, LPARAM lParam
)
1716 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1717 switch(LOWORD(wParam
))
1720 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
- 1, TRUE
);
1723 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ 1, TRUE
);
1726 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-
1727 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1730 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+
1731 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1733 case SB_THUMBPOSITION
:
1734 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
), TRUE
);
1737 info
.cbSize
= sizeof(info
);
1738 info
.fMask
= SIF_TRACKPOS
;
1739 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1740 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
, TRUE
);
1743 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1746 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1753 /***********************************************************************
1754 * LISTBOX_HandleHScroll
1756 static LRESULT
LISTBOX_HandleHScroll( WND
*wnd
, LB_DESCR
*descr
,
1757 WPARAM wParam
, LPARAM lParam
)
1762 if (descr
->style
& LBS_MULTICOLUMN
)
1764 switch(LOWORD(wParam
))
1767 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-descr
->page_size
,
1771 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+descr
->page_size
,
1775 page
= descr
->width
/ descr
->column_width
;
1776 if (page
< 1) page
= 1;
1777 LISTBOX_SetTopItem( wnd
, descr
,
1778 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1781 page
= descr
->width
/ descr
->column_width
;
1782 if (page
< 1) page
= 1;
1783 LISTBOX_SetTopItem( wnd
, descr
,
1784 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1786 case SB_THUMBPOSITION
:
1787 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1791 info
.cbSize
= sizeof(info
);
1792 info
.fMask
= SIF_TRACKPOS
;
1793 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1794 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1798 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1801 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1805 else if (descr
->horz_extent
)
1807 switch(LOWORD(wParam
))
1810 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
- 1 );
1813 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
+ 1 );
1816 LISTBOX_SetHorizontalPos( wnd
, descr
,
1817 descr
->horz_pos
- descr
->width
);
1820 LISTBOX_SetHorizontalPos( wnd
, descr
,
1821 descr
->horz_pos
+ descr
->width
);
1823 case SB_THUMBPOSITION
:
1824 LISTBOX_SetHorizontalPos( wnd
, descr
, HIWORD(wParam
) );
1827 info
.cbSize
= sizeof(info
);
1828 info
.fMask
= SIF_TRACKPOS
;
1829 GetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
);
1830 LISTBOX_SetHorizontalPos( wnd
, descr
, info
.nTrackPos
);
1833 LISTBOX_SetHorizontalPos( wnd
, descr
, 0 );
1836 LISTBOX_SetHorizontalPos( wnd
, descr
,
1837 descr
->horz_extent
- descr
->width
);
1844 static LRESULT
LISTBOX_HandleMouseWheel(WND
*wnd
, LB_DESCR
*descr
,WPARAM wParam
, LPARAM lParam
)
1846 short gcWheelDelta
= 0;
1847 UINT pulScrollLines
= 3;
1849 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1851 gcWheelDelta
-= (short) HIWORD(wParam
);
1853 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
1855 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
1856 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
1857 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ cLineScroll
, TRUE
);
1862 /***********************************************************************
1863 * LISTBOX_HandleLButtonDown
1865 static LRESULT
LISTBOX_HandleLButtonDown( WND
*wnd
, LB_DESCR
*descr
,
1866 WPARAM wParam
, INT x
, INT y
)
1868 INT index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1869 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1870 wnd
->hwndSelf
, x
, y
, index
);
1871 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
1873 if (!descr
->in_focus
)
1875 if( !descr
->lphc
) SetFocus( wnd
->hwndSelf
);
1876 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
1877 : descr
->lphc
->self
->hwndSelf
);
1882 if (descr
->style
& LBS_EXTENDEDSEL
)
1884 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1885 if (wParam
& MK_CONTROL
)
1887 LISTBOX_SetCaretIndex( wnd
, descr
, index
, FALSE
);
1888 LISTBOX_SetSelection( wnd
, descr
, index
,
1889 !descr
->items
[index
].selected
,
1890 (descr
->style
& LBS_NOTIFY
) != 0);
1892 else LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1896 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1897 LISTBOX_SetSelection( wnd
, descr
, index
,
1898 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1899 !descr
->items
[index
].selected
),
1900 (descr
->style
& LBS_NOTIFY
) != 0 );
1904 descr
->captured
= TRUE
;
1905 SetCapture( wnd
->hwndSelf
);
1906 if (index
!= -1 && !descr
->lphc
)
1908 if (descr
->style
& LBS_NOTIFY
)
1909 SendMessageA( descr
->owner
, WM_LBTRACKPOINT
, index
,
1910 MAKELPARAM( x
, y
) );
1911 if (wnd
->dwExStyle
& WS_EX_DRAGDETECT
)
1918 if (DragDetect( wnd
->hwndSelf
, pt
))
1919 SendMessageA( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
1926 /*************************************************************************
1927 * LISTBOX_HandleLButtonDownCombo [Internal]
1929 * Process LButtonDown message for the ComboListBox
1932 * pWnd [I] The windows internal structure
1933 * pDescr [I] The ListBox internal structure
1934 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
1935 * x [I] X Mouse Coordinate
1936 * y [I] Y Mouse Coordinate
1939 * 0 since we are processing the WM_LBUTTONDOWN Message
1942 * This function is only to be used when a ListBox is a ComboListBox
1945 static LRESULT
LISTBOX_HandleLButtonDownCombo( WND
*pWnd
, LB_DESCR
*pDescr
,
1946 UINT msg
, WPARAM wParam
, INT x
, INT y
)
1948 RECT clientRect
, screenRect
;
1954 GetClientRect(pWnd
->hwndSelf
, &clientRect
);
1956 if(PtInRect(&clientRect
, mousePos
))
1958 /* MousePos is in client, resume normal processing */
1959 if (msg
== WM_LBUTTONDOWN
)
1961 pDescr
->lphc
->droppedIndex
= pDescr
->nb_items
? pDescr
->selected_item
: -1;
1962 return LISTBOX_HandleLButtonDown( pWnd
, pDescr
, wParam
, x
, y
);
1964 else if (pDescr
->style
& LBS_NOTIFY
)
1965 SEND_NOTIFICATION( pWnd
, pDescr
, LBN_DBLCLK
);
1970 POINT screenMousePos
;
1971 HWND hWndOldCapture
;
1973 /* Check the Non-Client Area */
1974 screenMousePos
= mousePos
;
1975 hWndOldCapture
= GetCapture();
1977 GetWindowRect(pWnd
->hwndSelf
, &screenRect
);
1978 ClientToScreen(pWnd
->hwndSelf
, &screenMousePos
);
1980 if(!PtInRect(&screenRect
, screenMousePos
))
1982 LISTBOX_SetSelection( pWnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
, FALSE
);
1983 COMBO_FlipListbox( pDescr
->lphc
, FALSE
, FALSE
);
1988 /* Check to see the NC is a scrollbar */
1990 /* Check Vertical scroll bar */
1991 if (pWnd
->dwStyle
& WS_VSCROLL
)
1993 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
1994 if (PtInRect( &clientRect
, mousePos
))
1996 nHitTestType
= HTVSCROLL
;
1999 /* Check horizontal scroll bar */
2000 if (pWnd
->dwStyle
& WS_HSCROLL
)
2002 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2003 if (PtInRect( &clientRect
, mousePos
))
2005 nHitTestType
= HTHSCROLL
;
2008 /* Windows sends this message when a scrollbar is clicked
2011 if(nHitTestType
!= 0)
2013 SendMessageA(pWnd
->hwndSelf
, WM_NCLBUTTONDOWN
, nHitTestType
,
2014 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2016 /* Resume the Capture after scrolling is complete
2018 if(hWndOldCapture
!= 0)
2020 SetCapture(hWndOldCapture
);
2027 /***********************************************************************
2028 * LISTBOX_HandleLButtonUp
2030 static LRESULT
LISTBOX_HandleLButtonUp( WND
*wnd
, LB_DESCR
*descr
)
2032 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2033 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2034 LISTBOX_Timer
= LB_TIMER_NONE
;
2035 if (descr
->captured
)
2037 descr
->captured
= FALSE
;
2038 if (GetCapture() == wnd
->hwndSelf
) ReleaseCapture();
2039 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2040 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2046 /***********************************************************************
2047 * LISTBOX_HandleTimer
2049 * Handle scrolling upon a timer event.
2050 * Return TRUE if scrolling should continue.
2052 static LRESULT
LISTBOX_HandleTimer( WND
*wnd
, LB_DESCR
*descr
,
2053 INT index
, TIMER_DIRECTION dir
)
2058 if (descr
->top_item
) index
= descr
->top_item
- 1;
2062 if (descr
->top_item
) index
-= descr
->page_size
;
2065 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( wnd
, descr
);
2066 if (index
== descr
->focus_item
) index
++;
2067 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2069 case LB_TIMER_RIGHT
:
2070 if (index
+ descr
->page_size
< descr
->nb_items
)
2071 index
+= descr
->page_size
;
2076 if (index
== descr
->focus_item
) return FALSE
;
2077 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
2082 /***********************************************************************
2083 * LISTBOX_HandleSystemTimer
2085 * WM_SYSTIMER handler.
2087 static LRESULT
LISTBOX_HandleSystemTimer( WND
*wnd
, LB_DESCR
*descr
)
2089 if (!LISTBOX_HandleTimer( wnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
2091 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2092 LISTBOX_Timer
= LB_TIMER_NONE
;
2098 /***********************************************************************
2099 * LISTBOX_HandleMouseMove
2101 * WM_MOUSEMOVE handler.
2103 static void LISTBOX_HandleMouseMove( WND
*wnd
, LB_DESCR
*descr
,
2107 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2109 if (!descr
->captured
) return;
2111 if (descr
->style
& LBS_MULTICOLUMN
)
2114 else if (y
>= descr
->item_height
* descr
->page_size
)
2115 y
= descr
->item_height
* descr
->page_size
- 1;
2119 dir
= LB_TIMER_LEFT
;
2122 else if (x
>= descr
->width
)
2124 dir
= LB_TIMER_RIGHT
;
2125 x
= descr
->width
- 1;
2130 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2131 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2134 index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
2135 if (index
== -1) index
= descr
->focus_item
;
2136 if (!LISTBOX_HandleTimer( wnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2138 /* Start/stop the system timer */
2140 if (dir
!= LB_TIMER_NONE
)
2141 SetSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2142 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2143 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2144 LISTBOX_Timer
= dir
;
2148 /***********************************************************************
2149 * LISTBOX_HandleKeyDown
2151 static LRESULT
LISTBOX_HandleKeyDown( WND
*wnd
, LB_DESCR
*descr
, WPARAM wParam
)
2154 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2155 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2156 bForceSelection
= FALSE
; /* only for single select list */
2158 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2160 caret
= SendMessageA( descr
->owner
, WM_VKEYTOITEM
,
2161 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2163 if (caret
== -2) return 0;
2165 if (caret
== -1) switch(wParam
)
2168 if (descr
->style
& LBS_MULTICOLUMN
)
2170 bForceSelection
= FALSE
;
2171 if (descr
->focus_item
>= descr
->page_size
)
2172 caret
= descr
->focus_item
- descr
->page_size
;
2177 caret
= descr
->focus_item
- 1;
2178 if (caret
< 0) caret
= 0;
2181 if (descr
->style
& LBS_MULTICOLUMN
)
2183 bForceSelection
= FALSE
;
2184 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2185 caret
= descr
->focus_item
+ descr
->page_size
;
2190 caret
= descr
->focus_item
+ 1;
2191 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2195 if (descr
->style
& LBS_MULTICOLUMN
)
2197 INT page
= descr
->width
/ descr
->column_width
;
2198 if (page
< 1) page
= 1;
2199 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2201 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(wnd
,descr
)+1;
2202 if (caret
< 0) caret
= 0;
2205 if (descr
->style
& LBS_MULTICOLUMN
)
2207 INT page
= descr
->width
/ descr
->column_width
;
2208 if (page
< 1) page
= 1;
2209 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2211 else caret
= descr
->focus_item
+LISTBOX_GetCurrentPageSize(wnd
,descr
)-1;
2212 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2218 caret
= descr
->nb_items
- 1;
2221 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2222 else if (descr
->style
& LBS_MULTIPLESEL
)
2224 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
,
2225 !descr
->items
[descr
->focus_item
].selected
,
2226 (descr
->style
& LBS_NOTIFY
) != 0 );
2230 bForceSelection
= FALSE
;
2232 if (bForceSelection
) /* focused item is used instead of key */
2233 caret
= descr
->focus_item
;
2236 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
2237 !(GetKeyState( VK_SHIFT
) & 0x8000))
2238 descr
->anchor_item
= caret
;
2239 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2240 LISTBOX_SetSelection( wnd
, descr
, caret
, TRUE
, FALSE
);
2241 if (descr
->style
& LBS_NOTIFY
)
2243 if( descr
->lphc
&& CB_GETTYPE(descr
->lphc
) != CBS_SIMPLE
)
2245 /* make sure that combo parent doesn't hide us */
2246 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2248 if (descr
->nb_items
) SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2255 /***********************************************************************
2256 * LISTBOX_HandleChar
2258 static LRESULT
LISTBOX_HandleChar( WND
*wnd
, LB_DESCR
*descr
,
2264 str
[0] = wParam
& 0xff;
2267 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2269 caret
= SendMessageA( descr
->owner
, WM_CHARTOITEM
,
2270 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2272 if (caret
== -2) return 0;
2275 caret
= LISTBOX_FindString( wnd
, descr
, descr
->focus_item
, str
, FALSE
);
2278 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2279 LISTBOX_SetSelection( wnd
, descr
, caret
, TRUE
, FALSE
);
2280 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2281 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2282 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2288 /***********************************************************************
2291 static BOOL
LISTBOX_Create( WND
*wnd
, LPHEADCOMBO lphc
)
2294 MEASUREITEMSTRUCT mis
;
2297 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2299 if (!(descr
->heap
= HeapCreate( 0, 0x10000, 0 )))
2301 HeapFree( GetProcessHeap(), 0, descr
);
2304 GetClientRect( wnd
->hwndSelf
, &rect
);
2305 descr
->owner
= GetParent( wnd
->hwndSelf
);
2306 descr
->style
= wnd
->dwStyle
;
2307 descr
->width
= rect
.right
- rect
.left
;
2308 descr
->height
= rect
.bottom
- rect
.top
;
2309 descr
->items
= NULL
;
2310 descr
->nb_items
= 0;
2311 descr
->top_item
= 0;
2312 descr
->selected_item
= -1;
2313 descr
->focus_item
= 0;
2314 descr
->anchor_item
= -1;
2315 descr
->item_height
= 1;
2316 descr
->page_size
= 1;
2317 descr
->column_width
= 150;
2318 descr
->horz_extent
= (wnd
->dwStyle
& WS_HSCROLL
) ? 1 : 0;
2319 descr
->horz_pos
= 0;
2322 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2323 descr
->in_focus
= FALSE
;
2324 descr
->captured
= FALSE
;
2326 descr
->locale
= 0; /* FIXME */
2329 if( ( GetExpWinVer16( wnd
->hInstance
) & 0xFF00 ) == 0x0300
2330 && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2332 /* Win95 document "List Box Differences" from MSDN:
2333 If a list box in a version 3.x application has either the
2334 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2335 horizontal and vertical scroll bars.
2337 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2342 TRACE_(combo
)("[%04x]: resetting owner %04x -> %04x\n",
2343 wnd
->hwndSelf
, descr
->owner
, lphc
->self
->hwndSelf
);
2344 descr
->owner
= lphc
->self
->hwndSelf
;
2347 *(LB_DESCR
**)wnd
->wExtra
= descr
;
2349 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2351 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2352 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2353 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2354 descr
->item_height
= LISTBOX_SetFont( wnd
, descr
, 0 );
2356 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2358 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2360 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2361 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2365 mis
.CtlType
= ODT_LISTBOX
;
2366 mis
.CtlID
= wnd
->wIDmenu
;
2370 mis
.itemHeight
= descr
->item_height
;
2371 SendMessageA( descr
->owner
, WM_MEASUREITEM
, wnd
->wIDmenu
, (LPARAM
)&mis
);
2372 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2380 /***********************************************************************
2383 static BOOL
LISTBOX_Destroy( WND
*wnd
, LB_DESCR
*descr
)
2385 LISTBOX_ResetContent( wnd
, descr
);
2386 HeapDestroy( descr
->heap
);
2387 HeapFree( GetProcessHeap(), 0, descr
);
2393 /***********************************************************************
2396 static inline LRESULT WINAPI
ListBoxWndProc_locked( WND
* wnd
, UINT msg
,
2397 WPARAM wParam
, LPARAM lParam
)
2401 HWND hwnd
= wnd
->hwndSelf
;
2404 if (!(descr
= *(LB_DESCR
**)wnd
->wExtra
))
2410 if (!LISTBOX_Create( wnd
, NULL
))
2412 TRACE("creating wnd=%04x descr=%p\n",
2413 hwnd
, *(LB_DESCR
**)wnd
->wExtra
);
2419 * When a listbox is not in a combobox and the look
2420 * is win95, the WS_BORDER style is replaced with
2421 * the WS_EX_CLIENTEDGE style.
2423 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
2424 (wnd
->dwStyle
& WS_BORDER
) )
2426 wnd
->dwExStyle
|= WS_EX_CLIENTEDGE
;
2427 wnd
->dwStyle
&= ~ WS_BORDER
;
2432 /* Ignore all other messages before we get a WM_CREATE */
2433 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2436 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2437 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2440 case LB_RESETCONTENT16
:
2441 case LB_RESETCONTENT
:
2442 LISTBOX_ResetContent( wnd
, descr
);
2445 case LB_ADDSTRING16
:
2446 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2449 wParam
= LISTBOX_FindStringPos( wnd
, descr
, (LPCSTR
)lParam
, FALSE
);
2450 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2452 case LB_INSERTSTRING16
:
2453 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2454 wParam
= (INT
)(INT16
)wParam
;
2456 case LB_INSERTSTRING
:
2457 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2460 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2463 wParam
= LISTBOX_FindFileStrPos( wnd
, descr
, (LPCSTR
)lParam
);
2464 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2466 case LB_DELETESTRING16
:
2467 case LB_DELETESTRING
:
2468 if (LISTBOX_RemoveItem( wnd
, descr
, wParam
) != LB_ERR
)
2469 return descr
->nb_items
;
2473 case LB_GETITEMDATA16
:
2474 case LB_GETITEMDATA
:
2475 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2477 return descr
->items
[wParam
].data
;
2479 case LB_SETITEMDATA16
:
2480 case LB_SETITEMDATA
:
2481 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2483 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2488 return descr
->nb_items
;
2491 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2494 return LISTBOX_GetText( wnd
, descr
, wParam
, (LPSTR
)lParam
);
2496 case LB_GETTEXTLEN16
:
2499 if (wParam
>= descr
->nb_items
)
2501 return (HAS_STRINGS(descr
) ? strlen(descr
->items
[wParam
].str
)
2504 case LB_GETCURSEL16
:
2506 if (descr
->nb_items
==0)
2508 if (!IS_MULTISELECT(descr
))
2509 return descr
->selected_item
;
2511 if (descr
->selected_item
!=-1)
2512 return descr
->selected_item
;
2514 return descr
->focus_item
;
2515 /* otherwise, if the user tries to move the selection with the */
2516 /* arrow keys, we will give the application something to choke on */
2517 case LB_GETTOPINDEX16
:
2518 case LB_GETTOPINDEX
:
2519 return descr
->top_item
;
2521 case LB_GETITEMHEIGHT16
:
2522 case LB_GETITEMHEIGHT
:
2523 return LISTBOX_GetItemHeight( wnd
, descr
, wParam
);
2525 case LB_SETITEMHEIGHT16
:
2526 lParam
= LOWORD(lParam
);
2528 case LB_SETITEMHEIGHT
:
2529 return LISTBOX_SetItemHeight( wnd
, descr
, wParam
, lParam
);
2531 case LB_ITEMFROMPOINT
:
2536 pt
.x
= LOWORD(lParam
);
2537 pt
.y
= HIWORD(lParam
);
2540 rect
.right
= descr
->width
;
2541 rect
.bottom
= descr
->height
;
2543 return MAKELONG( LISTBOX_GetItemFromPoint(wnd
, descr
, pt
.x
, pt
.y
),
2544 !PtInRect( &rect
, pt
) );
2547 case LB_SETCARETINDEX16
:
2548 case LB_SETCARETINDEX
:
2549 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2550 if (LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, !lParam
) == LB_ERR
)
2557 case LB_GETCARETINDEX16
:
2558 case LB_GETCARETINDEX
:
2559 return descr
->focus_item
;
2561 case LB_SETTOPINDEX16
:
2562 case LB_SETTOPINDEX
:
2563 return LISTBOX_SetTopItem( wnd
, descr
, wParam
, TRUE
);
2565 case LB_SETCOLUMNWIDTH16
:
2566 case LB_SETCOLUMNWIDTH
:
2567 return LISTBOX_SetColumnWidth( wnd
, descr
, wParam
);
2569 case LB_GETITEMRECT16
:
2572 ret
= LISTBOX_GetItemRect( wnd
, descr
, (INT16
)wParam
, &rect
);
2573 CONV_RECT32TO16( &rect
, (RECT16
*)PTR_SEG_TO_LIN(lParam
) );
2577 case LB_GETITEMRECT
:
2578 return LISTBOX_GetItemRect( wnd
, descr
, wParam
, (RECT
*)lParam
);
2580 case LB_FINDSTRING16
:
2581 wParam
= (INT
)(INT16
)wParam
;
2582 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2585 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, FALSE
);
2587 case LB_FINDSTRINGEXACT16
:
2588 wParam
= (INT
)(INT16
)wParam
;
2589 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2591 case LB_FINDSTRINGEXACT
:
2592 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2594 case LB_SELECTSTRING16
:
2595 wParam
= (INT
)(INT16
)wParam
;
2596 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2598 case LB_SELECTSTRING
:
2600 INT index
= LISTBOX_FindString( wnd
, descr
, wParam
,
2601 (LPCSTR
)lParam
, FALSE
);
2602 if (index
== LB_ERR
)
2604 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
2609 wParam
= (INT
)(INT16
)wParam
;
2612 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2614 return descr
->items
[wParam
].selected
;
2617 lParam
= (INT
)(INT16
)lParam
;
2620 return LISTBOX_SetSelection( wnd
, descr
, lParam
, wParam
, FALSE
);
2622 case LB_SETCURSEL16
:
2623 wParam
= (INT
)(INT16
)wParam
;
2626 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2627 LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, TRUE
);
2628 return LISTBOX_SetSelection( wnd
, descr
, wParam
, TRUE
, FALSE
);
2630 case LB_GETSELCOUNT16
:
2631 case LB_GETSELCOUNT
:
2632 return LISTBOX_GetSelCount( wnd
, descr
);
2634 case LB_GETSELITEMS16
:
2635 return LISTBOX_GetSelItems16( wnd
, descr
, wParam
,
2636 (LPINT16
)PTR_SEG_TO_LIN(lParam
) );
2638 case LB_GETSELITEMS
:
2639 return LISTBOX_GetSelItems( wnd
, descr
, wParam
, (LPINT
)lParam
);
2641 case LB_SELITEMRANGE16
:
2642 case LB_SELITEMRANGE
:
2643 if (LOWORD(lParam
) <= HIWORD(lParam
))
2644 return LISTBOX_SelectItemRange( wnd
, descr
, LOWORD(lParam
),
2645 HIWORD(lParam
), wParam
);
2647 return LISTBOX_SelectItemRange( wnd
, descr
, HIWORD(lParam
),
2648 LOWORD(lParam
), wParam
);
2650 case LB_SELITEMRANGEEX16
:
2651 case LB_SELITEMRANGEEX
:
2652 if ((INT
)lParam
>= (INT
)wParam
)
2653 return LISTBOX_SelectItemRange( wnd
, descr
, wParam
, lParam
, TRUE
);
2655 return LISTBOX_SelectItemRange( wnd
, descr
, lParam
, wParam
, FALSE
);
2657 case LB_GETHORIZONTALEXTENT16
:
2658 case LB_GETHORIZONTALEXTENT
:
2659 return descr
->horz_extent
;
2661 case LB_SETHORIZONTALEXTENT16
:
2662 case LB_SETHORIZONTALEXTENT
:
2663 return LISTBOX_SetHorizontalExtent( wnd
, descr
, wParam
);
2665 case LB_GETANCHORINDEX16
:
2666 case LB_GETANCHORINDEX
:
2667 return descr
->anchor_item
;
2669 case LB_SETANCHORINDEX16
:
2670 wParam
= (INT
)(INT16
)wParam
;
2672 case LB_SETANCHORINDEX
:
2673 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2675 descr
->anchor_item
= (INT
)wParam
;
2679 return LISTBOX_Directory( wnd
, descr
, wParam
,
2680 (LPCSTR
)PTR_SEG_TO_LIN(lParam
), FALSE
);
2683 return LISTBOX_Directory( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2686 return descr
->locale
;
2689 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2692 case LB_INITSTORAGE
:
2693 return LISTBOX_InitStorage( wnd
, descr
, wParam
, (DWORD
)lParam
);
2696 return LISTBOX_SetCount( wnd
, descr
, (INT
)wParam
);
2698 case LB_SETTABSTOPS16
:
2699 return LISTBOX_SetTabStops( wnd
, descr
, (INT
)(INT16
)wParam
,
2700 (LPINT
)PTR_SEG_TO_LIN(lParam
), TRUE
);
2702 case LB_SETTABSTOPS
:
2703 return LISTBOX_SetTabStops( wnd
, descr
, wParam
, (LPINT
)lParam
, FALSE
);
2707 if (descr
->caret_on
)
2709 descr
->caret_on
= TRUE
;
2710 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2711 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2716 if (!descr
->caret_on
)
2718 descr
->caret_on
= FALSE
;
2719 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2720 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2724 return LISTBOX_Destroy( wnd
, descr
);
2727 InvalidateRect( hwnd
, NULL
, TRUE
);
2731 LISTBOX_SetRedraw( wnd
, descr
, wParam
!= 0 );
2735 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2740 HDC hdc
= ( wParam
) ? ((HDC
)wParam
)
2741 : BeginPaint( hwnd
, &ps
);
2742 ret
= LISTBOX_Paint( wnd
, descr
, hdc
);
2743 if( !wParam
) EndPaint( hwnd
, &ps
);
2747 LISTBOX_UpdateSize( wnd
, descr
);
2752 LISTBOX_SetFont( wnd
, descr
, (HFONT
)wParam
);
2753 if (lParam
) InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
2756 descr
->in_focus
= TRUE
;
2757 descr
->caret_on
= TRUE
;
2758 if (descr
->focus_item
!= -1)
2759 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2760 SEND_NOTIFICATION( wnd
, descr
, LBN_SETFOCUS
);
2763 descr
->in_focus
= FALSE
;
2764 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2765 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2766 SEND_NOTIFICATION( wnd
, descr
, LBN_KILLFOCUS
);
2769 return LISTBOX_HandleHScroll( wnd
, descr
, wParam
, lParam
);
2771 return LISTBOX_HandleVScroll( wnd
, descr
, wParam
, lParam
);
2772 case WM_MOUSEACTIVATE
:
2773 return MA_NOACTIVATE
;
2775 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2776 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2777 return LISTBOX_HandleMouseWheel( wnd
, descr
, wParam
, lParam
);
2778 case WM_LBUTTONDOWN
:
2779 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2780 (INT16
)LOWORD(lParam
),
2781 (INT16
)HIWORD(lParam
) );
2782 case WM_LBUTTONDBLCLK
:
2783 if (descr
->style
& LBS_NOTIFY
)
2784 SEND_NOTIFICATION( wnd
, descr
, LBN_DBLCLK
);
2787 if (GetCapture() == hwnd
)
2788 LISTBOX_HandleMouseMove( wnd
, descr
, (INT16
)LOWORD(lParam
),
2789 (INT16
)HIWORD(lParam
) );
2792 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2794 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2796 return LISTBOX_HandleChar( wnd
, descr
, wParam
);
2798 return LISTBOX_HandleSystemTimer( wnd
, descr
);
2800 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
2803 HBRUSH hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
2804 wParam
, (LPARAM
)wnd
->hwndSelf
);
2805 GetClientRect(hwnd
, &rect
);
2806 if (hbrush
) FillRect( (HDC
)wParam
, &rect
, hbrush
);
2811 return SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2815 case WM_QUERYDROPOBJECT
:
2820 LPDRAGINFO dragInfo
= (LPDRAGINFO
)PTR_SEG_TO_LIN( (SEGPTR
)lParam
);
2821 dragInfo
->l
= LISTBOX_GetItemFromPoint( wnd
, descr
, dragInfo
->pt
.x
,
2823 return SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2828 if ((msg
>= WM_USER
) && (msg
< 0xc000))
2829 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2830 hwnd
, msg
, wParam
, lParam
);
2831 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2836 /***********************************************************************
2839 * This is just a wrapper for the real wndproc, it only does window locking
2842 LRESULT WINAPI
ListBoxWndProc( HWND hwnd
, UINT msg
,
2843 WPARAM wParam
, LPARAM lParam
)
2845 WND
* wndPtr
= WIN_FindWndPtr( hwnd
);
2846 LRESULT res
= ListBoxWndProc_locked(wndPtr
,msg
,wParam
,lParam
);
2848 WIN_ReleaseWndPtr(wndPtr
);
2852 /***********************************************************************
2855 LRESULT
COMBO_Directory( LPHEADCOMBO lphc
, UINT attrib
, LPSTR dir
, BOOL bLong
)
2857 WND
*wnd
= WIN_FindWndPtr( lphc
->hWndLBox
);
2861 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2864 LRESULT lRet
= LISTBOX_Directory( wnd
, descr
, attrib
, dir
, bLong
);
2866 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0,
2867 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
2868 WIN_ReleaseWndPtr(wnd
);
2871 WIN_ReleaseWndPtr(wnd
);
2876 /***********************************************************************
2877 * ComboLBWndProc_locked
2879 * The real combo listbox wndproc, but called with locked WND struct.
2881 static inline LRESULT WINAPI
ComboLBWndProc_locked( WND
* wnd
, UINT msg
,
2882 WPARAM wParam
, LPARAM lParam
)
2885 HWND hwnd
= wnd
->hwndSelf
;
2889 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2891 TRACE_(combo
)("[%04x]: msg %s wp %08x lp %08lx\n",
2892 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2894 if( descr
|| msg
== WM_CREATE
)
2896 LPHEADCOMBO lphc
= (descr
) ? descr
->lphc
: NULL
;
2901 #define lpcs ((LPCREATESTRUCTA)lParam)
2902 TRACE_(combo
)("\tpassed parent handle = 0x%08x\n",
2903 (UINT
)lpcs
->lpCreateParams
);
2905 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
2907 return LISTBOX_Create( wnd
, lphc
);
2909 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
2910 (CB_GETTYPE(lphc
) != CBS_SIMPLE
) )
2916 mousePos
.x
= (INT16
)LOWORD(lParam
);
2917 mousePos
.y
= (INT16
)HIWORD(lParam
);
2920 * If we are in a dropdown combobox, we simulate that
2921 * the mouse is captured to show the tracking of the item.
2923 GetClientRect(hwnd
, &clientRect
);
2925 if (PtInRect( &clientRect
, mousePos
))
2927 captured
= descr
->captured
;
2928 descr
->captured
= TRUE
;
2930 LISTBOX_HandleMouseMove( wnd
, descr
,
2931 mousePos
.x
, mousePos
.y
);
2933 descr
->captured
= captured
;
2938 LISTBOX_HandleMouseMove( wnd
, descr
,
2939 mousePos
.x
, mousePos
.y
);
2948 * If we are in Win3.1 look, go with the default behavior.
2950 return ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2953 if (TWEAK_WineLook
> WIN31_LOOK
)
2959 * If the mouse button "up" is not in the listbox,
2960 * we make sure there is no selection by re-selecting the
2961 * item that was selected when the listbox was made visible.
2963 mousePos
.x
= (INT16
)LOWORD(lParam
);
2964 mousePos
.y
= (INT16
)HIWORD(lParam
);
2966 GetClientRect(hwnd
, &clientRect
);
2969 * When the user clicks outside the combobox and the focus
2970 * is lost, the owning combobox will send a fake buttonup with
2971 * 0xFFFFFFF as the mouse location, we must also revert the
2972 * selection to the original selection.
2974 if ( (lParam
== 0xFFFFFFFF) ||
2975 (!PtInRect( &clientRect
, mousePos
)) )
2977 LISTBOX_MoveCaret( wnd
,
2983 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2984 case WM_LBUTTONDBLCLK
:
2985 case WM_LBUTTONDOWN
:
2986 return LISTBOX_HandleLButtonDownCombo(wnd
, descr
, msg
, wParam
,
2987 (INT16
)LOWORD(lParam
),
2988 (INT16
)HIWORD(lParam
) );
2989 case WM_MOUSEACTIVATE
:
2990 return MA_NOACTIVATE
;
2994 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2996 /* for some reason(?) Windows makes it possible to
2997 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2999 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3000 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3001 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3003 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3007 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
3009 case LB_SETCURSEL16
:
3011 lRet
= ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
3012 lRet
=(lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
3015 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3020 return ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
3023 lRet
= DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3025 TRACE_(combo
)("\t default on msg [%04x]\n", (UINT16
)msg
);
3030 /***********************************************************************
3033 * NOTE: in Windows, winproc address of the ComboLBox is the same
3034 * as that of the Listbox.
3036 * This is just a wrapper for the real wndproc, it only does window locking
3039 LRESULT WINAPI
ComboLBWndProc( HWND hwnd
, UINT msg
,
3040 WPARAM wParam
, LPARAM lParam
)
3042 WND
*wnd
= WIN_FindWndPtr( hwnd
);
3043 LRESULT res
= ComboLBWndProc_locked(wnd
,msg
,wParam
,lParam
);
3045 WIN_ReleaseWndPtr(wnd
);