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 */
47 LPSTR str
; /* Item text */
48 BOOL selected
; /* Is item selected? */
49 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
50 DWORD data
; /* User data */
53 /* Listbox structure */
56 HANDLE heap
; /* Heap for this listbox */
57 HWND owner
; /* Owner window to send notifications to */
58 UINT style
; /* Window style */
59 INT width
; /* Window width */
60 INT height
; /* Window height */
61 LB_ITEMDATA
*items
; /* Array of items */
62 INT nb_items
; /* Number of items */
63 INT top_item
; /* Top visible item */
64 INT selected_item
; /* Selected item */
65 INT focus_item
; /* Item that has the focus */
66 INT anchor_item
; /* Anchor item for extended selection */
67 INT item_height
; /* Default item height */
68 INT page_size
; /* Items per listbox page */
69 INT column_width
; /* Column width for multi-column listboxes */
70 INT horz_extent
; /* Horizontal extent (0 if no hscroll) */
71 INT horz_pos
; /* Horizontal position */
72 INT nb_tabs
; /* Number of tabs in array */
73 INT
*tabs
; /* Array of tabs */
74 BOOL caret_on
; /* Is caret on? */
75 BOOL captured
; /* Is mouse captured? */
76 HFONT font
; /* Current font */
77 LCID locale
; /* Current locale for string comparisons */
78 LPHEADCOMBO lphc
; /* ComboLBox */
82 #define IS_OWNERDRAW(descr) \
83 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
85 #define HAS_STRINGS(descr) \
86 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
89 #define IS_MULTISELECT(descr) \
90 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
92 #define SEND_NOTIFICATION(wnd,descr,code) \
93 (SendMessageA( (descr)->owner, WM_COMMAND, \
94 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf ))
96 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
98 /* Current timer status */
108 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
111 /***********************************************************************
114 void LISTBOX_Dump( WND
*wnd
)
118 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
120 TRACE( "Listbox:\n" );
121 TRACE( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
122 wnd
->hwndSelf
, (UINT
)descr
, descr
->heap
, descr
->nb_items
,
124 for (i
= 0, item
= descr
->items
; i
< descr
->nb_items
; i
++, item
++)
126 TRACE( "%4d: %-40s %d %08lx %3d\n",
127 i
, item
->str
, item
->selected
, item
->data
, item
->height
);
132 /***********************************************************************
133 * LISTBOX_GetCurrentPageSize
135 * Return the current page size
137 static INT
LISTBOX_GetCurrentPageSize( WND
*wnd
, LB_DESCR
*descr
)
140 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
141 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
143 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
145 if (i
== descr
->top_item
) return 1;
146 else return i
- descr
->top_item
;
150 /***********************************************************************
151 * LISTBOX_GetMaxTopIndex
153 * Return the maximum possible index for the top of the listbox.
155 static INT
LISTBOX_GetMaxTopIndex( WND
*wnd
, LB_DESCR
*descr
)
159 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
161 page
= descr
->height
;
162 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
163 if ((page
-= descr
->items
[max
].height
) < 0) break;
164 if (max
< descr
->nb_items
- 1) max
++;
166 else if (descr
->style
& LBS_MULTICOLUMN
)
168 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
169 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
170 max
= (max
- page
) * descr
->page_size
;
174 max
= descr
->nb_items
- descr
->page_size
;
176 if (max
< 0) max
= 0;
181 /***********************************************************************
182 * LISTBOX_UpdateScroll
184 * Update the scrollbars. Should be called whenever the content
185 * of the listbox changes.
187 static void LISTBOX_UpdateScroll( WND
*wnd
, LB_DESCR
*descr
)
191 /* Check the listbox scroll bar flags individually before we call
192 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
193 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
194 scroll bar when we do not need one.
195 if (!(descr->style & WS_VSCROLL)) return;
198 /* It is important that we check descr->style, and not wnd->dwStyle,
199 for WS_VSCROLL, as the former is exactly the one passed in
200 argument to CreateWindow.
201 In Windows (and from now on in Wine :) a listbox created
202 with such a style (no WS_SCROLL) does not update
203 the scrollbar with listbox-related data, thus letting
204 the programmer use it for his/her own purposes. */
206 if (descr
->style
& LBS_NOREDRAW
) return;
207 info
.cbSize
= sizeof(info
);
209 if (descr
->style
& LBS_MULTICOLUMN
)
212 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
213 info
.nPos
= descr
->top_item
/ descr
->page_size
;
214 info
.nPage
= descr
->width
/ descr
->column_width
;
215 if (info
.nPage
< 1) info
.nPage
= 1;
216 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
217 if (descr
->style
& LBS_DISABLENOSCROLL
)
218 info
.fMask
|= SIF_DISABLENOSCROLL
;
219 if (descr
->style
& WS_HSCROLL
)
220 SetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
222 info
.fMask
= SIF_RANGE
;
223 if (descr
->style
& WS_VSCROLL
)
224 SetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
229 info
.nMax
= descr
->nb_items
- 1;
230 info
.nPos
= descr
->top_item
;
231 info
.nPage
= LISTBOX_GetCurrentPageSize( wnd
, descr
);
232 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
233 if (descr
->style
& LBS_DISABLENOSCROLL
)
234 info
.fMask
|= SIF_DISABLENOSCROLL
;
235 if (descr
->style
& WS_VSCROLL
)
236 SetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
238 if (descr
->horz_extent
)
241 info
.nMax
= descr
->horz_extent
- 1;
242 info
.nPos
= descr
->horz_pos
;
243 info
.nPage
= descr
->width
;
244 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
245 if (descr
->style
& LBS_DISABLENOSCROLL
)
246 info
.fMask
|= SIF_DISABLENOSCROLL
;
247 if (descr
->style
& WS_HSCROLL
)
248 SetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
255 /***********************************************************************
258 * Set the top item of the listbox, scrolling up or down if necessary.
260 static LRESULT
LISTBOX_SetTopItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
263 INT max
= LISTBOX_GetMaxTopIndex( wnd
, descr
);
264 if (index
> max
) index
= max
;
265 if (index
< 0) index
= 0;
266 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
267 if (descr
->top_item
== index
) return LB_OKAY
;
268 if (descr
->style
& LBS_MULTICOLUMN
)
270 INT diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
271 if (scroll
&& (abs(diff
) < descr
->width
))
272 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
273 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
281 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
285 if (index
> descr
->top_item
)
287 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
288 diff
-= descr
->items
[i
].height
;
292 for (i
= index
; i
< descr
->top_item
; i
++)
293 diff
+= descr
->items
[i
].height
;
297 diff
= (descr
->top_item
- index
) * descr
->item_height
;
299 if (abs(diff
) < descr
->height
)
300 ScrollWindowEx( wnd
->hwndSelf
, 0, diff
, NULL
, NULL
, 0, NULL
,
301 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
305 if (!scroll
) InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
306 descr
->top_item
= index
;
307 LISTBOX_UpdateScroll( wnd
, descr
);
312 /***********************************************************************
315 * Update the page size. Should be called when the size of
316 * the client area or the item height changes.
318 static void LISTBOX_UpdatePage( WND
*wnd
, LB_DESCR
*descr
)
322 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
324 if (page_size
== descr
->page_size
) return;
325 descr
->page_size
= page_size
;
326 if (descr
->style
& LBS_MULTICOLUMN
)
327 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
328 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
, FALSE
);
332 /***********************************************************************
335 * Update the size of the listbox. Should be called when the size of
336 * the client area changes.
338 static void LISTBOX_UpdateSize( WND
*wnd
, LB_DESCR
*descr
)
342 GetClientRect( wnd
->hwndSelf
, &rect
);
343 descr
->width
= rect
.right
- rect
.left
;
344 descr
->height
= rect
.bottom
- rect
.top
;
345 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !IS_OWNERDRAW(descr
))
347 if ((descr
->height
> descr
->item_height
) &&
348 (descr
->height
% descr
->item_height
))
350 TRACE("[%04x]: changing height %d -> %d\n",
351 wnd
->hwndSelf
, descr
->height
,
352 descr
->height
- descr
->height
%descr
->item_height
);
353 SetWindowPos( wnd
->hwndSelf
, 0, 0, 0,
354 wnd
->rectWindow
.right
- wnd
->rectWindow
.left
,
355 wnd
->rectWindow
.bottom
- wnd
->rectWindow
.top
-
356 (descr
->height
% descr
->item_height
),
357 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
361 TRACE("[%04x]: new size = %d,%d\n",
362 wnd
->hwndSelf
, descr
->width
, descr
->height
);
363 LISTBOX_UpdatePage( wnd
, descr
);
364 LISTBOX_UpdateScroll( wnd
, descr
);
368 /***********************************************************************
369 * LISTBOX_GetItemRect
371 * Get the rectangle enclosing an item, in listbox client coordinates.
372 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
374 static LRESULT
LISTBOX_GetItemRect( WND
*wnd
, LB_DESCR
*descr
, INT index
,
377 /* Index <= 0 is legal even on empty listboxes */
378 if (index
&& (index
>= descr
->nb_items
)) return -1;
379 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
380 if (descr
->style
& LBS_MULTICOLUMN
)
382 INT col
= (index
/ descr
->page_size
) -
383 (descr
->top_item
/ descr
->page_size
);
384 rect
->left
+= col
* descr
->column_width
;
385 rect
->right
= rect
->left
+ descr
->column_width
;
386 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
387 rect
->bottom
= rect
->top
+ descr
->item_height
;
389 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
392 rect
->right
+= descr
->horz_pos
;
393 if ((index
>= 0) && (index
< descr
->nb_items
))
395 if (index
< descr
->top_item
)
397 for (i
= descr
->top_item
-1; i
>= index
; i
--)
398 rect
->top
-= descr
->items
[i
].height
;
402 for (i
= descr
->top_item
; i
< index
; i
++)
403 rect
->top
+= descr
->items
[i
].height
;
405 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
411 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
412 rect
->bottom
= rect
->top
+ descr
->item_height
;
413 rect
->right
+= descr
->horz_pos
;
416 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
417 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
421 /***********************************************************************
422 * LISTBOX_GetItemFromPoint
424 * Return the item nearest from point (x,y) (in client coordinates).
426 static INT
LISTBOX_GetItemFromPoint( WND
*wnd
, LB_DESCR
*descr
,
429 INT index
= descr
->top_item
;
431 if (!descr
->nb_items
) return -1; /* No items */
432 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
437 while (index
< descr
->nb_items
)
439 if ((pos
+= descr
->items
[index
].height
) > y
) break;
448 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
452 else if (descr
->style
& LBS_MULTICOLUMN
)
454 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
455 if (y
>= 0) index
+= y
/ descr
->item_height
;
456 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
457 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
461 index
+= (y
/ descr
->item_height
);
463 if (index
< 0) return 0;
464 if (index
>= descr
->nb_items
) return -1;
469 /***********************************************************************
474 static void LISTBOX_PaintItem( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
,
475 const RECT
*rect
, INT index
, UINT action
)
477 LB_ITEMDATA
*item
= NULL
;
478 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
480 if (IS_OWNERDRAW(descr
))
483 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
487 if (action
== ODA_FOCUS
)
488 DrawFocusRect( hdc
, rect
);
490 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
493 dis
.CtlType
= ODT_LISTBOX
;
495 dis
.hwndItem
= wnd
->hwndSelf
;
496 dis
.itemAction
= action
;
500 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
501 if ((descr
->focus_item
== index
) &&
503 (GetFocus() == wnd
->hwndSelf
)) dis
.itemState
|= ODS_FOCUS
;
504 if (wnd
->dwStyle
& WS_DISABLED
) dis
.itemState
|= ODS_DISABLED
;
505 dis
.itemData
= item
? item
->data
: 0;
507 TRACE("[%04x]: drawitem %d (%s) action=%02x "
508 "state=%02x rect=%d,%d-%d,%d\n",
509 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
510 dis
.itemState
, rect
->left
, rect
->top
,
511 rect
->right
, rect
->bottom
);
512 SendMessageA(descr
->owner
, WM_DRAWITEM
, id
, (LPARAM
)&dis
);
516 COLORREF oldText
= 0, oldBk
= 0;
518 if (action
== ODA_FOCUS
)
520 DrawFocusRect( hdc
, rect
);
523 if (item
&& item
->selected
)
525 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
526 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
529 TRACE("[%04x]: painting %d (%s) action=%02x "
530 "rect=%d,%d-%d,%d\n",
531 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
532 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
534 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
,
535 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
536 else if (!(descr
->style
& LBS_USETABSTOPS
))
537 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
,
538 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
539 strlen(item
->str
), NULL
);
542 /* Output empty string to paint background in the full width. */
543 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
,
544 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
545 TabbedTextOutA( hdc
, rect
->left
+ 1 , rect
->top
,
546 item
->str
, strlen(item
->str
),
547 descr
->nb_tabs
, descr
->tabs
, 0);
549 if (item
&& item
->selected
)
551 SetBkColor( hdc
, oldBk
);
552 SetTextColor( hdc
, oldText
);
554 if ((descr
->focus_item
== index
) &&
556 (GetFocus() == wnd
->hwndSelf
)) DrawFocusRect( hdc
, rect
);
561 /***********************************************************************
564 * Change the redraw flag.
566 static void LISTBOX_SetRedraw( WND
*wnd
, LB_DESCR
*descr
, BOOL on
)
570 if (!(descr
->style
& LBS_NOREDRAW
)) return;
571 descr
->style
&= ~LBS_NOREDRAW
;
572 LISTBOX_UpdateScroll( wnd
, descr
);
574 else descr
->style
|= LBS_NOREDRAW
;
578 /***********************************************************************
579 * LISTBOX_RepaintItem
581 * Repaint a single item synchronously.
583 static void LISTBOX_RepaintItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
589 HBRUSH hbrush
, oldBrush
= 0;
591 /* Do not repaint the item if the item is not visible */
592 if ((descr
->style
& LBS_NOREDRAW
) || !IsWindowVisible(wnd
->hwndSelf
)) return;
594 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) != 1) return;
595 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
))) return;
596 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
597 hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
598 hdc
, (LPARAM
)wnd
->hwndSelf
);
599 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
600 if (wnd
->dwStyle
& WS_DISABLED
)
601 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
602 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
603 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, index
, action
);
604 if (oldFont
) SelectObject( hdc
, oldFont
);
605 if (oldBrush
) SelectObject( hdc
, oldBrush
);
606 ReleaseDC( wnd
->hwndSelf
, hdc
);
610 /***********************************************************************
611 * LISTBOX_InitStorage
613 static LRESULT
LISTBOX_InitStorage( WND
*wnd
, LB_DESCR
*descr
, INT nb_items
,
618 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
619 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
621 nb_items
+= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
622 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
623 nb_items
* sizeof(LB_ITEMDATA
) )))
625 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
633 /***********************************************************************
634 * LISTBOX_SetTabStops
636 static BOOL
LISTBOX_SetTabStops( WND
*wnd
, LB_DESCR
*descr
, INT count
,
637 LPINT tabs
, BOOL short_ints
)
639 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
640 if (descr
->tabs
) HeapFree( descr
->heap
, 0, descr
->tabs
);
641 if (!(descr
->nb_tabs
= count
))
646 /* FIXME: count = 1 */
647 if (!(descr
->tabs
= (INT
*)HeapAlloc( descr
->heap
, 0,
648 descr
->nb_tabs
* sizeof(INT
) )))
653 LPINT16 p
= (LPINT16
)tabs
;
655 TRACE("[%04x]: settabstops ", wnd
->hwndSelf
);
656 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
657 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
658 if (TRACE_ON(listbox
)) DPRINTF("%hd ", descr
->tabs
[i
]);
660 if (TRACE_ON(listbox
)) DPRINTF("\n");
662 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
663 /* FIXME: repaint the window? */
668 /***********************************************************************
671 static LRESULT
LISTBOX_GetText( WND
*wnd
, LB_DESCR
*descr
, INT index
,
674 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
675 if (HAS_STRINGS(descr
))
678 return strlen(descr
->items
[index
].str
);
679 lstrcpyA( buffer
, descr
->items
[index
].str
);
680 return strlen(buffer
);
683 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
684 return sizeof(DWORD
);
689 /***********************************************************************
690 * LISTBOX_FindStringPos
692 * Find the nearest string located before a given string in sort order.
693 * If 'exact' is TRUE, return an error if we don't get an exact match.
695 static INT
LISTBOX_FindStringPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
,
698 INT index
, min
, max
, res
= -1;
700 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
702 max
= descr
->nb_items
;
705 index
= (min
+ max
) / 2;
706 if (HAS_STRINGS(descr
))
707 res
= lstrcmpiA( descr
->items
[index
].str
, str
);
710 COMPAREITEMSTRUCT cis
;
711 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
713 cis
.CtlType
= ODT_LISTBOX
;
715 cis
.hwndItem
= wnd
->hwndSelf
;
717 cis
.itemData1
= descr
->items
[index
].data
;
719 cis
.itemData2
= (DWORD
)str
;
720 cis
.dwLocaleId
= descr
->locale
;
721 res
= SendMessageA( descr
->owner
, WM_COMPAREITEM
,
724 if (!res
) return index
;
725 if (res
> 0) max
= index
;
726 else min
= index
+ 1;
728 return exact
? -1 : max
;
732 /***********************************************************************
733 * LISTBOX_FindFileStrPos
735 * Find the nearest string located before a given string in directory
736 * sort order (i.e. first files, then directories, then drives).
738 static INT
LISTBOX_FindFileStrPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
)
740 INT min
, max
, res
= -1;
742 if (!HAS_STRINGS(descr
))
743 return LISTBOX_FindStringPos( wnd
, descr
, str
, FALSE
);
745 max
= descr
->nb_items
;
748 INT index
= (min
+ max
) / 2;
749 const char *p
= descr
->items
[index
].str
;
750 if (*p
== '[') /* drive or directory */
752 if (*str
!= '[') res
= -1;
753 else if (p
[1] == '-') /* drive */
755 if (str
[1] == '-') res
= str
[2] - p
[2];
760 if (str
[1] == '-') res
= 1;
761 else res
= lstrcmpiA( str
, p
);
766 if (*str
== '[') res
= 1;
767 else res
= lstrcmpiA( str
, p
);
769 if (!res
) return index
;
770 if (res
< 0) max
= index
;
771 else min
= index
+ 1;
777 /***********************************************************************
780 * Find the item beginning with a given string.
782 static INT
LISTBOX_FindString( WND
*wnd
, LB_DESCR
*descr
, INT start
,
783 LPCSTR str
, BOOL exact
)
788 if (start
>= descr
->nb_items
) start
= -1;
789 item
= descr
->items
+ start
+ 1;
790 if (HAS_STRINGS(descr
))
792 if (!str
|| ! str
[0] ) return LB_ERR
;
795 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
796 if (!lstrcmpiA( str
, item
->str
)) return i
;
797 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
798 if (!lstrcmpiA( str
, item
->str
)) return i
;
802 /* Special case for drives and directories: ignore prefix */
803 #define CHECK_DRIVE(item) \
804 if ((item)->str[0] == '[') \
806 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
807 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
811 INT len
= strlen(str
);
812 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
814 if (!lstrncmpiA( str
, item
->str
, len
)) return i
;
817 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
819 if (!lstrncmpiA( str
, item
->str
, len
)) return i
;
827 if (exact
&& (descr
->style
& LBS_SORT
))
828 /* If sorted, use a WM_COMPAREITEM binary search */
829 return LISTBOX_FindStringPos( wnd
, descr
, str
, TRUE
);
831 /* Otherwise use a linear search */
832 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
833 if (item
->data
== (DWORD
)str
) return i
;
834 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
835 if (item
->data
== (DWORD
)str
) return i
;
841 /***********************************************************************
842 * LISTBOX_GetSelCount
844 static LRESULT
LISTBOX_GetSelCount( WND
*wnd
, LB_DESCR
*descr
)
847 LB_ITEMDATA
*item
= descr
->items
;
849 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
850 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
851 if (item
->selected
) count
++;
856 /***********************************************************************
857 * LISTBOX_GetSelItems16
859 static LRESULT
LISTBOX_GetSelItems16( WND
*wnd
, LB_DESCR
*descr
, INT16 max
,
863 LB_ITEMDATA
*item
= descr
->items
;
865 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
866 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
867 if (item
->selected
) array
[count
++] = (INT16
)i
;
872 /***********************************************************************
873 * LISTBOX_GetSelItems32
875 static LRESULT
LISTBOX_GetSelItems( WND
*wnd
, LB_DESCR
*descr
, INT 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
++] = i
;
888 /***********************************************************************
891 static LRESULT
LISTBOX_Paint( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
)
893 INT i
, col_pos
= descr
->page_size
- 1;
896 HBRUSH hbrush
, oldBrush
= 0;
898 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
899 if (descr
->style
& LBS_NOREDRAW
) return 0;
900 if (descr
->style
& LBS_MULTICOLUMN
)
901 rect
.right
= rect
.left
+ descr
->column_width
;
902 else if (descr
->horz_pos
)
904 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
905 rect
.right
+= descr
->horz_pos
;
908 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
909 hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
910 hdc
, (LPARAM
)wnd
->hwndSelf
);
911 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
912 if (wnd
->dwStyle
& WS_DISABLED
)
913 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
915 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
916 (GetFocus() == wnd
->hwndSelf
))
918 /* Special case for empty listbox: paint focus rect */
919 rect
.bottom
= rect
.top
+ descr
->item_height
;
920 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, descr
->focus_item
,
922 rect
.top
= rect
.bottom
;
925 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
927 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
928 rect
.bottom
= rect
.top
+ descr
->item_height
;
930 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
932 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
);
933 rect
.top
= rect
.bottom
;
935 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
937 if (!IS_OWNERDRAW(descr
))
939 /* Clear the bottom of the column */
940 SetBkColor( hdc
, GetSysColor( COLOR_WINDOW
) );
941 if (rect
.top
< descr
->height
)
943 rect
.bottom
= descr
->height
;
944 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
945 &rect
, NULL
, 0, NULL
);
949 /* Go to the next column */
950 rect
.left
+= descr
->column_width
;
951 rect
.right
+= descr
->column_width
;
953 col_pos
= descr
->page_size
- 1;
958 if (rect
.top
>= descr
->height
) break;
962 if (!IS_OWNERDRAW(descr
))
964 /* Clear the remainder of the client area */
965 SetBkColor( hdc
, GetSysColor( COLOR_WINDOW
) );
966 if (rect
.top
< descr
->height
)
968 rect
.bottom
= descr
->height
;
969 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
970 &rect
, NULL
, 0, NULL
);
972 if (rect
.right
< descr
->width
)
974 rect
.left
= rect
.right
;
975 rect
.right
= descr
->width
;
977 rect
.bottom
= descr
->height
;
978 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
979 &rect
, NULL
, 0, NULL
);
982 if (oldFont
) SelectObject( hdc
, oldFont
);
983 if (oldBrush
) SelectObject( hdc
, oldBrush
);
988 /***********************************************************************
989 * LISTBOX_InvalidateItems
991 * Invalidate all items from a given item. If the specified item is not
992 * visible, nothing happens.
994 static void LISTBOX_InvalidateItems( WND
*wnd
, LB_DESCR
*descr
, INT index
)
998 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) == 1)
1000 rect
.bottom
= descr
->height
;
1001 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
1002 if (descr
->style
& LBS_MULTICOLUMN
)
1004 /* Repaint the other columns */
1005 rect
.left
= rect
.right
;
1006 rect
.right
= descr
->width
;
1008 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
1014 /***********************************************************************
1015 * LISTBOX_GetItemHeight
1017 static LRESULT
LISTBOX_GetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1019 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1021 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1022 return descr
->items
[index
].height
;
1024 else return descr
->item_height
;
1028 /***********************************************************************
1029 * LISTBOX_SetItemHeight
1031 static LRESULT
LISTBOX_SetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1034 if (!height
) height
= 1;
1036 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1038 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1039 TRACE("[%04x]: item %d height = %d\n",
1040 wnd
->hwndSelf
, index
, height
);
1041 descr
->items
[index
].height
= height
;
1042 LISTBOX_UpdateScroll( wnd
, descr
);
1043 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1045 else if (height
!= descr
->item_height
)
1047 TRACE("[%04x]: new height = %d\n",
1048 wnd
->hwndSelf
, height
);
1049 descr
->item_height
= height
;
1050 LISTBOX_UpdatePage( wnd
, descr
);
1051 LISTBOX_UpdateScroll( wnd
, descr
);
1052 InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
1058 /***********************************************************************
1059 * LISTBOX_SetHorizontalPos
1061 static void LISTBOX_SetHorizontalPos( WND
*wnd
, LB_DESCR
*descr
, INT pos
)
1065 if (pos
> descr
->horz_extent
- descr
->width
)
1066 pos
= descr
->horz_extent
- descr
->width
;
1067 if (pos
< 0) pos
= 0;
1068 if (!(diff
= descr
->horz_pos
- pos
)) return;
1069 TRACE("[%04x]: new horz pos = %d\n",
1070 wnd
->hwndSelf
, pos
);
1071 descr
->horz_pos
= pos
;
1072 LISTBOX_UpdateScroll( wnd
, descr
);
1073 if (abs(diff
) < descr
->width
)
1074 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
1075 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1077 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1081 /***********************************************************************
1082 * LISTBOX_SetHorizontalExtent
1084 static LRESULT
LISTBOX_SetHorizontalExtent( WND
*wnd
, LB_DESCR
*descr
,
1087 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1089 if (extent
<= 0) extent
= 1;
1090 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1091 TRACE("[%04x]: new horz extent = %d\n",
1092 wnd
->hwndSelf
, extent
);
1093 descr
->horz_extent
= extent
;
1094 if (descr
->horz_pos
> extent
- descr
->width
)
1095 LISTBOX_SetHorizontalPos( wnd
, descr
, extent
- descr
->width
);
1097 LISTBOX_UpdateScroll( wnd
, descr
);
1102 /***********************************************************************
1103 * LISTBOX_SetColumnWidth
1105 static LRESULT
LISTBOX_SetColumnWidth( WND
*wnd
, LB_DESCR
*descr
, UINT width
)
1107 if (width
== descr
->column_width
) return LB_OKAY
;
1108 TRACE("[%04x]: new column width = %d\n",
1109 wnd
->hwndSelf
, width
);
1110 descr
->column_width
= width
;
1111 LISTBOX_UpdatePage( wnd
, descr
);
1116 /***********************************************************************
1119 * Returns the item height.
1121 static INT
LISTBOX_SetFont( WND
*wnd
, LB_DESCR
*descr
, HFONT font
)
1129 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
)))
1131 ERR("unable to get DC.\n" );
1134 if (font
) oldFont
= SelectObject( hdc
, font
);
1135 GetTextMetricsA( hdc
, &tm
);
1136 if (oldFont
) SelectObject( hdc
, oldFont
);
1137 ReleaseDC( wnd
->hwndSelf
, hdc
);
1138 if (!IS_OWNERDRAW(descr
))
1139 LISTBOX_SetItemHeight( wnd
, descr
, 0, tm
.tmHeight
);
1140 return tm
.tmHeight
;
1144 /***********************************************************************
1145 * LISTBOX_MakeItemVisible
1147 * Make sure that a given item is partially or fully visible.
1149 static void LISTBOX_MakeItemVisible( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1154 if (index
<= descr
->top_item
) top
= index
;
1155 else if (descr
->style
& LBS_MULTICOLUMN
)
1157 INT cols
= descr
->width
;
1158 if (!fully
) cols
+= descr
->column_width
- 1;
1159 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1161 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1162 top
= index
- descr
->page_size
* (cols
- 1);
1164 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1166 INT height
= fully
? descr
->items
[index
].height
: 1;
1167 for (top
= index
; top
> descr
->top_item
; top
--)
1168 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1172 if (index
< descr
->top_item
+ descr
->page_size
) return;
1173 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1174 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1175 top
= index
- descr
->page_size
+ 1;
1177 LISTBOX_SetTopItem( wnd
, descr
, top
, TRUE
);
1181 /***********************************************************************
1182 * LISTBOX_SelectItemRange
1184 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1186 static LRESULT
LISTBOX_SelectItemRange( WND
*wnd
, LB_DESCR
*descr
, INT first
,
1191 /* A few sanity checks */
1193 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1194 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1195 if (last
== -1) last
= descr
->nb_items
- 1;
1196 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1197 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1198 /* selected_item reflects last selected/unselected item on multiple sel */
1199 descr
->selected_item
= last
;
1201 if (on
) /* Turn selection on */
1203 for (i
= first
; i
<= last
; i
++)
1205 if (descr
->items
[i
].selected
) continue;
1206 descr
->items
[i
].selected
= TRUE
;
1207 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1210 else /* Turn selection off */
1212 for (i
= first
; i
<= last
; i
++)
1214 if (!descr
->items
[i
].selected
) continue;
1215 descr
->items
[i
].selected
= FALSE
;
1216 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1223 /***********************************************************************
1224 * LISTBOX_SetCaretIndex
1227 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1230 static LRESULT
LISTBOX_SetCaretIndex( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1231 BOOL fully_visible
)
1233 INT oldfocus
= descr
->focus_item
;
1235 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1236 if (index
== oldfocus
) return LB_OKAY
;
1237 descr
->focus_item
= index
;
1238 if ((oldfocus
!= -1) && descr
->caret_on
&& (GetFocus() == wnd
->hwndSelf
))
1239 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1241 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1242 if (descr
->caret_on
&& (GetFocus() == wnd
->hwndSelf
))
1243 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1249 /***********************************************************************
1250 * LISTBOX_SetSelection
1252 static LRESULT
LISTBOX_SetSelection( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1253 BOOL on
, BOOL send_notify
)
1255 TRACE( "index=%d notify=%s\n", index
, send_notify
? "YES" : "NO" );
1257 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1258 if (descr
->style
& LBS_MULTIPLESEL
)
1260 if (index
== -1) /* Select all items */
1261 return LISTBOX_SelectItemRange( wnd
, descr
, 0, -1, on
);
1262 else /* Only one item */
1263 return LISTBOX_SelectItemRange( wnd
, descr
, index
, index
, on
);
1267 INT oldsel
= descr
->selected_item
;
1268 if (index
== oldsel
) return LB_OKAY
;
1269 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1270 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1271 descr
->selected_item
= index
;
1272 if (oldsel
!= -1) LISTBOX_RepaintItem( wnd
, descr
, oldsel
, ODA_SELECT
);
1273 if (index
!= -1) LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_SELECT
);
1274 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( wnd
, descr
,
1275 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1277 if( descr
->lphc
) /* set selection change flag for parent combo */
1278 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1284 /***********************************************************************
1287 * Change the caret position and extend the selection to the new caret.
1289 static void LISTBOX_MoveCaret( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1290 BOOL fully_visible
)
1292 LISTBOX_SetCaretIndex( wnd
, descr
, index
, fully_visible
);
1293 if (descr
->style
& LBS_EXTENDEDSEL
)
1295 if (descr
->anchor_item
!= -1)
1297 INT first
= min( descr
->focus_item
, descr
->anchor_item
);
1298 INT last
= max( descr
->focus_item
, descr
->anchor_item
);
1300 LISTBOX_SelectItemRange( wnd
, descr
, 0, first
- 1, FALSE
);
1301 LISTBOX_SelectItemRange( wnd
, descr
, last
+ 1, -1, FALSE
);
1302 LISTBOX_SelectItemRange( wnd
, descr
, first
, last
, TRUE
);
1305 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1307 /* Set selection to new caret item */
1308 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
1313 /***********************************************************************
1314 * LISTBOX_InsertItem
1316 static LRESULT
LISTBOX_InsertItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1317 LPSTR str
, DWORD data
)
1321 INT oldfocus
= descr
->focus_item
;
1323 if (index
== -1) index
= descr
->nb_items
;
1324 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1325 if (!descr
->items
) max_items
= 0;
1326 else max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
1327 if (descr
->nb_items
== max_items
)
1329 /* We need to grow the array */
1330 max_items
+= LB_ARRAY_GRANULARITY
;
1331 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1332 max_items
* sizeof(LB_ITEMDATA
) )))
1334 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1337 descr
->items
= item
;
1340 /* Insert the item structure */
1342 item
= &descr
->items
[index
];
1343 if (index
< descr
->nb_items
)
1344 RtlMoveMemory( item
+ 1, item
,
1345 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1349 item
->selected
= FALSE
;
1352 /* Get item height */
1354 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1356 MEASUREITEMSTRUCT mis
;
1357 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
1359 mis
.CtlType
= ODT_LISTBOX
;
1362 mis
.itemData
= descr
->items
[index
].data
;
1363 mis
.itemHeight
= descr
->item_height
;
1364 SendMessageA( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1365 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1366 TRACE("[%04x]: measure item %d (%s) = %d\n",
1367 wnd
->hwndSelf
, index
, str
? str
: "", item
->height
);
1370 /* Repaint the items */
1372 LISTBOX_UpdateScroll( wnd
, descr
);
1373 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1375 /* Move selection and focused item */
1376 /* If listbox was empty, set focus to the first item */
1377 if (descr
->nb_items
== 1)
1378 LISTBOX_SetCaretIndex( wnd
, descr
, 0, FALSE
);
1379 /* single select don't change selection index in win31 */
1380 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1382 descr
->selected_item
++;
1383 LISTBOX_SetSelection( wnd
, descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1387 if (index
<= descr
->selected_item
)
1389 descr
->selected_item
++;
1390 descr
->focus_item
= oldfocus
; /* focus not changed */
1397 /***********************************************************************
1398 * LISTBOX_InsertString
1400 static LRESULT
LISTBOX_InsertString( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1403 LPSTR new_str
= NULL
;
1407 if (HAS_STRINGS(descr
))
1410 if (!(new_str
= HEAP_strdupA( descr
->heap
, 0, str
)))
1412 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1416 else data
= (DWORD
)str
;
1418 if (index
== -1) index
= descr
->nb_items
;
1419 if ((ret
= LISTBOX_InsertItem( wnd
, descr
, index
, new_str
, data
)) != 0)
1421 if (new_str
) HeapFree( descr
->heap
, 0, new_str
);
1425 TRACE("[%04x]: added item %d '%s'\n",
1426 wnd
->hwndSelf
, index
, HAS_STRINGS(descr
) ? new_str
: "" );
1431 /***********************************************************************
1432 * LISTBOX_DeleteItem
1434 * Delete the content of an item. 'index' must be a valid index.
1436 static void LISTBOX_DeleteItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1438 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1439 * while Win95 sends it for all items with user data.
1440 * It's probably better to send it too often than not
1441 * often enough, so this is what we do here.
1443 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1445 DELETEITEMSTRUCT dis
;
1446 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
1448 dis
.CtlType
= ODT_LISTBOX
;
1451 dis
.hwndItem
= wnd
->hwndSelf
;
1452 dis
.itemData
= descr
->items
[index
].data
;
1453 SendMessageA( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1455 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1456 HeapFree( descr
->heap
, 0, descr
->items
[index
].str
);
1460 /***********************************************************************
1461 * LISTBOX_RemoveItem
1463 * Remove an item from the listbox and delete its content.
1465 static LRESULT
LISTBOX_RemoveItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1470 if (index
== -1) index
= descr
->nb_items
- 1;
1471 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1473 /* We need to invalidate the original rect instead of the updated one. */
1474 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1476 LISTBOX_DeleteItem( wnd
, descr
, index
);
1478 /* Remove the item */
1480 item
= &descr
->items
[index
];
1481 if (index
< descr
->nb_items
-1)
1482 RtlMoveMemory( item
, item
+ 1,
1483 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1485 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1487 /* Shrink the item array if possible */
1489 max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1490 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1492 max_items
-= LB_ARRAY_GRANULARITY
;
1493 item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1494 max_items
* sizeof(LB_ITEMDATA
) );
1495 if (item
) descr
->items
= item
;
1497 /* Repaint the items */
1499 LISTBOX_UpdateScroll( wnd
, descr
);
1500 /* if we removed the scrollbar, reset the top of the list
1501 (correct for owner-drawn ???) */
1502 if (descr
->nb_items
== descr
->page_size
)
1503 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1505 /* Move selection and focused item */
1506 if (!IS_MULTISELECT(descr
))
1508 if (index
== descr
->selected_item
)
1509 descr
->selected_item
= -1;
1510 else if (index
< descr
->selected_item
)
1512 descr
->selected_item
--;
1513 if (ISWIN31
) /* win 31 do not change the selected item number */
1514 LISTBOX_SetSelection( wnd
, descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1518 if (descr
->focus_item
>= descr
->nb_items
)
1520 descr
->focus_item
= descr
->nb_items
- 1;
1521 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1527 /***********************************************************************
1528 * LISTBOX_ResetContent
1530 static void LISTBOX_ResetContent( WND
*wnd
, LB_DESCR
*descr
)
1534 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( wnd
, descr
, i
);
1535 if (descr
->items
) HeapFree( descr
->heap
, 0, descr
->items
);
1536 descr
->nb_items
= 0;
1537 descr
->top_item
= 0;
1538 descr
->selected_item
= -1;
1539 descr
->focus_item
= 0;
1540 descr
->anchor_item
= -1;
1541 descr
->items
= NULL
;
1542 LISTBOX_UpdateScroll( wnd
, descr
);
1543 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1547 /***********************************************************************
1550 static LRESULT
LISTBOX_SetCount( WND
*wnd
, LB_DESCR
*descr
, INT count
)
1554 if (HAS_STRINGS(descr
)) return LB_ERR
;
1555 /* FIXME: this is far from optimal... */
1556 if (count
> descr
->nb_items
)
1558 while (count
> descr
->nb_items
)
1559 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, 0 )) < 0)
1562 else if (count
< descr
->nb_items
)
1564 while (count
< descr
->nb_items
)
1565 if ((ret
= LISTBOX_RemoveItem( wnd
, descr
, -1 )) < 0)
1572 /***********************************************************************
1575 static LRESULT
LISTBOX_Directory( WND
*wnd
, LB_DESCR
*descr
, UINT attrib
,
1576 LPCSTR filespec
, BOOL long_names
)
1579 LRESULT ret
= LB_OKAY
;
1580 WIN32_FIND_DATAA entry
;
1583 if ((handle
= FindFirstFileA(filespec
,&entry
)) == INVALID_HANDLE_VALUE
)
1585 if (GetLastError() != ERROR_NO_MORE_FILES
) return LB_ERR
;
1592 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1594 if (!(attrib
& DDL_DIRECTORY
) ||
1595 !strcmp( entry
.cAlternateFileName
, "." )) continue;
1596 if (long_names
) sprintf( buffer
, "[%s]", entry
.cFileName
);
1597 else sprintf( buffer
, "[%s]", entry
.cAlternateFileName
);
1599 else /* not a directory */
1601 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1602 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1604 if ((attrib
& DDL_EXCLUSIVE
) &&
1605 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1608 if (long_names
) strcpy( buffer
, entry
.cFileName
);
1609 else strcpy( buffer
, entry
.cAlternateFileName
);
1611 if (!long_names
) CharLowerA( buffer
);
1612 pos
= LISTBOX_FindFileStrPos( wnd
, descr
, buffer
);
1613 if ((ret
= LISTBOX_InsertString( wnd
, descr
, pos
, buffer
)) < 0)
1615 } while (FindNextFileA( handle
, &entry
));
1616 FindClose( handle
);
1619 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1621 char buffer
[] = "[-a-]";
1623 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++, buffer
[2]++)
1625 if (!DRIVE_IsValid(drive
)) continue;
1626 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, buffer
)) < 0)
1634 /***********************************************************************
1635 * LISTBOX_HandleVScroll
1637 static LRESULT
LISTBOX_HandleVScroll( WND
*wnd
, LB_DESCR
*descr
,
1638 WPARAM wParam
, LPARAM lParam
)
1642 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1643 switch(LOWORD(wParam
))
1646 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
- 1, TRUE
);
1649 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ 1, TRUE
);
1652 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-
1653 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1656 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+
1657 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1659 case SB_THUMBPOSITION
:
1660 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
), TRUE
);
1663 info
.cbSize
= sizeof(info
);
1664 info
.fMask
= SIF_TRACKPOS
;
1665 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1666 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
, TRUE
);
1669 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1672 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1679 /***********************************************************************
1680 * LISTBOX_HandleHScroll
1682 static LRESULT
LISTBOX_HandleHScroll( WND
*wnd
, LB_DESCR
*descr
,
1683 WPARAM wParam
, LPARAM lParam
)
1688 if (descr
->style
& LBS_MULTICOLUMN
)
1690 switch(LOWORD(wParam
))
1693 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-descr
->page_size
,
1697 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+descr
->page_size
,
1701 page
= descr
->width
/ descr
->column_width
;
1702 if (page
< 1) page
= 1;
1703 LISTBOX_SetTopItem( wnd
, descr
,
1704 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1707 page
= descr
->width
/ descr
->column_width
;
1708 if (page
< 1) page
= 1;
1709 LISTBOX_SetTopItem( wnd
, descr
,
1710 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1712 case SB_THUMBPOSITION
:
1713 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1717 info
.cbSize
= sizeof(info
);
1718 info
.fMask
= SIF_TRACKPOS
;
1719 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1720 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1724 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1727 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1731 else if (descr
->horz_extent
)
1733 switch(LOWORD(wParam
))
1736 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
- 1 );
1739 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
+ 1 );
1742 LISTBOX_SetHorizontalPos( wnd
, descr
,
1743 descr
->horz_pos
- descr
->width
);
1746 LISTBOX_SetHorizontalPos( wnd
, descr
,
1747 descr
->horz_pos
+ descr
->width
);
1749 case SB_THUMBPOSITION
:
1750 LISTBOX_SetHorizontalPos( wnd
, descr
, HIWORD(wParam
) );
1753 info
.cbSize
= sizeof(info
);
1754 info
.fMask
= SIF_TRACKPOS
;
1755 GetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
);
1756 LISTBOX_SetHorizontalPos( wnd
, descr
, info
.nTrackPos
);
1759 LISTBOX_SetHorizontalPos( wnd
, descr
, 0 );
1762 LISTBOX_SetHorizontalPos( wnd
, descr
,
1763 descr
->horz_extent
- descr
->width
);
1770 static LRESULT
LISTBOX_HandleMouseWheel(WND
*wnd
, LB_DESCR
*descr
,WPARAM wParam
, LPARAM lParam
)
1772 short gcWheelDelta
= 0;
1773 UINT pulScrollLines
= 3;
1775 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1777 gcWheelDelta
-= (short) HIWORD(wParam
);
1779 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
1781 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
1782 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
1783 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ cLineScroll
, TRUE
);
1788 /***********************************************************************
1789 * LISTBOX_HandleLButtonDown
1791 static LRESULT
LISTBOX_HandleLButtonDown( WND
*wnd
, LB_DESCR
*descr
,
1792 WPARAM wParam
, INT x
, INT y
)
1794 INT index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1795 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1796 wnd
->hwndSelf
, x
, y
, index
);
1797 if (!descr
->caret_on
&& (GetFocus() == wnd
->hwndSelf
)) return 0;
1800 if (descr
->style
& LBS_EXTENDEDSEL
)
1802 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1803 if (wParam
& MK_CONTROL
)
1805 LISTBOX_SetCaretIndex( wnd
, descr
, index
, FALSE
);
1806 LISTBOX_SetSelection( wnd
, descr
, index
,
1807 !descr
->items
[index
].selected
,
1808 (descr
->style
& LBS_NOTIFY
) != 0);
1810 else LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1814 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1815 LISTBOX_SetSelection( wnd
, descr
, index
,
1816 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1817 !descr
->items
[index
].selected
),
1818 (descr
->style
& LBS_NOTIFY
) != 0 );
1824 HWND hwndFocus
= GetFocus();
1825 if ((hwndFocus
!= wnd
->hwndSelf
) && (hwndFocus
!= descr
->owner
))
1826 SetFocus( wnd
->hwndSelf
);
1828 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
1829 : descr
->lphc
->self
->hwndSelf
) ;
1831 descr
->captured
= TRUE
;
1832 SetCapture( wnd
->hwndSelf
);
1833 if (index
!= -1 && !descr
->lphc
)
1835 if (descr
->style
& LBS_NOTIFY
)
1836 SendMessageA( descr
->owner
, WM_LBTRACKPOINT
, index
,
1837 MAKELPARAM( x
, y
) );
1838 if (wnd
->dwExStyle
& WS_EX_DRAGDETECT
)
1845 if (DragDetect( wnd
->hwndSelf
, pt
))
1846 SendMessageA( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
1853 /*************************************************************************
1854 * LISTBOX_HandleLButtonDownCombo [Internal]
1856 * Process LButtonDown message for the ComboListBox
1859 * pWnd [I] The windows internal structure
1860 * pDescr [I] The ListBox internal structure
1861 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
1862 * x [I] X Mouse Coordinate
1863 * y [I] Y Mouse Coordinate
1866 * 0 since we are processing the WM_LBUTTONDOWN Message
1869 * This function is only to be used when a ListBox is a ComboListBox
1872 static LRESULT
LISTBOX_HandleLButtonDownCombo( WND
*pWnd
, LB_DESCR
*pDescr
,
1873 UINT msg
, WPARAM wParam
, INT x
, INT y
)
1875 RECT clientRect
, screenRect
;
1881 GetClientRect(pWnd
->hwndSelf
, &clientRect
);
1883 if(PtInRect(&clientRect
, mousePos
))
1885 /* MousePos is in client, resume normal processing */
1886 if (msg
== WM_LBUTTONDOWN
)
1887 return LISTBOX_HandleLButtonDown( pWnd
, pDescr
, wParam
, x
, y
);
1888 else if (pDescr
->style
& LBS_NOTIFY
)
1889 SEND_NOTIFICATION( pWnd
, pDescr
, LBN_DBLCLK
);
1894 POINT screenMousePos
;
1895 HWND hWndOldCapture
;
1897 /* Check the Non-Client Area */
1898 screenMousePos
= mousePos
;
1899 hWndOldCapture
= GetCapture();
1901 GetWindowRect(pWnd
->hwndSelf
, &screenRect
);
1902 ClientToScreen(pWnd
->hwndSelf
, &screenMousePos
);
1904 if(!PtInRect(&screenRect
, screenMousePos
))
1906 /* Close The Drop Down */
1907 SEND_NOTIFICATION( pWnd
, pDescr
, LBN_SELCANCEL
);
1912 /* Check to see the NC is a scrollbar */
1914 /* Check Vertical scroll bar */
1915 if (pWnd
->dwStyle
& WS_VSCROLL
)
1917 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
1918 if (PtInRect( &clientRect
, mousePos
))
1920 nHitTestType
= HTVSCROLL
;
1923 /* Check horizontal scroll bar */
1924 if (pWnd
->dwStyle
& WS_HSCROLL
)
1926 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
1927 if (PtInRect( &clientRect
, mousePos
))
1929 nHitTestType
= HTHSCROLL
;
1932 /* Windows sends this message when a scrollbar is clicked
1935 if(nHitTestType
!= 0)
1937 SendMessageA(pWnd
->hwndSelf
, WM_NCLBUTTONDOWN
, nHitTestType
,
1938 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
1940 /* Resume the Capture after scrolling is complete
1942 if(hWndOldCapture
!= 0)
1944 SetCapture(hWndOldCapture
);
1951 /***********************************************************************
1952 * LISTBOX_HandleLButtonUp
1954 static LRESULT
LISTBOX_HandleLButtonUp( WND
*wnd
, LB_DESCR
*descr
)
1956 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
1957 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
1958 LISTBOX_Timer
= LB_TIMER_NONE
;
1959 if (descr
->captured
)
1961 descr
->captured
= FALSE
;
1962 if (GetCapture() == wnd
->hwndSelf
) ReleaseCapture();
1963 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
1964 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
1970 /***********************************************************************
1971 * LISTBOX_HandleTimer
1973 * Handle scrolling upon a timer event.
1974 * Return TRUE if scrolling should continue.
1976 static LRESULT
LISTBOX_HandleTimer( WND
*wnd
, LB_DESCR
*descr
,
1977 INT index
, TIMER_DIRECTION dir
)
1982 if (descr
->top_item
) index
= descr
->top_item
- 1;
1986 if (descr
->top_item
) index
-= descr
->page_size
;
1989 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( wnd
, descr
);
1990 if (index
== descr
->focus_item
) index
++;
1991 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
1993 case LB_TIMER_RIGHT
:
1994 if (index
+ descr
->page_size
< descr
->nb_items
)
1995 index
+= descr
->page_size
;
2000 if (index
== descr
->focus_item
) return FALSE
;
2001 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
2006 /***********************************************************************
2007 * LISTBOX_HandleSystemTimer
2009 * WM_SYSTIMER handler.
2011 static LRESULT
LISTBOX_HandleSystemTimer( WND
*wnd
, LB_DESCR
*descr
)
2013 if (!LISTBOX_HandleTimer( wnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
2015 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2016 LISTBOX_Timer
= LB_TIMER_NONE
;
2022 /***********************************************************************
2023 * LISTBOX_HandleMouseMove
2025 * WM_MOUSEMOVE handler.
2027 static void LISTBOX_HandleMouseMove( WND
*wnd
, LB_DESCR
*descr
,
2031 TIMER_DIRECTION dir
;
2033 if (!descr
->captured
) return;
2035 if (descr
->style
& LBS_MULTICOLUMN
)
2038 else if (y
>= descr
->item_height
* descr
->page_size
)
2039 y
= descr
->item_height
* descr
->page_size
- 1;
2043 dir
= LB_TIMER_LEFT
;
2046 else if (x
>= descr
->width
)
2048 dir
= LB_TIMER_RIGHT
;
2049 x
= descr
->width
- 1;
2051 else dir
= LB_TIMER_NONE
; /* inside */
2055 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2056 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2057 else dir
= LB_TIMER_NONE
; /* inside */
2060 index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
2061 if (index
== -1) index
= descr
->focus_item
;
2062 if (!LISTBOX_HandleTimer( wnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2064 /* Start/stop the system timer */
2066 if (dir
!= LB_TIMER_NONE
)
2067 SetSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2068 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2069 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
2070 LISTBOX_Timer
= dir
;
2074 /***********************************************************************
2075 * LISTBOX_HandleKeyDown
2077 static LRESULT
LISTBOX_HandleKeyDown( WND
*wnd
, LB_DESCR
*descr
, WPARAM wParam
)
2080 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2081 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2082 bForceSelection
= FALSE
; /* only for single select list */
2084 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2086 caret
= SendMessageA( descr
->owner
, WM_VKEYTOITEM
,
2087 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2089 if (caret
== -2) return 0;
2091 if (caret
== -1) switch(wParam
)
2094 if (descr
->style
& LBS_MULTICOLUMN
)
2096 bForceSelection
= FALSE
;
2097 if (descr
->focus_item
>= descr
->page_size
)
2098 caret
= descr
->focus_item
- descr
->page_size
;
2103 caret
= descr
->focus_item
- 1;
2104 if (caret
< 0) caret
= 0;
2107 if (descr
->style
& LBS_MULTICOLUMN
)
2109 bForceSelection
= FALSE
;
2110 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2111 caret
= descr
->focus_item
+ descr
->page_size
;
2116 caret
= descr
->focus_item
+ 1;
2117 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2121 if (descr
->style
& LBS_MULTICOLUMN
)
2123 INT page
= descr
->width
/ descr
->column_width
;
2124 if (page
< 1) page
= 1;
2125 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2127 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(wnd
,descr
)+1;
2128 if (caret
< 0) caret
= 0;
2131 if (descr
->style
& LBS_MULTICOLUMN
)
2133 INT page
= descr
->width
/ descr
->column_width
;
2134 if (page
< 1) page
= 1;
2135 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2137 else caret
= descr
->focus_item
+LISTBOX_GetCurrentPageSize(wnd
,descr
)-1;
2138 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2144 caret
= descr
->nb_items
- 1;
2147 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2148 else if (descr
->style
& LBS_MULTIPLESEL
)
2150 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
,
2151 !descr
->items
[descr
->focus_item
].selected
,
2152 (descr
->style
& LBS_NOTIFY
) != 0 );
2156 bForceSelection
= FALSE
;
2158 if (bForceSelection
) /* focused item is used instead of key */
2159 caret
= descr
->focus_item
;
2162 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
2163 !(GetKeyState( VK_SHIFT
) & 0x8000))
2164 descr
->anchor_item
= caret
;
2165 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2166 LISTBOX_SetSelection( wnd
, descr
, caret
, TRUE
, FALSE
);
2167 if (descr
->style
& LBS_NOTIFY
)
2169 if( descr
->lphc
&& CB_GETTYPE(descr
->lphc
) != CBS_SIMPLE
)
2171 /* make sure that combo parent doesn't hide us */
2172 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2174 if (descr
->nb_items
) SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2181 /***********************************************************************
2182 * LISTBOX_HandleChar
2184 static LRESULT
LISTBOX_HandleChar( WND
*wnd
, LB_DESCR
*descr
,
2190 str
[0] = wParam
& 0xff;
2193 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2195 caret
= SendMessageA( descr
->owner
, WM_CHARTOITEM
,
2196 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2198 if (caret
== -2) return 0;
2201 caret
= LISTBOX_FindString( wnd
, descr
, descr
->focus_item
, str
, FALSE
);
2204 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2205 LISTBOX_SetSelection( wnd
, descr
, caret
, TRUE
, FALSE
);
2206 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2207 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2208 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2214 /***********************************************************************
2217 static BOOL
LISTBOX_Create( WND
*wnd
, LPHEADCOMBO lphc
)
2220 MEASUREITEMSTRUCT mis
;
2223 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2225 if (!(descr
->heap
= HeapCreate( 0, 0x10000, 0 )))
2227 HeapFree( GetProcessHeap(), 0, descr
);
2230 GetClientRect( wnd
->hwndSelf
, &rect
);
2231 descr
->owner
= GetParent( wnd
->hwndSelf
);
2232 descr
->style
= wnd
->dwStyle
;
2233 descr
->width
= rect
.right
- rect
.left
;
2234 descr
->height
= rect
.bottom
- rect
.top
;
2235 descr
->items
= NULL
;
2236 descr
->nb_items
= 0;
2237 descr
->top_item
= 0;
2238 descr
->selected_item
= -1;
2239 descr
->focus_item
= 0;
2240 descr
->anchor_item
= -1;
2241 descr
->item_height
= 1;
2242 descr
->page_size
= 1;
2243 descr
->column_width
= 150;
2244 descr
->horz_extent
= (wnd
->dwStyle
& WS_HSCROLL
) ? 1 : 0;
2245 descr
->horz_pos
= 0;
2248 descr
->caret_on
= TRUE
;
2249 descr
->captured
= FALSE
;
2251 descr
->locale
= 0; /* FIXME */
2254 if( ( GetExpWinVer16( wnd
->hInstance
) & 0xFF00 ) == 0x0300
2255 && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2257 /* Win95 document "List Box Differences" from MSDN:
2258 If a list box in a version 3.x application has either the
2259 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2260 horizontal and vertical scroll bars.
2262 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2267 TRACE_(combo
)("[%04x]: resetting owner %04x -> %04x\n",
2268 wnd
->hwndSelf
, descr
->owner
, lphc
->self
->hwndSelf
);
2269 descr
->owner
= lphc
->self
->hwndSelf
;
2272 *(LB_DESCR
**)wnd
->wExtra
= descr
;
2274 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2276 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2277 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2278 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2279 descr
->item_height
= LISTBOX_SetFont( wnd
, descr
, 0 );
2281 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2283 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2285 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2286 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2290 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
2292 mis
.CtlType
= ODT_LISTBOX
;
2297 mis
.itemHeight
= descr
->item_height
;
2298 SendMessageA( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2299 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2307 /***********************************************************************
2310 static BOOL
LISTBOX_Destroy( WND
*wnd
, LB_DESCR
*descr
)
2312 LISTBOX_ResetContent( wnd
, descr
);
2313 HeapDestroy( descr
->heap
);
2314 HeapFree( GetProcessHeap(), 0, descr
);
2320 /***********************************************************************
2323 static inline LRESULT WINAPI
ListBoxWndProc_locked( WND
* wnd
, UINT msg
,
2324 WPARAM wParam
, LPARAM lParam
)
2328 HWND hwnd
= wnd
->hwndSelf
;
2331 if (!(descr
= *(LB_DESCR
**)wnd
->wExtra
))
2337 if (!LISTBOX_Create( wnd
, NULL
))
2339 TRACE("creating wnd=%04x descr=%p\n",
2340 hwnd
, *(LB_DESCR
**)wnd
->wExtra
);
2346 * When a listbox is not in a combobox and the look
2347 * is win95, the WS_BORDER style is replaced with
2348 * the WS_EX_CLIENTEDGE style.
2350 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
2351 (wnd
->dwStyle
& WS_BORDER
) )
2353 wnd
->dwExStyle
|= WS_EX_CLIENTEDGE
;
2354 wnd
->dwStyle
&= ~ WS_BORDER
;
2359 /* Ignore all other messages before we get a WM_CREATE */
2360 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2363 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2364 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2367 case LB_RESETCONTENT16
:
2368 case LB_RESETCONTENT
:
2369 LISTBOX_ResetContent( wnd
, descr
);
2372 case LB_ADDSTRING16
:
2373 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2376 wParam
= LISTBOX_FindStringPos( wnd
, descr
, (LPCSTR
)lParam
, FALSE
);
2377 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2379 case LB_INSERTSTRING16
:
2380 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2381 wParam
= (INT
)(INT16
)wParam
;
2383 case LB_INSERTSTRING
:
2384 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2387 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2390 wParam
= LISTBOX_FindFileStrPos( wnd
, descr
, (LPCSTR
)lParam
);
2391 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2393 case LB_DELETESTRING16
:
2394 case LB_DELETESTRING
:
2395 if (LISTBOX_RemoveItem( wnd
, descr
, wParam
) != LB_ERR
)
2396 return descr
->nb_items
;
2400 case LB_GETITEMDATA16
:
2401 case LB_GETITEMDATA
:
2402 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2404 return descr
->items
[wParam
].data
;
2406 case LB_SETITEMDATA16
:
2407 case LB_SETITEMDATA
:
2408 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2410 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2415 return descr
->nb_items
;
2418 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2421 return LISTBOX_GetText( wnd
, descr
, wParam
, (LPSTR
)lParam
);
2423 case LB_GETTEXTLEN16
:
2426 if (wParam
>= descr
->nb_items
)
2428 return (HAS_STRINGS(descr
) ? strlen(descr
->items
[wParam
].str
)
2431 case LB_GETCURSEL16
:
2433 if (descr
->nb_items
==0)
2435 if (!IS_MULTISELECT(descr
))
2436 return descr
->selected_item
;
2438 if (descr
->selected_item
!=-1)
2439 return descr
->selected_item
;
2441 return descr
->focus_item
;
2442 /* otherwise, if the user tries to move the selection with the */
2443 /* arrow keys, we will give the application something to choke on */
2444 case LB_GETTOPINDEX16
:
2445 case LB_GETTOPINDEX
:
2446 return descr
->top_item
;
2448 case LB_GETITEMHEIGHT16
:
2449 case LB_GETITEMHEIGHT
:
2450 return LISTBOX_GetItemHeight( wnd
, descr
, wParam
);
2452 case LB_SETITEMHEIGHT16
:
2453 lParam
= LOWORD(lParam
);
2455 case LB_SETITEMHEIGHT
:
2456 return LISTBOX_SetItemHeight( wnd
, descr
, wParam
, lParam
);
2458 case LB_ITEMFROMPOINT
:
2463 pt
.x
= LOWORD(lParam
);
2464 pt
.y
= HIWORD(lParam
);
2467 rect
.right
= descr
->width
;
2468 rect
.bottom
= descr
->height
;
2470 return MAKELONG( LISTBOX_GetItemFromPoint(wnd
, descr
, pt
.x
, pt
.y
),
2471 !PtInRect( &rect
, pt
) );
2474 case LB_SETCARETINDEX16
:
2475 case LB_SETCARETINDEX
:
2476 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2477 if (LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, !lParam
) == LB_ERR
)
2484 case LB_GETCARETINDEX16
:
2485 case LB_GETCARETINDEX
:
2486 return descr
->focus_item
;
2488 case LB_SETTOPINDEX16
:
2489 case LB_SETTOPINDEX
:
2490 return LISTBOX_SetTopItem( wnd
, descr
, wParam
, TRUE
);
2492 case LB_SETCOLUMNWIDTH16
:
2493 case LB_SETCOLUMNWIDTH
:
2494 return LISTBOX_SetColumnWidth( wnd
, descr
, wParam
);
2496 case LB_GETITEMRECT16
:
2499 ret
= LISTBOX_GetItemRect( wnd
, descr
, (INT16
)wParam
, &rect
);
2500 CONV_RECT32TO16( &rect
, (RECT16
*)PTR_SEG_TO_LIN(lParam
) );
2504 case LB_GETITEMRECT
:
2505 return LISTBOX_GetItemRect( wnd
, descr
, wParam
, (RECT
*)lParam
);
2507 case LB_FINDSTRING16
:
2508 wParam
= (INT
)(INT16
)wParam
;
2509 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2512 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, FALSE
);
2514 case LB_FINDSTRINGEXACT16
:
2515 wParam
= (INT
)(INT16
)wParam
;
2516 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2518 case LB_FINDSTRINGEXACT
:
2519 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2521 case LB_SELECTSTRING16
:
2522 wParam
= (INT
)(INT16
)wParam
;
2523 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2525 case LB_SELECTSTRING
:
2527 INT index
= LISTBOX_FindString( wnd
, descr
, wParam
,
2528 (LPCSTR
)lParam
, FALSE
);
2529 if (index
== LB_ERR
)
2531 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
2536 wParam
= (INT
)(INT16
)wParam
;
2539 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2541 return descr
->items
[wParam
].selected
;
2544 lParam
= (INT
)(INT16
)lParam
;
2547 return LISTBOX_SetSelection( wnd
, descr
, lParam
, wParam
, FALSE
);
2549 case LB_SETCURSEL16
:
2550 wParam
= (INT
)(INT16
)wParam
;
2553 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2554 LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, TRUE
);
2555 return LISTBOX_SetSelection( wnd
, descr
, wParam
, TRUE
, FALSE
);
2557 case LB_GETSELCOUNT16
:
2558 case LB_GETSELCOUNT
:
2559 return LISTBOX_GetSelCount( wnd
, descr
);
2561 case LB_GETSELITEMS16
:
2562 return LISTBOX_GetSelItems16( wnd
, descr
, wParam
,
2563 (LPINT16
)PTR_SEG_TO_LIN(lParam
) );
2565 case LB_GETSELITEMS
:
2566 return LISTBOX_GetSelItems( wnd
, descr
, wParam
, (LPINT
)lParam
);
2568 case LB_SELITEMRANGE16
:
2569 case LB_SELITEMRANGE
:
2570 if (LOWORD(lParam
) <= HIWORD(lParam
))
2571 return LISTBOX_SelectItemRange( wnd
, descr
, LOWORD(lParam
),
2572 HIWORD(lParam
), wParam
);
2574 return LISTBOX_SelectItemRange( wnd
, descr
, HIWORD(lParam
),
2575 LOWORD(lParam
), wParam
);
2577 case LB_SELITEMRANGEEX16
:
2578 case LB_SELITEMRANGEEX
:
2579 if ((INT
)lParam
>= (INT
)wParam
)
2580 return LISTBOX_SelectItemRange( wnd
, descr
, wParam
, lParam
, TRUE
);
2582 return LISTBOX_SelectItemRange( wnd
, descr
, lParam
, wParam
, FALSE
);
2584 case LB_GETHORIZONTALEXTENT16
:
2585 case LB_GETHORIZONTALEXTENT
:
2586 return descr
->horz_extent
;
2588 case LB_SETHORIZONTALEXTENT16
:
2589 case LB_SETHORIZONTALEXTENT
:
2590 return LISTBOX_SetHorizontalExtent( wnd
, descr
, wParam
);
2592 case LB_GETANCHORINDEX16
:
2593 case LB_GETANCHORINDEX
:
2594 return descr
->anchor_item
;
2596 case LB_SETANCHORINDEX16
:
2597 wParam
= (INT
)(INT16
)wParam
;
2599 case LB_SETANCHORINDEX
:
2600 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2602 descr
->anchor_item
= (INT
)wParam
;
2606 return LISTBOX_Directory( wnd
, descr
, wParam
,
2607 (LPCSTR
)PTR_SEG_TO_LIN(lParam
), FALSE
);
2610 return LISTBOX_Directory( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2613 return descr
->locale
;
2616 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2619 case LB_INITSTORAGE
:
2620 return LISTBOX_InitStorage( wnd
, descr
, wParam
, (DWORD
)lParam
);
2623 return LISTBOX_SetCount( wnd
, descr
, (INT
)wParam
);
2625 case LB_SETTABSTOPS16
:
2626 return LISTBOX_SetTabStops( wnd
, descr
, (INT
)(INT16
)wParam
,
2627 (LPINT
)PTR_SEG_TO_LIN(lParam
), TRUE
);
2629 case LB_SETTABSTOPS
:
2630 return LISTBOX_SetTabStops( wnd
, descr
, wParam
, (LPINT
)lParam
, FALSE
);
2634 if (descr
->caret_on
)
2636 descr
->caret_on
= TRUE
;
2637 if ((descr
->focus_item
!= -1) && (GetFocus() == wnd
->hwndSelf
))
2638 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2643 if (!descr
->caret_on
)
2645 descr
->caret_on
= FALSE
;
2646 if ((descr
->focus_item
!= -1) && (GetFocus() == wnd
->hwndSelf
))
2647 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2650 case WM_MOUSEACTIVATE
:
2652 return MA_NOACTIVATE
;
2655 return LISTBOX_Destroy( wnd
, descr
);
2658 InvalidateRect( hwnd
, NULL
, TRUE
);
2662 LISTBOX_SetRedraw( wnd
, descr
, wParam
!= 0 );
2666 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2671 HDC hdc
= ( wParam
) ? ((HDC
)wParam
)
2672 : BeginPaint( hwnd
, &ps
);
2673 ret
= LISTBOX_Paint( wnd
, descr
, hdc
);
2674 if( !wParam
) EndPaint( hwnd
, &ps
);
2678 LISTBOX_UpdateSize( wnd
, descr
);
2683 LISTBOX_SetFont( wnd
, descr
, (HFONT
)wParam
);
2684 if (lParam
) InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
2687 descr
->caret_on
= TRUE
;
2688 if (descr
->focus_item
!= -1)
2689 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2690 SEND_NOTIFICATION( wnd
, descr
, LBN_SETFOCUS
);
2693 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2694 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2695 SEND_NOTIFICATION( wnd
, descr
, LBN_KILLFOCUS
);
2698 return LISTBOX_HandleHScroll( wnd
, descr
, wParam
, lParam
);
2700 return LISTBOX_HandleVScroll( wnd
, descr
, wParam
, lParam
);
2702 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2703 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2704 return LISTBOX_HandleMouseWheel( wnd
, descr
, wParam
, lParam
);
2705 case WM_LBUTTONDOWN
:
2706 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2707 (INT16
)LOWORD(lParam
),
2708 (INT16
)HIWORD(lParam
) );
2709 case WM_LBUTTONDBLCLK
:
2710 if (descr
->style
& LBS_NOTIFY
)
2711 SEND_NOTIFICATION( wnd
, descr
, LBN_DBLCLK
);
2714 if (GetCapture() == hwnd
)
2715 LISTBOX_HandleMouseMove( wnd
, descr
, (INT16
)LOWORD(lParam
),
2716 (INT16
)HIWORD(lParam
) );
2719 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2721 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2723 return LISTBOX_HandleChar( wnd
, descr
, wParam
);
2725 return LISTBOX_HandleSystemTimer( wnd
, descr
);
2727 if (IS_OWNERDRAW(descr
))
2730 HBRUSH hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
2731 wParam
, (LPARAM
)wnd
->hwndSelf
);
2732 GetClientRect(hwnd
, &rect
);
2733 if (hbrush
) FillRect( (HDC
)wParam
, &rect
, hbrush
);
2738 return SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2742 case WM_QUERYDROPOBJECT
:
2747 LPDRAGINFO dragInfo
= (LPDRAGINFO
)PTR_SEG_TO_LIN( (SEGPTR
)lParam
);
2748 dragInfo
->l
= LISTBOX_GetItemFromPoint( wnd
, descr
, dragInfo
->pt
.x
,
2750 return SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2755 if ((msg
>= WM_USER
) && (msg
< 0xc000))
2756 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2757 hwnd
, msg
, wParam
, lParam
);
2758 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2763 /***********************************************************************
2766 * This is just a wrapper for the real wndproc, it only does window locking
2769 LRESULT WINAPI
ListBoxWndProc( HWND hwnd
, UINT msg
,
2770 WPARAM wParam
, LPARAM lParam
)
2772 WND
* wndPtr
= WIN_FindWndPtr( hwnd
);
2773 LRESULT res
= ListBoxWndProc_locked(wndPtr
,msg
,wParam
,lParam
);
2775 WIN_ReleaseWndPtr(wndPtr
);
2779 /***********************************************************************
2782 LRESULT
COMBO_Directory( LPHEADCOMBO lphc
, UINT attrib
, LPSTR dir
, BOOL bLong
)
2784 WND
*wnd
= WIN_FindWndPtr( lphc
->hWndLBox
);
2788 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2791 LRESULT lRet
= LISTBOX_Directory( wnd
, descr
, attrib
, dir
, bLong
);
2793 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0,
2794 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
2795 WIN_ReleaseWndPtr(wnd
);
2798 WIN_ReleaseWndPtr(wnd
);
2803 /***********************************************************************
2804 * ComboLBWndProc_locked
2806 * The real combo listbox wndproc, but called with locked WND struct.
2808 static inline LRESULT WINAPI
ComboLBWndProc_locked( WND
* wnd
, UINT msg
,
2809 WPARAM wParam
, LPARAM lParam
)
2812 HWND hwnd
= wnd
->hwndSelf
;
2816 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2818 TRACE_(combo
)("[%04x]: msg %s wp %08x lp %08lx\n",
2819 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2821 if( descr
|| msg
== WM_CREATE
)
2823 LPHEADCOMBO lphc
= (descr
) ? descr
->lphc
: NULL
;
2828 #define lpcs ((LPCREATESTRUCTA)lParam)
2829 TRACE_(combo
)("\tpassed parent handle = 0x%08x\n",
2830 (UINT
)lpcs
->lpCreateParams
);
2832 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
2834 return LISTBOX_Create( wnd
, lphc
);
2836 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
2837 (CB_GETTYPE(lphc
) != CBS_SIMPLE
) )
2843 mousePos
.x
= (INT16
)LOWORD(lParam
);
2844 mousePos
.y
= (INT16
)HIWORD(lParam
);
2847 * If we are in a dropdown combobox, we simulate that
2848 * the mouse is captured to show the tracking of the item.
2850 GetClientRect(hwnd
, &clientRect
);
2852 if (PtInRect( &clientRect
, mousePos
))
2854 captured
= descr
->captured
;
2855 descr
->captured
= TRUE
;
2857 LISTBOX_HandleMouseMove( wnd
, descr
,
2858 mousePos
.x
, mousePos
.y
);
2860 descr
->captured
= captured
;
2865 LISTBOX_HandleMouseMove( wnd
, descr
,
2866 mousePos
.x
, mousePos
.y
);
2875 * If we are in Win3.1 look, go with the default behavior.
2877 return ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2880 if (TWEAK_WineLook
> WIN31_LOOK
)
2886 * If the mouse button "up" is not in the listbox,
2887 * we make sure there is no selection by re-selecting the
2888 * item that was selected when the listbox was made visible.
2890 mousePos
.x
= (INT16
)LOWORD(lParam
);
2891 mousePos
.y
= (INT16
)HIWORD(lParam
);
2893 GetClientRect(hwnd
, &clientRect
);
2896 * When the user clicks outside the combobox and the focus
2897 * is lost, the owning combobox will send a fake buttonup with
2898 * 0xFFFFFFF as the mouse location, we must also revert the
2899 * selection to the original selection.
2901 if ( (lParam
== 0xFFFFFFFF) ||
2902 (!PtInRect( &clientRect
, mousePos
)) )
2904 LISTBOX_MoveCaret( wnd
,
2910 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2911 case WM_LBUTTONDBLCLK
:
2912 case WM_LBUTTONDOWN
:
2913 return LISTBOX_HandleLButtonDownCombo(wnd
, descr
, msg
, wParam
,
2914 (INT16
)LOWORD(lParam
),
2915 (INT16
)HIWORD(lParam
) );
2916 case WM_MOUSEACTIVATE
:
2917 return MA_NOACTIVATE
;
2921 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2923 /* for some reason(?) Windows makes it possible to
2924 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2926 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
2927 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
2928 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
2930 COMBO_FlipListbox( lphc
, FALSE
);
2934 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2936 case LB_SETCURSEL16
:
2938 lRet
= ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2939 lRet
=(lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
2942 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2947 return ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2950 lRet
= DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2952 TRACE_(combo
)("\t default on msg [%04x]\n", (UINT16
)msg
);
2957 /***********************************************************************
2960 * NOTE: in Windows, winproc address of the ComboLBox is the same
2961 * as that of the Listbox.
2963 * This is just a wrapper for the real wndproc, it only does window locking
2966 LRESULT WINAPI
ComboLBWndProc( HWND hwnd
, UINT msg
,
2967 WPARAM wParam
, LPARAM lParam
)
2969 WND
*wnd
= WIN_FindWndPtr( hwnd
);
2970 LRESULT res
= ComboLBWndProc_locked(wnd
,msg
,wParam
,lParam
);
2972 WIN_ReleaseWndPtr(wnd
);