4 * Copyright 1996 Alexandre Julliard
12 #include "wine/winuser16.h"
13 #include "wine/winbase16.h"
14 #include "wine/unicode.h"
20 #include "debugtools.h"
22 DEFAULT_DEBUG_CHANNEL(listbox
);
23 DECLARE_DEBUG_CHANNEL(combo
);
29 * Probably needs improvement:
33 /* Items array granularity */
34 #define LB_ARRAY_GRANULARITY 16
36 /* Scrolling timeout in ms */
37 #define LB_SCROLL_TIMEOUT 50
39 /* Listbox system timer id */
42 /* flag listbox changed while setredraw false - internal style */
43 #define LBS_DISPLAYCHANGED 0x80000000
48 LPWSTR str
; /* Item text */
49 BOOL selected
; /* Is item selected? */
50 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
51 DWORD data
; /* User data */
54 /* Listbox structure */
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? */
77 HFONT font
; /* Current font */
78 LCID locale
; /* Current locale for string comparisons */
79 LPHEADCOMBO lphc
; /* ComboLBox */
83 #define IS_OWNERDRAW(descr) \
84 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
86 #define HAS_STRINGS(descr) \
87 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
90 #define IS_MULTISELECT(descr) \
91 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
93 #define SEND_NOTIFICATION(hwnd,descr,code) \
94 (SendMessageW( (descr)->owner, WM_COMMAND, \
95 MAKEWPARAM( GetWindowLongA((hwnd),GWL_ID), (code)), (hwnd) ))
97 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
99 /* Current timer status */
109 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
111 static LRESULT WINAPI
ComboLBWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
112 static LRESULT WINAPI
ComboLBWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
113 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
114 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
116 static LRESULT
LISTBOX_GetItemRect( LB_DESCR
*descr
, INT index
, RECT
*rect
);
118 /*********************************************************************
119 * listbox class descriptor
121 const struct builtin_class_descr LISTBOX_builtin_class
=
123 "ListBox", /* name */
124 CS_GLOBALCLASS
| CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
125 ListBoxWndProcA
, /* procA */
126 ListBoxWndProcW
, /* procW */
127 sizeof(LB_DESCR
*), /* extra */
128 IDC_ARROWA
, /* cursor */
133 /*********************************************************************
134 * combolbox class descriptor
136 const struct builtin_class_descr COMBOLBOX_builtin_class
=
138 "ComboLBox", /* name */
139 CS_GLOBALCLASS
| CS_DBLCLKS
| CS_SAVEBITS
, /* style */
140 ComboLBWndProcA
, /* procA */
141 ComboLBWndProcW
, /* procW */
142 sizeof(LB_DESCR
*), /* extra */
143 IDC_ARROWA
, /* cursor */
148 /* check whether app is a Win 3.1 app */
149 inline static BOOL
is_old_app( HWND hwnd
)
151 return (GetExpWinVer16( GetWindowLongA(hwnd
,GWL_HINSTANCE
) ) & 0xFF00 ) == 0x0300;
155 /***********************************************************************
158 void LISTBOX_Dump( HWND hwnd
)
162 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 );
164 TRACE( "Listbox:\n" );
165 TRACE( "hwnd=%04x descr=%08x items=%d top=%d\n",
166 hwnd
, (UINT
)descr
, descr
->nb_items
,
168 for (i
= 0, item
= descr
->items
; i
< descr
->nb_items
; i
++, item
++)
170 TRACE( "%4d: %-40s %d %08lx %3d\n",
171 i
, debugstr_w(item
->str
), item
->selected
, item
->data
, item
->height
);
176 /***********************************************************************
177 * LISTBOX_GetCurrentPageSize
179 * Return the current page size
181 static INT
LISTBOX_GetCurrentPageSize( LB_DESCR
*descr
)
184 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
185 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
187 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
189 if (i
== descr
->top_item
) return 1;
190 else return i
- descr
->top_item
;
194 /***********************************************************************
195 * LISTBOX_GetMaxTopIndex
197 * Return the maximum possible index for the top of the listbox.
199 static INT
LISTBOX_GetMaxTopIndex( LB_DESCR
*descr
)
203 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
205 page
= descr
->height
;
206 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
207 if ((page
-= descr
->items
[max
].height
) < 0) break;
208 if (max
< descr
->nb_items
- 1) max
++;
210 else if (descr
->style
& LBS_MULTICOLUMN
)
212 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
213 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
214 max
= (max
- page
) * descr
->page_size
;
218 max
= descr
->nb_items
- descr
->page_size
;
220 if (max
< 0) max
= 0;
225 /***********************************************************************
226 * LISTBOX_UpdateScroll
228 * Update the scrollbars. Should be called whenever the content
229 * of the listbox changes.
231 static void LISTBOX_UpdateScroll( HWND hwnd
, LB_DESCR
*descr
)
235 /* Check the listbox scroll bar flags individually before we call
236 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
237 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
238 scroll bar when we do not need one.
239 if (!(descr->style & WS_VSCROLL)) return;
242 /* It is important that we check descr->style, and not wnd->dwStyle,
243 for WS_VSCROLL, as the former is exactly the one passed in
244 argument to CreateWindow.
245 In Windows (and from now on in Wine :) a listbox created
246 with such a style (no WS_SCROLL) does not update
247 the scrollbar with listbox-related data, thus letting
248 the programmer use it for his/her own purposes. */
250 if (descr
->style
& LBS_NOREDRAW
) return;
251 info
.cbSize
= sizeof(info
);
253 if (descr
->style
& LBS_MULTICOLUMN
)
256 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
257 info
.nPos
= descr
->top_item
/ descr
->page_size
;
258 info
.nPage
= descr
->width
/ descr
->column_width
;
259 if (info
.nPage
< 1) info
.nPage
= 1;
260 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
261 if (descr
->style
& LBS_DISABLENOSCROLL
)
262 info
.fMask
|= SIF_DISABLENOSCROLL
;
263 if (descr
->style
& WS_HSCROLL
)
264 SetScrollInfo( hwnd
, SB_HORZ
, &info
, TRUE
);
266 info
.fMask
= SIF_RANGE
;
267 if (descr
->style
& WS_VSCROLL
)
268 SetScrollInfo( hwnd
, SB_VERT
, &info
, TRUE
);
273 info
.nMax
= descr
->nb_items
- 1;
274 info
.nPos
= descr
->top_item
;
275 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
276 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
277 if (descr
->style
& LBS_DISABLENOSCROLL
)
278 info
.fMask
|= SIF_DISABLENOSCROLL
;
279 if (descr
->style
& WS_VSCROLL
)
280 SetScrollInfo( hwnd
, SB_VERT
, &info
, TRUE
);
282 if (descr
->horz_extent
)
285 info
.nMax
= descr
->horz_extent
- 1;
286 info
.nPos
= descr
->horz_pos
;
287 info
.nPage
= descr
->width
;
288 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
289 if (descr
->style
& LBS_DISABLENOSCROLL
)
290 info
.fMask
|= SIF_DISABLENOSCROLL
;
291 if (descr
->style
& WS_HSCROLL
)
292 SetScrollInfo( hwnd
, SB_HORZ
, &info
, TRUE
);
298 /***********************************************************************
301 * Set the top item of the listbox, scrolling up or down if necessary.
303 static LRESULT
LISTBOX_SetTopItem( HWND hwnd
, LB_DESCR
*descr
, INT index
,
306 INT max
= LISTBOX_GetMaxTopIndex( descr
);
307 if (index
> max
) index
= max
;
308 if (index
< 0) index
= 0;
309 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
310 if (descr
->top_item
== index
) return LB_OKAY
;
311 if (descr
->style
& LBS_MULTICOLUMN
)
313 INT diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
314 if (scroll
&& (abs(diff
) < descr
->width
))
315 ScrollWindowEx( hwnd
, diff
, 0, NULL
, NULL
, 0, NULL
,
316 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
324 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
328 if (index
> descr
->top_item
)
330 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
331 diff
-= descr
->items
[i
].height
;
335 for (i
= index
; i
< descr
->top_item
; i
++)
336 diff
+= descr
->items
[i
].height
;
340 diff
= (descr
->top_item
- index
) * descr
->item_height
;
342 if (abs(diff
) < descr
->height
)
343 ScrollWindowEx( hwnd
, 0, diff
, NULL
, NULL
, 0, NULL
,
344 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
348 if (!scroll
) InvalidateRect( hwnd
, NULL
, TRUE
);
349 descr
->top_item
= index
;
350 LISTBOX_UpdateScroll( hwnd
, descr
);
355 /***********************************************************************
358 * Update the page size. Should be called when the size of
359 * the client area or the item height changes.
361 static void LISTBOX_UpdatePage( HWND hwnd
, LB_DESCR
*descr
)
365 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
367 if (page_size
== descr
->page_size
) return;
368 descr
->page_size
= page_size
;
369 if (descr
->style
& LBS_MULTICOLUMN
)
370 InvalidateRect( hwnd
, NULL
, TRUE
);
371 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
, FALSE
);
375 /***********************************************************************
378 * Update the size of the listbox. Should be called when the size of
379 * the client area changes.
381 static void LISTBOX_UpdateSize( HWND hwnd
, LB_DESCR
*descr
)
385 GetClientRect( hwnd
, &rect
);
386 descr
->width
= rect
.right
- rect
.left
;
387 descr
->height
= rect
.bottom
- rect
.top
;
388 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
393 GetWindowRect( hwnd
, &rect
);
394 if(descr
->item_height
!= 0)
395 remaining
= descr
->height
% descr
->item_height
;
398 if ((descr
->height
> descr
->item_height
) && remaining
)
400 if (is_old_app(hwnd
))
401 { /* give a margin for error to 16 bits programs - if we need
402 less than the height of the nonclient area, round to the
403 *next* number of items */
404 int ncheight
= rect
.bottom
- rect
.top
- descr
->height
;
405 if ((descr
->item_height
- remaining
) <= ncheight
)
406 remaining
= remaining
- descr
->item_height
;
408 TRACE("[%04x]: changing height %d -> %d\n",
409 hwnd
, descr
->height
, descr
->height
- remaining
);
410 SetWindowPos( hwnd
, 0, 0, 0, rect
.right
- rect
.left
,
411 rect
.bottom
- rect
.top
- remaining
,
412 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
416 TRACE("[%04x]: new size = %d,%d\n", hwnd
, descr
->width
, descr
->height
);
417 LISTBOX_UpdatePage( hwnd
, descr
);
418 LISTBOX_UpdateScroll( hwnd
, descr
);
420 /* Invalidate the focused item so it will be repainted correctly */
421 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
423 InvalidateRect( hwnd
, &rect
, FALSE
);
428 /***********************************************************************
429 * LISTBOX_GetItemRect
431 * Get the rectangle enclosing an item, in listbox client coordinates.
432 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
434 static LRESULT
LISTBOX_GetItemRect( LB_DESCR
*descr
, INT index
, RECT
*rect
)
436 /* Index <= 0 is legal even on empty listboxes */
437 if (index
&& (index
>= descr
->nb_items
)) return -1;
438 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
439 if (descr
->style
& LBS_MULTICOLUMN
)
441 INT col
= (index
/ descr
->page_size
) -
442 (descr
->top_item
/ descr
->page_size
);
443 rect
->left
+= col
* descr
->column_width
;
444 rect
->right
= rect
->left
+ descr
->column_width
;
445 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
446 rect
->bottom
= rect
->top
+ descr
->item_height
;
448 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
451 rect
->right
+= descr
->horz_pos
;
452 if ((index
>= 0) && (index
< descr
->nb_items
))
454 if (index
< descr
->top_item
)
456 for (i
= descr
->top_item
-1; i
>= index
; i
--)
457 rect
->top
-= descr
->items
[i
].height
;
461 for (i
= descr
->top_item
; i
< index
; i
++)
462 rect
->top
+= descr
->items
[i
].height
;
464 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
470 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
471 rect
->bottom
= rect
->top
+ descr
->item_height
;
472 rect
->right
+= descr
->horz_pos
;
475 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
476 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
480 /***********************************************************************
481 * LISTBOX_GetItemFromPoint
483 * Return the item nearest from point (x,y) (in client coordinates).
485 static INT
LISTBOX_GetItemFromPoint( LB_DESCR
*descr
, INT x
, INT y
)
487 INT index
= descr
->top_item
;
489 if (!descr
->nb_items
) return -1; /* No items */
490 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
495 while (index
< descr
->nb_items
)
497 if ((pos
+= descr
->items
[index
].height
) > y
) break;
506 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
510 else if (descr
->style
& LBS_MULTICOLUMN
)
512 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
513 if (y
>= 0) index
+= y
/ descr
->item_height
;
514 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
515 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
519 index
+= (y
/ descr
->item_height
);
521 if (index
< 0) return 0;
522 if (index
>= descr
->nb_items
) return -1;
527 /***********************************************************************
532 static void LISTBOX_PaintItem( HWND hwnd
, LB_DESCR
*descr
, HDC hdc
,
533 const RECT
*rect
, INT index
, UINT action
, BOOL ignoreFocus
)
535 LB_ITEMDATA
*item
= NULL
;
536 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
538 if (IS_OWNERDRAW(descr
))
543 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
547 if (action
== ODA_FOCUS
)
548 DrawFocusRect( hdc
, rect
);
550 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
554 /* some programs mess with the clipping region when
555 drawing the item, *and* restore the previous region
556 after they are done, so a region has better to exist
557 else everything ends clipped */
558 GetClientRect(hwnd
, &r
);
559 hrgn
= CreateRectRgnIndirect(&r
);
560 SelectClipRgn( hdc
, hrgn
);
561 DeleteObject( hrgn
);
563 dis
.CtlType
= ODT_LISTBOX
;
566 dis
.itemAction
= action
;
570 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
571 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
573 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
574 if (!IsWindowEnabled(hwnd
)) dis
.itemState
|= ODS_DISABLED
;
575 dis
.itemData
= item
? item
->data
: 0;
577 TRACE("[%04x]: drawitem %d (%s) action=%02x state=%02x rect=%d,%d-%d,%d\n",
578 hwnd
, index
, item
? debugstr_w(item
->str
) : "", action
,
579 dis
.itemState
, rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
580 SendMessageW(descr
->owner
, WM_DRAWITEM
, id
, (LPARAM
)&dis
);
584 COLORREF oldText
= 0, oldBk
= 0;
586 if (action
== ODA_FOCUS
)
588 DrawFocusRect( hdc
, rect
);
591 if (item
&& item
->selected
)
593 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
594 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
597 TRACE("[%04x]: painting %d (%s) action=%02x rect=%d,%d-%d,%d\n",
598 hwnd
, index
, item
? debugstr_w(item
->str
) : "", action
,
599 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
601 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
602 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
603 else if (!(descr
->style
& LBS_USETABSTOPS
))
604 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
605 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
606 strlenW(item
->str
), NULL
);
609 /* Output empty string to paint background in the full width. */
610 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
611 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
612 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
613 item
->str
, strlenW(item
->str
),
614 descr
->nb_tabs
, descr
->tabs
, 0);
616 if (item
&& item
->selected
)
618 SetBkColor( hdc
, oldBk
);
619 SetTextColor( hdc
, oldText
);
621 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
623 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
628 /***********************************************************************
631 * Change the redraw flag.
633 static void LISTBOX_SetRedraw( HWND hwnd
, LB_DESCR
*descr
, BOOL on
)
637 if (!(descr
->style
& LBS_NOREDRAW
)) return;
638 descr
->style
&= ~LBS_NOREDRAW
;
639 if (descr
->style
& LBS_DISPLAYCHANGED
)
640 { /* page was changed while setredraw false, refresh automatically */
641 InvalidateRect(hwnd
, NULL
, TRUE
);
642 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
643 { /* reset top of page if less than number of items/page */
644 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
645 if (descr
->top_item
< 0) descr
->top_item
= 0;
647 descr
->style
&= ~LBS_DISPLAYCHANGED
;
649 LISTBOX_UpdateScroll( hwnd
, descr
);
651 else descr
->style
|= LBS_NOREDRAW
;
655 /***********************************************************************
656 * LISTBOX_RepaintItem
658 * Repaint a single item synchronously.
660 static void LISTBOX_RepaintItem( HWND hwnd
, LB_DESCR
*descr
, INT index
,
666 HBRUSH hbrush
, oldBrush
= 0;
668 /* Do not repaint the item if the item is not visible */
669 if (!IsWindowVisible(hwnd
)) return;
670 if (descr
->style
& LBS_NOREDRAW
)
672 descr
->style
|= LBS_DISPLAYCHANGED
;
675 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
676 if (!(hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
))) return;
677 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
678 hbrush
= SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
680 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
681 if (!IsWindowEnabled(hwnd
))
682 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
683 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
684 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, index
, action
, FALSE
);
685 if (oldFont
) SelectObject( hdc
, oldFont
);
686 if (oldBrush
) SelectObject( hdc
, oldBrush
);
687 ReleaseDC( hwnd
, hdc
);
691 /***********************************************************************
692 * LISTBOX_InitStorage
694 static LRESULT
LISTBOX_InitStorage( HWND hwnd
, LB_DESCR
*descr
, INT nb_items
)
698 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
699 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
701 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
702 if (!(item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
703 nb_items
* sizeof(LB_ITEMDATA
) )))
705 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
713 /***********************************************************************
714 * LISTBOX_SetTabStops
716 static BOOL
LISTBOX_SetTabStops( HWND hwnd
, LB_DESCR
*descr
, INT count
,
717 LPINT tabs
, BOOL short_ints
)
719 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
720 if (descr
->tabs
) HeapFree( GetProcessHeap(), 0, descr
->tabs
);
721 if (!(descr
->nb_tabs
= count
))
726 /* FIXME: count = 1 */
727 if (!(descr
->tabs
= (INT
*)HeapAlloc( GetProcessHeap(), 0,
728 descr
->nb_tabs
* sizeof(INT
) )))
733 LPINT16 p
= (LPINT16
)tabs
;
735 TRACE("[%04x]: settabstops ", hwnd
);
736 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
737 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
738 if (TRACE_ON(listbox
)) DPRINTF("%hd ", descr
->tabs
[i
]);
740 if (TRACE_ON(listbox
)) DPRINTF("\n");
742 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
743 /* FIXME: repaint the window? */
748 /***********************************************************************
751 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPARAM lParam
, BOOL unicode
)
753 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
754 if (HAS_STRINGS(descr
))
757 return strlenW(descr
->items
[index
].str
);
759 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
763 LPWSTR buffer
= (LPWSTR
)lParam
;
764 strcpyW( buffer
, descr
->items
[index
].str
);
765 return strlenW(buffer
);
769 LPSTR buffer
= (LPSTR
)lParam
;
770 return WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1, buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
774 *((LPDWORD
)lParam
)=*(LPDWORD
)(&descr
->items
[index
].data
);
775 return sizeof(DWORD
);
780 /***********************************************************************
781 * LISTBOX_FindStringPos
783 * Find the nearest string located before a given string in sort order.
784 * If 'exact' is TRUE, return an error if we don't get an exact match.
786 static INT
LISTBOX_FindStringPos( HWND hwnd
, LB_DESCR
*descr
, LPCWSTR str
,
789 INT index
, min
, max
, res
= -1;
791 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
793 max
= descr
->nb_items
;
796 index
= (min
+ max
) / 2;
797 if (HAS_STRINGS(descr
))
798 res
= lstrcmpiW( descr
->items
[index
].str
, str
);
801 COMPAREITEMSTRUCT cis
;
802 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
804 cis
.CtlType
= ODT_LISTBOX
;
808 cis
.itemData1
= descr
->items
[index
].data
;
810 cis
.itemData2
= (DWORD
)str
;
811 cis
.dwLocaleId
= descr
->locale
;
812 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
814 if (!res
) return index
;
815 if (res
> 0) max
= index
;
816 else min
= index
+ 1;
818 return exact
? -1 : max
;
822 /***********************************************************************
823 * LISTBOX_FindFileStrPos
825 * Find the nearest string located before a given string in directory
826 * sort order (i.e. first files, then directories, then drives).
828 static INT
LISTBOX_FindFileStrPos( HWND hwnd
, LB_DESCR
*descr
, LPCWSTR str
)
830 INT min
, max
, res
= -1;
832 if (!HAS_STRINGS(descr
))
833 return LISTBOX_FindStringPos( hwnd
, descr
, str
, FALSE
);
835 max
= descr
->nb_items
;
838 INT index
= (min
+ max
) / 2;
839 LPCWSTR p
= descr
->items
[index
].str
;
840 if (*p
== '[') /* drive or directory */
842 if (*str
!= '[') res
= -1;
843 else if (p
[1] == '-') /* drive */
845 if (str
[1] == '-') res
= str
[2] - p
[2];
850 if (str
[1] == '-') res
= 1;
851 else res
= lstrcmpiW( str
, p
);
856 if (*str
== '[') res
= 1;
857 else res
= lstrcmpiW( str
, p
);
859 if (!res
) return index
;
860 if (res
< 0) max
= index
;
861 else min
= index
+ 1;
867 /***********************************************************************
870 * Find the item beginning with a given string.
872 static INT
LISTBOX_FindString( HWND hwnd
, LB_DESCR
*descr
, INT start
,
873 LPCWSTR str
, BOOL exact
)
878 if (start
>= descr
->nb_items
) start
= -1;
879 item
= descr
->items
+ start
+ 1;
880 if (HAS_STRINGS(descr
))
882 if (!str
|| ! str
[0] ) return LB_ERR
;
885 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
886 if (!lstrcmpiW( str
, item
->str
)) return i
;
887 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
888 if (!lstrcmpiW( str
, item
->str
)) return i
;
892 /* Special case for drives and directories: ignore prefix */
893 #define CHECK_DRIVE(item) \
894 if ((item)->str[0] == '[') \
896 if (!strncmpiW( str, (item)->str+1, len )) return i; \
897 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
901 INT len
= strlenW(str
);
902 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
904 if (!strncmpiW( str
, item
->str
, len
)) return i
;
907 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
909 if (!strncmpiW( str
, item
->str
, len
)) return i
;
917 if (exact
&& (descr
->style
& LBS_SORT
))
918 /* If sorted, use a WM_COMPAREITEM binary search */
919 return LISTBOX_FindStringPos( hwnd
, descr
, str
, TRUE
);
921 /* Otherwise use a linear search */
922 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
923 if (item
->data
== (DWORD
)str
) return i
;
924 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
925 if (item
->data
== (DWORD
)str
) return i
;
931 /***********************************************************************
932 * LISTBOX_GetSelCount
934 static LRESULT
LISTBOX_GetSelCount( LB_DESCR
*descr
)
937 LB_ITEMDATA
*item
= descr
->items
;
939 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
940 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
941 if (item
->selected
) count
++;
946 /***********************************************************************
947 * LISTBOX_GetSelItems16
949 static LRESULT
LISTBOX_GetSelItems16( LB_DESCR
*descr
, INT16 max
, LPINT16 array
)
952 LB_ITEMDATA
*item
= descr
->items
;
954 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
955 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
956 if (item
->selected
) array
[count
++] = (INT16
)i
;
961 /***********************************************************************
962 * LISTBOX_GetSelItems
964 static LRESULT
LISTBOX_GetSelItems( LB_DESCR
*descr
, INT max
, LPINT array
)
967 LB_ITEMDATA
*item
= descr
->items
;
969 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
970 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
971 if (item
->selected
) array
[count
++] = i
;
976 /***********************************************************************
979 static LRESULT
LISTBOX_Paint( HWND hwnd
, LB_DESCR
*descr
, HDC hdc
)
981 INT i
, col_pos
= descr
->page_size
- 1;
983 RECT focusRect
= {-1, -1, -1, -1};
985 HBRUSH hbrush
, oldBrush
= 0;
987 if (descr
->style
& LBS_NOREDRAW
) return 0;
989 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
990 if (descr
->style
& LBS_MULTICOLUMN
)
991 rect
.right
= rect
.left
+ descr
->column_width
;
992 else if (descr
->horz_pos
)
994 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
995 rect
.right
+= descr
->horz_pos
;
998 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
999 hbrush
= SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
1000 hdc
, (LPARAM
)hwnd
);
1001 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1002 if (!IsWindowEnabled(hwnd
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1004 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1007 /* Special case for empty listbox: paint focus rect */
1008 rect
.bottom
= rect
.top
+ descr
->item_height
;
1009 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, descr
->focus_item
,
1011 rect
.top
= rect
.bottom
;
1014 /* Paint all the item, regarding the selection
1015 Focus state will be painted after */
1017 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1019 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1020 rect
.bottom
= rect
.top
+ descr
->item_height
;
1022 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1024 if (i
== descr
->focus_item
)
1026 /* keep the focus rect, to paint the focus item after */
1027 focusRect
.left
= rect
.left
;
1028 focusRect
.right
= rect
.right
;
1029 focusRect
.top
= rect
.top
;
1030 focusRect
.bottom
= rect
.bottom
;
1032 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1033 rect
.top
= rect
.bottom
;
1035 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1037 if (!IS_OWNERDRAW(descr
))
1039 /* Clear the bottom of the column */
1040 if (rect
.top
< descr
->height
)
1042 rect
.bottom
= descr
->height
;
1043 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1044 &rect
, NULL
, 0, NULL
);
1048 /* Go to the next column */
1049 rect
.left
+= descr
->column_width
;
1050 rect
.right
+= descr
->column_width
;
1052 col_pos
= descr
->page_size
- 1;
1057 if (rect
.top
>= descr
->height
) break;
1061 /* Paint the focus item now */
1062 if (focusRect
.top
!= focusRect
.bottom
&& descr
->caret_on
)
1063 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1065 if (!IS_OWNERDRAW(descr
))
1067 /* Clear the remainder of the client area */
1068 if (rect
.top
< descr
->height
)
1070 rect
.bottom
= descr
->height
;
1071 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1072 &rect
, NULL
, 0, NULL
);
1074 if (rect
.right
< descr
->width
)
1076 rect
.left
= rect
.right
;
1077 rect
.right
= descr
->width
;
1079 rect
.bottom
= descr
->height
;
1080 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1081 &rect
, NULL
, 0, NULL
);
1084 if (oldFont
) SelectObject( hdc
, oldFont
);
1085 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1090 /***********************************************************************
1091 * LISTBOX_InvalidateItems
1093 * Invalidate all items from a given item. If the specified item is not
1094 * visible, nothing happens.
1096 static void LISTBOX_InvalidateItems( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1100 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1102 if (descr
->style
& LBS_NOREDRAW
)
1104 descr
->style
|= LBS_DISPLAYCHANGED
;
1107 rect
.bottom
= descr
->height
;
1108 InvalidateRect( hwnd
, &rect
, TRUE
);
1109 if (descr
->style
& LBS_MULTICOLUMN
)
1111 /* Repaint the other columns */
1112 rect
.left
= rect
.right
;
1113 rect
.right
= descr
->width
;
1115 InvalidateRect( hwnd
, &rect
, TRUE
);
1121 /***********************************************************************
1122 * LISTBOX_GetItemHeight
1124 static LRESULT
LISTBOX_GetItemHeight( LB_DESCR
*descr
, INT index
)
1126 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1128 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1129 return descr
->items
[index
].height
;
1131 else return descr
->item_height
;
1135 /***********************************************************************
1136 * LISTBOX_SetItemHeight
1138 static LRESULT
LISTBOX_SetItemHeight( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1141 if (!height
) height
= 1;
1143 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1145 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1146 TRACE("[%04x]: item %d height = %d\n", hwnd
, index
, height
);
1147 descr
->items
[index
].height
= height
;
1148 LISTBOX_UpdateScroll( hwnd
, descr
);
1149 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1151 else if (height
!= descr
->item_height
)
1153 TRACE("[%04x]: new height = %d\n", hwnd
, height
);
1154 descr
->item_height
= height
;
1155 LISTBOX_UpdatePage( hwnd
, descr
);
1156 LISTBOX_UpdateScroll( hwnd
, descr
);
1157 InvalidateRect( hwnd
, 0, TRUE
);
1163 /***********************************************************************
1164 * LISTBOX_SetHorizontalPos
1166 static void LISTBOX_SetHorizontalPos( HWND hwnd
, LB_DESCR
*descr
, INT pos
)
1170 if (pos
> descr
->horz_extent
- descr
->width
)
1171 pos
= descr
->horz_extent
- descr
->width
;
1172 if (pos
< 0) pos
= 0;
1173 if (!(diff
= descr
->horz_pos
- pos
)) return;
1174 TRACE("[%04x]: new horz pos = %d\n", hwnd
, pos
);
1175 descr
->horz_pos
= pos
;
1176 LISTBOX_UpdateScroll( hwnd
, descr
);
1177 if (abs(diff
) < descr
->width
)
1178 ScrollWindowEx( hwnd
, diff
, 0, NULL
, NULL
, 0, NULL
,
1179 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1181 InvalidateRect( hwnd
, NULL
, TRUE
);
1185 /***********************************************************************
1186 * LISTBOX_SetHorizontalExtent
1188 static LRESULT
LISTBOX_SetHorizontalExtent( HWND hwnd
, LB_DESCR
*descr
,
1191 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1193 if (extent
<= 0) extent
= 1;
1194 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1195 TRACE("[%04x]: new horz extent = %d\n", hwnd
, extent
);
1196 descr
->horz_extent
= extent
;
1197 if (descr
->horz_pos
> extent
- descr
->width
)
1198 LISTBOX_SetHorizontalPos( hwnd
, descr
, extent
- descr
->width
);
1200 LISTBOX_UpdateScroll( hwnd
, descr
);
1205 /***********************************************************************
1206 * LISTBOX_SetColumnWidth
1208 static LRESULT
LISTBOX_SetColumnWidth( HWND hwnd
, LB_DESCR
*descr
, INT width
)
1210 if (width
== descr
->column_width
) return LB_OKAY
;
1211 TRACE("[%04x]: new column width = %d\n", hwnd
, width
);
1212 descr
->column_width
= width
;
1213 LISTBOX_UpdatePage( hwnd
, descr
);
1218 /***********************************************************************
1221 * Returns the item height.
1223 static INT
LISTBOX_SetFont( HWND hwnd
, LB_DESCR
*descr
, HFONT font
)
1231 if (!(hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
)))
1233 ERR("unable to get DC.\n" );
1236 if (font
) oldFont
= SelectObject( hdc
, font
);
1237 GetTextMetricsW( hdc
, &tm
);
1238 if (oldFont
) SelectObject( hdc
, oldFont
);
1239 ReleaseDC( hwnd
, hdc
);
1240 if (!IS_OWNERDRAW(descr
))
1241 LISTBOX_SetItemHeight( hwnd
, descr
, 0, tm
.tmHeight
);
1242 return tm
.tmHeight
;
1246 /***********************************************************************
1247 * LISTBOX_MakeItemVisible
1249 * Make sure that a given item is partially or fully visible.
1251 static void LISTBOX_MakeItemVisible( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1256 if (index
<= descr
->top_item
) top
= index
;
1257 else if (descr
->style
& LBS_MULTICOLUMN
)
1259 INT cols
= descr
->width
;
1260 if (!fully
) cols
+= descr
->column_width
- 1;
1261 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1263 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1264 top
= index
- descr
->page_size
* (cols
- 1);
1266 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1268 INT height
= fully
? descr
->items
[index
].height
: 1;
1269 for (top
= index
; top
> descr
->top_item
; top
--)
1270 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1274 if (index
< descr
->top_item
+ descr
->page_size
) return;
1275 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1276 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1277 top
= index
- descr
->page_size
+ 1;
1279 LISTBOX_SetTopItem( hwnd
, descr
, top
, TRUE
);
1282 /***********************************************************************
1283 * LISTBOX_SetCaretIndex
1286 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1289 static LRESULT
LISTBOX_SetCaretIndex( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1290 BOOL fully_visible
)
1292 INT oldfocus
= descr
->focus_item
;
1294 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1295 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1296 if (index
== oldfocus
) return LB_OKAY
;
1297 descr
->focus_item
= index
;
1298 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1299 LISTBOX_RepaintItem( hwnd
, descr
, oldfocus
, ODA_FOCUS
);
1301 LISTBOX_MakeItemVisible( hwnd
, descr
, index
, fully_visible
);
1302 if (descr
->caret_on
&& (descr
->in_focus
))
1303 LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_FOCUS
);
1309 /***********************************************************************
1310 * LISTBOX_SelectItemRange
1312 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1314 static LRESULT
LISTBOX_SelectItemRange( HWND hwnd
, LB_DESCR
*descr
, INT first
,
1319 /* A few sanity checks */
1321 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1322 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1323 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1324 if (last
== -1) last
= descr
->nb_items
- 1;
1325 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1326 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1327 /* selected_item reflects last selected/unselected item on multiple sel */
1328 descr
->selected_item
= last
;
1330 if (on
) /* Turn selection on */
1332 for (i
= first
; i
<= last
; i
++)
1334 if (descr
->items
[i
].selected
) continue;
1335 descr
->items
[i
].selected
= TRUE
;
1336 LISTBOX_RepaintItem( hwnd
, descr
, i
, ODA_SELECT
);
1338 LISTBOX_SetCaretIndex( hwnd
, descr
, last
, TRUE
);
1340 else /* Turn selection off */
1342 for (i
= first
; i
<= last
; i
++)
1344 if (!descr
->items
[i
].selected
) continue;
1345 descr
->items
[i
].selected
= FALSE
;
1346 LISTBOX_RepaintItem( hwnd
, descr
, i
, ODA_SELECT
);
1352 /***********************************************************************
1353 * LISTBOX_SetSelection
1355 static LRESULT
LISTBOX_SetSelection( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1356 BOOL on
, BOOL send_notify
)
1358 TRACE( "index=%d notify=%s\n", index
, send_notify
? "YES" : "NO" );
1360 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1361 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1362 if (descr
->style
& LBS_MULTIPLESEL
)
1364 if (index
== -1) /* Select all items */
1365 return LISTBOX_SelectItemRange( hwnd
, descr
, 0, -1, on
);
1366 else /* Only one item */
1367 return LISTBOX_SelectItemRange( hwnd
, descr
, index
, index
, on
);
1371 INT oldsel
= descr
->selected_item
;
1372 if (index
== oldsel
) return LB_OKAY
;
1373 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1374 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1375 descr
->selected_item
= index
;
1376 if (oldsel
!= -1) LISTBOX_RepaintItem( hwnd
, descr
, oldsel
, ODA_SELECT
);
1377 if (index
!= -1) LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_SELECT
);
1378 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( hwnd
, descr
,
1379 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1381 if( descr
->lphc
) /* set selection change flag for parent combo */
1382 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1388 /***********************************************************************
1391 * Change the caret position and extend the selection to the new caret.
1393 static void LISTBOX_MoveCaret( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1394 BOOL fully_visible
)
1396 INT oldfocus
= descr
->focus_item
;
1398 if ((index
< 0) || (index
>= descr
->nb_items
))
1401 /* Important, repaint needs to be done in this order if
1402 you want to mimic Windows behavior:
1403 1. Remove the focus and paint the item
1404 2. Remove the selection and paint the item(s)
1405 3. Set the selection and repaint the item(s)
1406 4. Set the focus to 'index' and repaint the item */
1408 /* 1. remove the focus and repaint the item */
1409 descr
->focus_item
= -1;
1410 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1411 LISTBOX_RepaintItem( hwnd
, descr
, oldfocus
, ODA_FOCUS
);
1413 /* 2. then turn off the previous selection */
1414 /* 3. repaint the new selected item */
1415 if (descr
->style
& LBS_EXTENDEDSEL
)
1417 if (descr
->anchor_item
!= -1)
1419 INT first
= min( index
, descr
->anchor_item
);
1420 INT last
= max( index
, descr
->anchor_item
);
1422 LISTBOX_SelectItemRange( hwnd
, descr
, 0, first
- 1, FALSE
);
1423 LISTBOX_SelectItemRange( hwnd
, descr
, last
+ 1, -1, FALSE
);
1424 LISTBOX_SelectItemRange( hwnd
, descr
, first
, last
, TRUE
);
1427 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1429 /* Set selection to new caret item */
1430 LISTBOX_SetSelection( hwnd
, descr
, index
, TRUE
, FALSE
);
1433 /* 4. repaint the new item with the focus */
1434 descr
->focus_item
= index
;
1435 LISTBOX_MakeItemVisible( hwnd
, descr
, index
, fully_visible
);
1436 if (descr
->caret_on
&& (descr
->in_focus
))
1437 LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_FOCUS
);
1441 /***********************************************************************
1442 * LISTBOX_InsertItem
1444 static LRESULT
LISTBOX_InsertItem( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1445 LPWSTR str
, DWORD data
)
1449 INT oldfocus
= descr
->focus_item
;
1451 if (index
== -1) index
= descr
->nb_items
;
1452 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1453 if (!descr
->items
) max_items
= 0;
1454 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1455 if (descr
->nb_items
== max_items
)
1457 /* We need to grow the array */
1458 max_items
+= LB_ARRAY_GRANULARITY
;
1459 if (!(item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1460 max_items
* sizeof(LB_ITEMDATA
) )))
1462 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
1465 descr
->items
= item
;
1468 /* Insert the item structure */
1470 item
= &descr
->items
[index
];
1471 if (index
< descr
->nb_items
)
1472 RtlMoveMemory( item
+ 1, item
,
1473 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1477 item
->selected
= FALSE
;
1480 /* Get item height */
1482 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1484 MEASUREITEMSTRUCT mis
;
1485 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
1487 mis
.CtlType
= ODT_LISTBOX
;
1490 mis
.itemData
= descr
->items
[index
].data
;
1491 mis
.itemHeight
= descr
->item_height
;
1492 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1493 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1494 TRACE("[%04x]: measure item %d (%s) = %d\n",
1495 hwnd
, index
, str
? debugstr_w(str
) : "", item
->height
);
1498 /* Repaint the items */
1500 LISTBOX_UpdateScroll( hwnd
, descr
);
1501 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1503 /* Move selection and focused item */
1504 /* If listbox was empty, set focus to the first item */
1505 if (descr
->nb_items
== 1)
1506 LISTBOX_SetCaretIndex( hwnd
, descr
, 0, FALSE
);
1507 /* single select don't change selection index in win31 */
1508 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1510 descr
->selected_item
++;
1511 LISTBOX_SetSelection( hwnd
, descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1515 if (index
<= descr
->selected_item
)
1517 descr
->selected_item
++;
1518 descr
->focus_item
= oldfocus
; /* focus not changed */
1525 /***********************************************************************
1526 * LISTBOX_InsertString
1528 static LRESULT
LISTBOX_InsertString( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1531 LPWSTR new_str
= NULL
;
1535 if (HAS_STRINGS(descr
))
1537 static const WCHAR empty_stringW
[] = { 0 };
1538 if (!str
) str
= empty_stringW
;
1539 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1541 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
1544 strcpyW(new_str
, str
);
1546 else data
= (DWORD
)str
;
1548 if (index
== -1) index
= descr
->nb_items
;
1549 if ((ret
= LISTBOX_InsertItem( hwnd
, descr
, index
, new_str
, data
)) != 0)
1551 if (new_str
) HeapFree( GetProcessHeap(), 0, new_str
);
1555 TRACE("[%04x]: added item %d %s\n",
1556 hwnd
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1561 /***********************************************************************
1562 * LISTBOX_DeleteItem
1564 * Delete the content of an item. 'index' must be a valid index.
1566 static void LISTBOX_DeleteItem( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1568 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1569 * while Win95 sends it for all items with user data.
1570 * It's probably better to send it too often than not
1571 * often enough, so this is what we do here.
1573 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1575 DELETEITEMSTRUCT dis
;
1576 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
1578 dis
.CtlType
= ODT_LISTBOX
;
1581 dis
.hwndItem
= hwnd
;
1582 dis
.itemData
= descr
->items
[index
].data
;
1583 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1585 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1586 HeapFree( GetProcessHeap(), 0, descr
->items
[index
].str
);
1590 /***********************************************************************
1591 * LISTBOX_RemoveItem
1593 * Remove an item from the listbox and delete its content.
1595 static LRESULT
LISTBOX_RemoveItem( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1600 if ((index
== -1) && (descr
->nb_items
> 0)) index
= descr
->nb_items
- 1;
1601 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1603 /* We need to invalidate the original rect instead of the updated one. */
1604 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1606 LISTBOX_DeleteItem( hwnd
, descr
, index
);
1608 /* Remove the item */
1610 item
= &descr
->items
[index
];
1611 if (index
< descr
->nb_items
-1)
1612 RtlMoveMemory( item
, item
+ 1,
1613 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1615 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1617 /* Shrink the item array if possible */
1619 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1620 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1622 max_items
-= LB_ARRAY_GRANULARITY
;
1623 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1624 max_items
* sizeof(LB_ITEMDATA
) );
1625 if (item
) descr
->items
= item
;
1627 /* Repaint the items */
1629 LISTBOX_UpdateScroll( hwnd
, descr
);
1630 /* if we removed the scrollbar, reset the top of the list
1631 (correct for owner-drawn ???) */
1632 if (descr
->nb_items
== descr
->page_size
)
1633 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1635 /* Move selection and focused item */
1636 if (!IS_MULTISELECT(descr
))
1638 if (index
== descr
->selected_item
)
1639 descr
->selected_item
= -1;
1640 else if (index
< descr
->selected_item
)
1642 descr
->selected_item
--;
1643 if (ISWIN31
) /* win 31 do not change the selected item number */
1644 LISTBOX_SetSelection( hwnd
, descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1648 if (descr
->focus_item
>= descr
->nb_items
)
1650 descr
->focus_item
= descr
->nb_items
- 1;
1651 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1657 /***********************************************************************
1658 * LISTBOX_ResetContent
1660 static void LISTBOX_ResetContent( HWND hwnd
, LB_DESCR
*descr
)
1664 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( hwnd
, descr
, i
);
1665 if (descr
->items
) HeapFree( GetProcessHeap(), 0, descr
->items
);
1666 descr
->nb_items
= 0;
1667 descr
->top_item
= 0;
1668 descr
->selected_item
= -1;
1669 descr
->focus_item
= 0;
1670 descr
->anchor_item
= -1;
1671 descr
->items
= NULL
;
1675 /***********************************************************************
1678 static LRESULT
LISTBOX_SetCount( HWND hwnd
, LB_DESCR
*descr
, INT count
)
1682 if (HAS_STRINGS(descr
)) return LB_ERR
;
1683 /* FIXME: this is far from optimal... */
1684 if (count
> descr
->nb_items
)
1686 while (count
> descr
->nb_items
)
1687 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, -1, 0 )) < 0)
1690 else if (count
< descr
->nb_items
)
1692 while (count
< descr
->nb_items
)
1693 if ((ret
= LISTBOX_RemoveItem( hwnd
, descr
, -1 )) < 0)
1700 /***********************************************************************
1703 static LRESULT
LISTBOX_Directory( HWND hwnd
, LB_DESCR
*descr
, UINT attrib
,
1704 LPCWSTR filespec
, BOOL long_names
)
1707 LRESULT ret
= LB_OKAY
;
1708 WIN32_FIND_DATAW entry
;
1711 /* don't scan directory if we just want drives exclusively */
1712 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1713 /* scan directory */
1714 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1716 if (GetLastError() != ERROR_NO_MORE_FILES
) return LB_ERR
;
1723 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1725 static const WCHAR bracketW
[] = { ']',0 };
1726 static const WCHAR dotW
[] = { '.',0 };
1727 if (!(attrib
& DDL_DIRECTORY
) ||
1728 !strcmpW( entry
.cAlternateFileName
, dotW
)) continue;
1730 if (long_names
) strcpyW( buffer
+ 1, entry
.cFileName
);
1731 else strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1732 strcatW(buffer
, bracketW
);
1734 else /* not a directory */
1736 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1737 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1739 if ((attrib
& DDL_EXCLUSIVE
) &&
1740 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1743 if (long_names
) strcpyW( buffer
, entry
.cFileName
);
1744 else strcpyW( buffer
, entry
.cAlternateFileName
);
1746 if (!long_names
) CharLowerW( buffer
);
1747 pos
= LISTBOX_FindFileStrPos( hwnd
, descr
, buffer
);
1748 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, pos
, buffer
)) < 0)
1750 } while (FindNextFileW( handle
, &entry
));
1751 FindClose( handle
);
1756 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1758 WCHAR buffer
[] = {'[','-','a','-',']',0};
1759 WCHAR root
[] = {'A',':','\\',0};
1761 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1763 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1764 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, -1, buffer
)) < 0)
1772 /***********************************************************************
1773 * LISTBOX_HandleVScroll
1775 static LRESULT
LISTBOX_HandleVScroll( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1779 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1780 switch(LOWORD(wParam
))
1783 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
- 1, TRUE
);
1786 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+ 1, TRUE
);
1789 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
-
1790 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1793 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+
1794 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1796 case SB_THUMBPOSITION
:
1797 LISTBOX_SetTopItem( hwnd
, descr
, HIWORD(wParam
), TRUE
);
1800 info
.cbSize
= sizeof(info
);
1801 info
.fMask
= SIF_TRACKPOS
;
1802 GetScrollInfo( hwnd
, SB_VERT
, &info
);
1803 LISTBOX_SetTopItem( hwnd
, descr
, info
.nTrackPos
, TRUE
);
1806 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1809 LISTBOX_SetTopItem( hwnd
, descr
, descr
->nb_items
, TRUE
);
1816 /***********************************************************************
1817 * LISTBOX_HandleHScroll
1819 static LRESULT
LISTBOX_HandleHScroll( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1824 if (descr
->style
& LBS_MULTICOLUMN
)
1826 switch(LOWORD(wParam
))
1829 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
-descr
->page_size
,
1833 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+descr
->page_size
,
1837 page
= descr
->width
/ descr
->column_width
;
1838 if (page
< 1) page
= 1;
1839 LISTBOX_SetTopItem( hwnd
, descr
,
1840 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1843 page
= descr
->width
/ descr
->column_width
;
1844 if (page
< 1) page
= 1;
1845 LISTBOX_SetTopItem( hwnd
, descr
,
1846 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1848 case SB_THUMBPOSITION
:
1849 LISTBOX_SetTopItem( hwnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1853 info
.cbSize
= sizeof(info
);
1854 info
.fMask
= SIF_TRACKPOS
;
1855 GetScrollInfo( hwnd
, SB_VERT
, &info
);
1856 LISTBOX_SetTopItem( hwnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1860 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1863 LISTBOX_SetTopItem( hwnd
, descr
, descr
->nb_items
, TRUE
);
1867 else if (descr
->horz_extent
)
1869 switch(LOWORD(wParam
))
1872 LISTBOX_SetHorizontalPos( hwnd
, descr
, descr
->horz_pos
- 1 );
1875 LISTBOX_SetHorizontalPos( hwnd
, descr
, descr
->horz_pos
+ 1 );
1878 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1879 descr
->horz_pos
- descr
->width
);
1882 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1883 descr
->horz_pos
+ descr
->width
);
1885 case SB_THUMBPOSITION
:
1886 LISTBOX_SetHorizontalPos( hwnd
, descr
, HIWORD(wParam
) );
1889 info
.cbSize
= sizeof(info
);
1890 info
.fMask
= SIF_TRACKPOS
;
1891 GetScrollInfo( hwnd
, SB_HORZ
, &info
);
1892 LISTBOX_SetHorizontalPos( hwnd
, descr
, info
.nTrackPos
);
1895 LISTBOX_SetHorizontalPos( hwnd
, descr
, 0 );
1898 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1899 descr
->horz_extent
- descr
->width
);
1906 static LRESULT
LISTBOX_HandleMouseWheel(HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1908 short gcWheelDelta
= 0;
1909 UINT pulScrollLines
= 3;
1911 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1913 gcWheelDelta
-= (short) HIWORD(wParam
);
1915 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
1917 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
1918 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
1919 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+ cLineScroll
, TRUE
);
1924 /***********************************************************************
1925 * LISTBOX_HandleLButtonDown
1927 static LRESULT
LISTBOX_HandleLButtonDown( HWND hwnd
, LB_DESCR
*descr
,
1928 WPARAM wParam
, INT x
, INT y
)
1930 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
1931 TRACE("[%04x]: lbuttondown %d,%d item %d\n", hwnd
, x
, y
, index
);
1932 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
1934 if (!descr
->in_focus
)
1936 if( !descr
->lphc
) SetFocus( hwnd
);
1937 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
1940 if (index
== -1) return 0;
1942 if (descr
->style
& LBS_EXTENDEDSEL
)
1944 /* we should perhaps make sure that all items are deselected
1945 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1946 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1947 LISTBOX_SetSelection( hwnd, descr, -1, FALSE, FALSE);
1950 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1951 if (wParam
& MK_CONTROL
)
1953 LISTBOX_SetCaretIndex( hwnd
, descr
, index
, FALSE
);
1954 LISTBOX_SetSelection( hwnd
, descr
, index
,
1955 !descr
->items
[index
].selected
,
1956 (descr
->style
& LBS_NOTIFY
) != 0);
1958 else LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
1962 LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
1963 LISTBOX_SetSelection( hwnd
, descr
, index
,
1964 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1965 !descr
->items
[index
].selected
),
1966 (descr
->style
& LBS_NOTIFY
) != 0 );
1969 descr
->captured
= TRUE
;
1974 if (descr
->style
& LBS_NOTIFY
)
1975 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
1976 MAKELPARAM( x
, y
) );
1977 if (GetWindowLongA( hwnd
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
1984 if (DragDetect( hwnd
, pt
))
1985 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
1992 /*************************************************************************
1993 * LISTBOX_HandleLButtonDownCombo [Internal]
1995 * Process LButtonDown message for the ComboListBox
1998 * pWnd [I] The windows internal structure
1999 * pDescr [I] The ListBox internal structure
2000 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2001 * x [I] X Mouse Coordinate
2002 * y [I] Y Mouse Coordinate
2005 * 0 since we are processing the WM_LBUTTONDOWN Message
2008 * This function is only to be used when a ListBox is a ComboListBox
2011 static LRESULT
LISTBOX_HandleLButtonDownCombo( HWND hwnd
, LB_DESCR
*pDescr
,
2012 UINT msg
, WPARAM wParam
, INT x
, INT y
)
2014 RECT clientRect
, screenRect
;
2020 GetClientRect(hwnd
, &clientRect
);
2022 if(PtInRect(&clientRect
, mousePos
))
2024 /* MousePos is in client, resume normal processing */
2025 if (msg
== WM_LBUTTONDOWN
)
2027 pDescr
->lphc
->droppedIndex
= pDescr
->nb_items
? pDescr
->selected_item
: -1;
2028 return LISTBOX_HandleLButtonDown( hwnd
, pDescr
, wParam
, x
, y
);
2030 else if (pDescr
->style
& LBS_NOTIFY
)
2031 SEND_NOTIFICATION( hwnd
, pDescr
, LBN_DBLCLK
);
2036 POINT screenMousePos
;
2037 HWND hWndOldCapture
;
2039 /* Check the Non-Client Area */
2040 screenMousePos
= mousePos
;
2041 hWndOldCapture
= GetCapture();
2043 GetWindowRect(hwnd
, &screenRect
);
2044 ClientToScreen(hwnd
, &screenMousePos
);
2046 if(!PtInRect(&screenRect
, screenMousePos
))
2048 LISTBOX_SetCaretIndex( hwnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
);
2049 LISTBOX_SetSelection( hwnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2050 COMBO_FlipListbox( pDescr
->lphc
, FALSE
, FALSE
);
2055 /* Check to see the NC is a scrollbar */
2057 LONG style
= GetWindowLongA( hwnd
, GWL_STYLE
);
2058 /* Check Vertical scroll bar */
2059 if (style
& WS_VSCROLL
)
2061 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2062 if (PtInRect( &clientRect
, mousePos
))
2064 nHitTestType
= HTVSCROLL
;
2067 /* Check horizontal scroll bar */
2068 if (style
& WS_HSCROLL
)
2070 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2071 if (PtInRect( &clientRect
, mousePos
))
2073 nHitTestType
= HTHSCROLL
;
2076 /* Windows sends this message when a scrollbar is clicked
2079 if(nHitTestType
!= 0)
2081 SendMessageW(hwnd
, WM_NCLBUTTONDOWN
, nHitTestType
,
2082 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2084 /* Resume the Capture after scrolling is complete
2086 if(hWndOldCapture
!= 0)
2088 SetCapture(hWndOldCapture
);
2095 /***********************************************************************
2096 * LISTBOX_HandleLButtonUp
2098 static LRESULT
LISTBOX_HandleLButtonUp( HWND hwnd
, LB_DESCR
*descr
)
2100 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2101 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2102 LISTBOX_Timer
= LB_TIMER_NONE
;
2103 if (descr
->captured
)
2105 descr
->captured
= FALSE
;
2106 if (GetCapture() == hwnd
) ReleaseCapture();
2107 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2108 SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2114 /***********************************************************************
2115 * LISTBOX_HandleTimer
2117 * Handle scrolling upon a timer event.
2118 * Return TRUE if scrolling should continue.
2120 static LRESULT
LISTBOX_HandleTimer( HWND hwnd
, LB_DESCR
*descr
,
2121 INT index
, TIMER_DIRECTION dir
)
2126 if (descr
->top_item
) index
= descr
->top_item
- 1;
2130 if (descr
->top_item
) index
-= descr
->page_size
;
2133 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2134 if (index
== descr
->focus_item
) index
++;
2135 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2137 case LB_TIMER_RIGHT
:
2138 if (index
+ descr
->page_size
< descr
->nb_items
)
2139 index
+= descr
->page_size
;
2144 if (index
== descr
->focus_item
) return FALSE
;
2145 LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
2150 /***********************************************************************
2151 * LISTBOX_HandleSystemTimer
2153 * WM_SYSTIMER handler.
2155 static LRESULT
LISTBOX_HandleSystemTimer( HWND hwnd
, LB_DESCR
*descr
)
2157 if (!LISTBOX_HandleTimer( hwnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
2159 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2160 LISTBOX_Timer
= LB_TIMER_NONE
;
2166 /***********************************************************************
2167 * LISTBOX_HandleMouseMove
2169 * WM_MOUSEMOVE handler.
2171 static void LISTBOX_HandleMouseMove( HWND hwnd
, LB_DESCR
*descr
,
2175 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2177 if (!descr
->captured
) return;
2179 if (descr
->style
& LBS_MULTICOLUMN
)
2182 else if (y
>= descr
->item_height
* descr
->page_size
)
2183 y
= descr
->item_height
* descr
->page_size
- 1;
2187 dir
= LB_TIMER_LEFT
;
2190 else if (x
>= descr
->width
)
2192 dir
= LB_TIMER_RIGHT
;
2193 x
= descr
->width
- 1;
2198 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2199 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2202 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2203 if (index
== -1) index
= descr
->focus_item
;
2204 if (!LISTBOX_HandleTimer( hwnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2206 /* Start/stop the system timer */
2208 if (dir
!= LB_TIMER_NONE
)
2209 SetSystemTimer( hwnd
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2210 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2211 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2212 LISTBOX_Timer
= dir
;
2216 /***********************************************************************
2217 * LISTBOX_HandleKeyDown
2219 static LRESULT
LISTBOX_HandleKeyDown( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
2222 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2223 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2224 bForceSelection
= FALSE
; /* only for single select list */
2226 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2228 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2229 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2231 if (caret
== -2) return 0;
2233 if (caret
== -1) switch(wParam
)
2236 if (descr
->style
& LBS_MULTICOLUMN
)
2238 bForceSelection
= FALSE
;
2239 if (descr
->focus_item
>= descr
->page_size
)
2240 caret
= descr
->focus_item
- descr
->page_size
;
2245 caret
= descr
->focus_item
- 1;
2246 if (caret
< 0) caret
= 0;
2249 if (descr
->style
& LBS_MULTICOLUMN
)
2251 bForceSelection
= FALSE
;
2252 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2253 caret
= descr
->focus_item
+ descr
->page_size
;
2258 caret
= descr
->focus_item
+ 1;
2259 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2263 if (descr
->style
& LBS_MULTICOLUMN
)
2265 INT page
= descr
->width
/ descr
->column_width
;
2266 if (page
< 1) page
= 1;
2267 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2269 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2270 if (caret
< 0) caret
= 0;
2273 if (descr
->style
& LBS_MULTICOLUMN
)
2275 INT page
= descr
->width
/ descr
->column_width
;
2276 if (page
< 1) page
= 1;
2277 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2279 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2280 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2286 caret
= descr
->nb_items
- 1;
2289 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2290 else if (descr
->style
& LBS_MULTIPLESEL
)
2292 LISTBOX_SetSelection( hwnd
, descr
, descr
->focus_item
,
2293 !descr
->items
[descr
->focus_item
].selected
,
2294 (descr
->style
& LBS_NOTIFY
) != 0 );
2298 bForceSelection
= FALSE
;
2300 if (bForceSelection
) /* focused item is used instead of key */
2301 caret
= descr
->focus_item
;
2304 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
2305 !(GetKeyState( VK_SHIFT
) & 0x8000))
2306 descr
->anchor_item
= caret
;
2307 LISTBOX_MoveCaret( hwnd
, descr
, caret
, TRUE
);
2308 LISTBOX_SetSelection( hwnd
, descr
, caret
, TRUE
, FALSE
);
2309 if (descr
->style
& LBS_NOTIFY
)
2313 /* make sure that combo parent doesn't hide us */
2314 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2316 if (descr
->nb_items
) SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2323 /***********************************************************************
2324 * LISTBOX_HandleChar
2326 static LRESULT
LISTBOX_HandleChar( HWND hwnd
, LB_DESCR
*descr
, WCHAR charW
)
2334 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2336 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2337 MAKEWPARAM(charW
, descr
->focus_item
),
2339 if (caret
== -2) return 0;
2342 caret
= LISTBOX_FindString( hwnd
, descr
, descr
->focus_item
, str
, FALSE
);
2345 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2346 LISTBOX_SetSelection( hwnd
, descr
, caret
, TRUE
, FALSE
);
2347 LISTBOX_MoveCaret( hwnd
, descr
, caret
, TRUE
);
2348 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2349 SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2355 /***********************************************************************
2358 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2361 MEASUREITEMSTRUCT mis
;
2364 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2367 GetClientRect( hwnd
, &rect
);
2368 descr
->owner
= GetParent( hwnd
);
2369 descr
->style
= GetWindowLongA( hwnd
, GWL_STYLE
);
2370 descr
->width
= rect
.right
- rect
.left
;
2371 descr
->height
= rect
.bottom
- rect
.top
;
2372 descr
->items
= NULL
;
2373 descr
->nb_items
= 0;
2374 descr
->top_item
= 0;
2375 descr
->selected_item
= -1;
2376 descr
->focus_item
= 0;
2377 descr
->anchor_item
= -1;
2378 descr
->item_height
= 1;
2379 descr
->page_size
= 1;
2380 descr
->column_width
= 150;
2381 descr
->horz_extent
= (descr
->style
& WS_HSCROLL
) ? 1 : 0;
2382 descr
->horz_pos
= 0;
2385 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2386 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2387 descr
->in_focus
= FALSE
;
2388 descr
->captured
= FALSE
;
2390 descr
->locale
= 0; /* FIXME */
2393 if (is_old_app(hwnd
) && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2395 /* Win95 document "List Box Differences" from MSDN:
2396 If a list box in a version 3.x application has either the
2397 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2398 horizontal and vertical scroll bars.
2400 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2405 TRACE_(combo
)("[%04x]: resetting owner %04x -> %04x\n",
2406 hwnd
, descr
->owner
, lphc
->self
);
2407 descr
->owner
= lphc
->self
;
2410 SetWindowLongA( hwnd
, 0, (LONG
)descr
);
2412 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2414 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2415 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2416 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2417 descr
->item_height
= LISTBOX_SetFont( hwnd
, descr
, 0 );
2419 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2421 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2423 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2424 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2428 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
2429 mis
.CtlType
= ODT_LISTBOX
;
2434 mis
.itemHeight
= descr
->item_height
;
2435 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2436 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2440 TRACE("owner: %04x, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2445 /***********************************************************************
2448 static BOOL
LISTBOX_Destroy( HWND hwnd
, LB_DESCR
*descr
)
2450 LISTBOX_ResetContent( hwnd
, descr
);
2451 SetWindowLongA( hwnd
, 0, 0 );
2452 HeapFree( GetProcessHeap(), 0, descr
);
2457 /***********************************************************************
2458 * ListBoxWndProc_common
2460 static LRESULT WINAPI
ListBoxWndProc_common( HWND hwnd
, UINT msg
,
2461 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2466 if (!(descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 )))
2468 if (msg
== WM_CREATE
)
2470 if (!LISTBOX_Create( hwnd
, NULL
))
2472 TRACE("creating wnd=%04x descr=%lx\n", hwnd
, GetWindowLongA( hwnd
, 0 ) );
2475 /* Ignore all other messages before we get a WM_CREATE */
2476 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2477 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2480 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2481 hwnd
, SPY_GetMsgName(msg
, hwnd
), wParam
, lParam
);
2484 case LB_RESETCONTENT16
:
2485 case LB_RESETCONTENT
:
2486 LISTBOX_ResetContent( hwnd
, descr
);
2487 LISTBOX_UpdateScroll( hwnd
, descr
);
2488 InvalidateRect( hwnd
, NULL
, TRUE
);
2491 case LB_ADDSTRING16
:
2492 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2498 if(unicode
|| !HAS_STRINGS(descr
))
2499 textW
= (LPWSTR
)lParam
;
2502 LPSTR textA
= (LPSTR
)lParam
;
2503 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2504 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2505 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2507 wParam
= LISTBOX_FindStringPos( hwnd
, descr
, textW
, FALSE
);
2508 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2509 if (!unicode
&& HAS_STRINGS(descr
))
2510 HeapFree(GetProcessHeap(), 0, textW
);
2514 case LB_INSERTSTRING16
:
2515 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2516 wParam
= (INT
)(INT16
)wParam
;
2518 case LB_INSERTSTRING
:
2522 if(unicode
|| !HAS_STRINGS(descr
))
2523 textW
= (LPWSTR
)lParam
;
2526 LPSTR textA
= (LPSTR
)lParam
;
2527 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2528 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2529 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2531 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2532 if(!unicode
&& HAS_STRINGS(descr
))
2533 HeapFree(GetProcessHeap(), 0, textW
);
2538 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2544 if(unicode
|| !HAS_STRINGS(descr
))
2545 textW
= (LPWSTR
)lParam
;
2548 LPSTR textA
= (LPSTR
)lParam
;
2549 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2550 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2551 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2553 wParam
= LISTBOX_FindFileStrPos( hwnd
, descr
, textW
);
2554 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2555 if(!unicode
&& HAS_STRINGS(descr
))
2556 HeapFree(GetProcessHeap(), 0, textW
);
2560 case LB_DELETESTRING16
:
2561 case LB_DELETESTRING
:
2562 if (LISTBOX_RemoveItem( hwnd
, descr
, wParam
) != LB_ERR
)
2563 return descr
->nb_items
;
2567 case LB_GETITEMDATA16
:
2568 case LB_GETITEMDATA
:
2569 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2571 return descr
->items
[wParam
].data
;
2573 case LB_SETITEMDATA16
:
2574 case LB_SETITEMDATA
:
2575 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2577 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2582 return descr
->nb_items
;
2585 lParam
= (LPARAM
)MapSL(lParam
);
2588 return LISTBOX_GetText( descr
, wParam
, lParam
, unicode
);
2590 case LB_GETTEXTLEN16
:
2593 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2595 return (HAS_STRINGS(descr
) ? strlenW(descr
->items
[wParam
].str
)
2598 case LB_GETCURSEL16
:
2600 if (descr
->nb_items
==0)
2602 if (!IS_MULTISELECT(descr
))
2603 return descr
->selected_item
;
2605 if (descr
->selected_item
!=-1)
2606 return descr
->selected_item
;
2608 return descr
->focus_item
;
2609 /* otherwise, if the user tries to move the selection with the */
2610 /* arrow keys, we will give the application something to choke on */
2611 case LB_GETTOPINDEX16
:
2612 case LB_GETTOPINDEX
:
2613 return descr
->top_item
;
2615 case LB_GETITEMHEIGHT16
:
2616 case LB_GETITEMHEIGHT
:
2617 return LISTBOX_GetItemHeight( descr
, wParam
);
2619 case LB_SETITEMHEIGHT16
:
2620 lParam
= LOWORD(lParam
);
2622 case LB_SETITEMHEIGHT
:
2623 return LISTBOX_SetItemHeight( hwnd
, descr
, wParam
, lParam
);
2625 case LB_ITEMFROMPOINT
:
2630 pt
.x
= LOWORD(lParam
);
2631 pt
.y
= HIWORD(lParam
);
2634 rect
.right
= descr
->width
;
2635 rect
.bottom
= descr
->height
;
2637 return MAKELONG( LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
),
2638 !PtInRect( &rect
, pt
) );
2641 case LB_SETCARETINDEX16
:
2642 case LB_SETCARETINDEX
:
2643 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2644 if (LISTBOX_SetCaretIndex( hwnd
, descr
, wParam
, !lParam
) == LB_ERR
)
2651 case LB_GETCARETINDEX16
:
2652 case LB_GETCARETINDEX
:
2653 return descr
->focus_item
;
2655 case LB_SETTOPINDEX16
:
2656 case LB_SETTOPINDEX
:
2657 return LISTBOX_SetTopItem( hwnd
, descr
, wParam
, TRUE
);
2659 case LB_SETCOLUMNWIDTH16
:
2660 case LB_SETCOLUMNWIDTH
:
2661 return LISTBOX_SetColumnWidth( hwnd
, descr
, wParam
);
2663 case LB_GETITEMRECT16
:
2666 ret
= LISTBOX_GetItemRect( descr
, (INT16
)wParam
, &rect
);
2667 CONV_RECT32TO16( &rect
, MapSL(lParam
) );
2671 case LB_GETITEMRECT
:
2672 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2674 case LB_FINDSTRING16
:
2675 wParam
= (INT
)(INT16
)wParam
;
2676 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2682 if(unicode
|| !HAS_STRINGS(descr
))
2683 textW
= (LPWSTR
)lParam
;
2686 LPSTR textA
= (LPSTR
)lParam
;
2687 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2688 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2689 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2691 ret
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, FALSE
);
2692 if(!unicode
&& HAS_STRINGS(descr
))
2693 HeapFree(GetProcessHeap(), 0, textW
);
2697 case LB_FINDSTRINGEXACT16
:
2698 wParam
= (INT
)(INT16
)wParam
;
2699 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2701 case LB_FINDSTRINGEXACT
:
2705 if(unicode
|| !HAS_STRINGS(descr
))
2706 textW
= (LPWSTR
)lParam
;
2709 LPSTR textA
= (LPSTR
)lParam
;
2710 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2711 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2712 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2714 ret
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, TRUE
);
2715 if(!unicode
&& HAS_STRINGS(descr
))
2716 HeapFree(GetProcessHeap(), 0, textW
);
2720 case LB_SELECTSTRING16
:
2721 wParam
= (INT
)(INT16
)wParam
;
2722 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2724 case LB_SELECTSTRING
:
2729 if(HAS_STRINGS(descr
))
2730 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2731 debugstr_a((LPSTR
)lParam
));
2732 if(unicode
|| !HAS_STRINGS(descr
))
2733 textW
= (LPWSTR
)lParam
;
2736 LPSTR textA
= (LPSTR
)lParam
;
2737 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2738 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2739 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2741 index
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, FALSE
);
2742 if(!unicode
&& HAS_STRINGS(descr
))
2743 HeapFree(GetProcessHeap(), 0, textW
);
2744 if (index
!= LB_ERR
)
2746 LISTBOX_SetCaretIndex( hwnd
, descr
, index
, TRUE
);
2747 LISTBOX_SetSelection( hwnd
, descr
, index
, TRUE
, FALSE
);
2753 wParam
= (INT
)(INT16
)wParam
;
2756 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2758 return descr
->items
[wParam
].selected
;
2761 lParam
= (INT
)(INT16
)lParam
;
2764 return LISTBOX_SetSelection( hwnd
, descr
, lParam
, wParam
, FALSE
);
2766 case LB_SETCURSEL16
:
2767 wParam
= (INT
)(INT16
)wParam
;
2770 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2771 LISTBOX_SetCaretIndex( hwnd
, descr
, wParam
, TRUE
);
2772 return LISTBOX_SetSelection( hwnd
, descr
, wParam
, TRUE
, FALSE
);
2774 case LB_GETSELCOUNT16
:
2775 case LB_GETSELCOUNT
:
2776 return LISTBOX_GetSelCount( descr
);
2778 case LB_GETSELITEMS16
:
2779 return LISTBOX_GetSelItems16( descr
, wParam
, (LPINT16
)MapSL(lParam
) );
2781 case LB_GETSELITEMS
:
2782 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2784 case LB_SELITEMRANGE16
:
2785 case LB_SELITEMRANGE
:
2786 if (LOWORD(lParam
) <= HIWORD(lParam
))
2787 return LISTBOX_SelectItemRange( hwnd
, descr
, LOWORD(lParam
),
2788 HIWORD(lParam
), wParam
);
2790 return LISTBOX_SelectItemRange( hwnd
, descr
, HIWORD(lParam
),
2791 LOWORD(lParam
), wParam
);
2793 case LB_SELITEMRANGEEX16
:
2794 case LB_SELITEMRANGEEX
:
2795 if ((INT
)lParam
>= (INT
)wParam
)
2796 return LISTBOX_SelectItemRange( hwnd
, descr
, wParam
, lParam
, TRUE
);
2798 return LISTBOX_SelectItemRange( hwnd
, descr
, lParam
, wParam
, FALSE
);
2800 case LB_GETHORIZONTALEXTENT16
:
2801 case LB_GETHORIZONTALEXTENT
:
2802 return descr
->horz_extent
;
2804 case LB_SETHORIZONTALEXTENT16
:
2805 case LB_SETHORIZONTALEXTENT
:
2806 return LISTBOX_SetHorizontalExtent( hwnd
, descr
, wParam
);
2808 case LB_GETANCHORINDEX16
:
2809 case LB_GETANCHORINDEX
:
2810 return descr
->anchor_item
;
2812 case LB_SETANCHORINDEX16
:
2813 wParam
= (INT
)(INT16
)wParam
;
2815 case LB_SETANCHORINDEX
:
2816 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2818 descr
->anchor_item
= (INT
)wParam
;
2822 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2823 * be set automatically (this is different in Win32) */
2824 if (wParam
& DDL_DRIVES
) wParam
|= DDL_EXCLUSIVE
;
2825 lParam
= (LPARAM
)MapSL(lParam
);
2832 textW
= (LPWSTR
)lParam
;
2835 LPSTR textA
= (LPSTR
)lParam
;
2836 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2837 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2838 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2840 ret
= LISTBOX_Directory( hwnd
, descr
, wParam
, textW
, msg
== LB_DIR
);
2842 HeapFree(GetProcessHeap(), 0, textW
);
2847 return descr
->locale
;
2850 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2853 case LB_INITSTORAGE
:
2854 return LISTBOX_InitStorage( hwnd
, descr
, wParam
);
2857 return LISTBOX_SetCount( hwnd
, descr
, (INT
)wParam
);
2859 case LB_SETTABSTOPS16
:
2860 return LISTBOX_SetTabStops( hwnd
, descr
, (INT
)(INT16
)wParam
, MapSL(lParam
), TRUE
);
2862 case LB_SETTABSTOPS
:
2863 return LISTBOX_SetTabStops( hwnd
, descr
, wParam
, (LPINT
)lParam
, FALSE
);
2867 if (descr
->caret_on
)
2869 descr
->caret_on
= TRUE
;
2870 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2871 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2876 if (!descr
->caret_on
)
2878 descr
->caret_on
= FALSE
;
2879 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2880 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2884 return LISTBOX_Destroy( hwnd
, descr
);
2887 InvalidateRect( hwnd
, NULL
, TRUE
);
2891 LISTBOX_SetRedraw( hwnd
, descr
, wParam
!= 0 );
2895 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2900 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( hwnd
, &ps
);
2901 ret
= LISTBOX_Paint( hwnd
, descr
, hdc
);
2902 if( !wParam
) EndPaint( hwnd
, &ps
);
2906 LISTBOX_UpdateSize( hwnd
, descr
);
2911 LISTBOX_SetFont( hwnd
, descr
, (HFONT
)wParam
);
2912 if (lParam
) InvalidateRect( hwnd
, 0, TRUE
);
2915 descr
->in_focus
= TRUE
;
2916 descr
->caret_on
= TRUE
;
2917 if (descr
->focus_item
!= -1)
2918 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2919 SEND_NOTIFICATION( hwnd
, descr
, LBN_SETFOCUS
);
2922 descr
->in_focus
= FALSE
;
2923 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2924 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2925 SEND_NOTIFICATION( hwnd
, descr
, LBN_KILLFOCUS
);
2928 return LISTBOX_HandleHScroll( hwnd
, descr
, wParam
);
2930 return LISTBOX_HandleVScroll( hwnd
, descr
, wParam
);
2932 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2933 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
2934 return LISTBOX_HandleMouseWheel( hwnd
, descr
, wParam
);
2935 case WM_LBUTTONDOWN
:
2936 return LISTBOX_HandleLButtonDown( hwnd
, descr
, wParam
,
2937 (INT16
)LOWORD(lParam
),
2938 (INT16
)HIWORD(lParam
) );
2939 case WM_LBUTTONDBLCLK
:
2940 if (descr
->style
& LBS_NOTIFY
)
2941 SEND_NOTIFICATION( hwnd
, descr
, LBN_DBLCLK
);
2944 if (GetCapture() == hwnd
)
2945 LISTBOX_HandleMouseMove( hwnd
, descr
, (INT16
)LOWORD(lParam
),
2946 (INT16
)HIWORD(lParam
) );
2949 return LISTBOX_HandleLButtonUp( hwnd
, descr
);
2951 return LISTBOX_HandleKeyDown( hwnd
, descr
, wParam
);
2956 charW
= (WCHAR
)wParam
;
2959 CHAR charA
= (CHAR
)wParam
;
2960 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
2962 return LISTBOX_HandleChar( hwnd
, descr
, charW
);
2965 return LISTBOX_HandleSystemTimer( hwnd
, descr
);
2967 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
2970 HBRUSH hbrush
= SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
2971 wParam
, (LPARAM
)hwnd
);
2972 TRACE("hbrush = %04x\n", hbrush
);
2974 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
2977 GetClientRect(hwnd
, &rect
);
2978 FillRect((HDC
)wParam
, &rect
, hbrush
);
2984 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
2985 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2989 case WM_QUERYDROPOBJECT
:
2994 LPDRAGINFO16 dragInfo
= MapSL( lParam
);
2995 dragInfo
->l
= LISTBOX_GetItemFromPoint( descr
, dragInfo
->pt
.x
,
2997 return SendMessage16( descr
->owner
, msg
, wParam
, lParam
);
3002 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3003 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
3004 hwnd
, msg
, wParam
, lParam
);
3005 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3006 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3011 /***********************************************************************
3014 * This is just a wrapper for the real wndproc, it only does window locking
3017 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3019 if (!IsWindow(hwnd
)) return 0;
3020 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3023 /***********************************************************************
3026 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3028 if (!IsWindow(hwnd
)) return 0;
3029 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);
3032 /***********************************************************************
3033 * ComboLBWndProc_common
3035 * The real combo listbox wndproc
3037 static LRESULT WINAPI
ComboLBWndProc_common( HWND hwnd
, UINT msg
,
3038 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
3041 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 );
3043 TRACE_(combo
)("[%04x]: msg %s wp %08x lp %08lx\n",
3044 hwnd
, SPY_GetMsgName(msg
, hwnd
), wParam
, lParam
);
3046 if( descr
|| msg
== WM_CREATE
)
3048 LPHEADCOMBO lphc
= (descr
) ? descr
->lphc
: NULL
;
3054 CREATESTRUCTA
*lpcs
= (CREATESTRUCTA
*)lParam
;
3055 TRACE_(combo
)("\tpassed parent handle = %p\n",lpcs
->lpCreateParams
);
3056 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
3057 return LISTBOX_Create( hwnd
, lphc
);
3060 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
3061 (CB_GETTYPE(lphc
) != CBS_SIMPLE
) )
3067 mousePos
.x
= (INT16
)LOWORD(lParam
);
3068 mousePos
.y
= (INT16
)HIWORD(lParam
);
3071 * If we are in a dropdown combobox, we simulate that
3072 * the mouse is captured to show the tracking of the item.
3074 GetClientRect(hwnd
, &clientRect
);
3076 if (PtInRect( &clientRect
, mousePos
))
3078 captured
= descr
->captured
;
3079 descr
->captured
= TRUE
;
3081 LISTBOX_HandleMouseMove( hwnd
, descr
,
3082 mousePos
.x
, mousePos
.y
);
3084 descr
->captured
= captured
;
3089 LISTBOX_HandleMouseMove( hwnd
, descr
,
3090 mousePos
.x
, mousePos
.y
);
3099 * If we are in Win3.1 look, go with the default behavior.
3101 return unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3102 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3105 if (TWEAK_WineLook
> WIN31_LOOK
)
3111 * If the mouse button "up" is not in the listbox,
3112 * we make sure there is no selection by re-selecting the
3113 * item that was selected when the listbox was made visible.
3115 mousePos
.x
= (INT16
)LOWORD(lParam
);
3116 mousePos
.y
= (INT16
)HIWORD(lParam
);
3118 GetClientRect(hwnd
, &clientRect
);
3121 * When the user clicks outside the combobox and the focus
3122 * is lost, the owning combobox will send a fake buttonup with
3123 * 0xFFFFFFF as the mouse location, we must also revert the
3124 * selection to the original selection.
3126 if ( (lParam
== (LPARAM
)-1) ||
3127 (!PtInRect( &clientRect
, mousePos
)) )
3129 LISTBOX_MoveCaret( hwnd
, descr
, lphc
->droppedIndex
, FALSE
);
3132 return LISTBOX_HandleLButtonUp( hwnd
, descr
);
3133 case WM_LBUTTONDBLCLK
:
3134 case WM_LBUTTONDOWN
:
3135 return LISTBOX_HandleLButtonDownCombo(hwnd
, descr
, msg
, wParam
,
3136 (INT16
)LOWORD(lParam
),
3137 (INT16
)HIWORD(lParam
) );
3141 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3143 /* for some reason(?) Windows makes it possible to
3144 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3146 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3147 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3148 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3150 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3154 return LISTBOX_HandleKeyDown( hwnd
, descr
, wParam
);
3156 case LB_SETCURSEL16
:
3158 lRet
= unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3159 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3160 lRet
=(lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
3163 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3168 return unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3169 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3172 lRet
= unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3173 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3175 TRACE_(combo
)("\t default on msg [%04x]\n", (UINT16
)msg
);
3180 /***********************************************************************
3183 * NOTE: in Windows, winproc address of the ComboLBox is the same
3184 * as that of the Listbox.
3186 LRESULT WINAPI
ComboLBWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3188 if (!IsWindow(hwnd
)) return 0;
3189 return ComboLBWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3192 /***********************************************************************
3195 LRESULT WINAPI
ComboLBWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3197 if (!IsWindow(hwnd
)) return 0;
3198 return ComboLBWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);