4 * Copyright 1996 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include "wine/winuser16.h"
29 #include "wine/winbase16.h"
31 #include "wine/unicode.h"
37 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(listbox
);
41 WINE_DECLARE_DEBUG_CHANNEL(combo
);
47 * Probably needs improvement:
51 /* Items array granularity */
52 #define LB_ARRAY_GRANULARITY 16
54 /* Scrolling timeout in ms */
55 #define LB_SCROLL_TIMEOUT 50
57 /* Listbox system timer id */
60 /* flag listbox changed while setredraw false - internal style */
61 #define LBS_DISPLAYCHANGED 0x80000000
66 LPWSTR str
; /* Item text */
67 BOOL selected
; /* Is item selected? */
68 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
69 DWORD data
; /* User data */
72 /* Listbox structure */
75 HWND owner
; /* Owner window to send notifications to */
76 UINT style
; /* Window style */
77 INT width
; /* Window width */
78 INT height
; /* Window height */
79 LB_ITEMDATA
*items
; /* Array of items */
80 INT nb_items
; /* Number of items */
81 INT top_item
; /* Top visible item */
82 INT selected_item
; /* Selected item */
83 INT focus_item
; /* Item that has the focus */
84 INT anchor_item
; /* Anchor item for extended selection */
85 INT item_height
; /* Default item height */
86 INT page_size
; /* Items per listbox page */
87 INT column_width
; /* Column width for multi-column listboxes */
88 INT horz_extent
; /* Horizontal extent (0 if no hscroll) */
89 INT horz_pos
; /* Horizontal position */
90 INT nb_tabs
; /* Number of tabs in array */
91 INT
*tabs
; /* Array of tabs */
92 BOOL caret_on
; /* Is caret on? */
93 BOOL captured
; /* Is mouse captured? */
95 HFONT font
; /* Current font */
96 LCID locale
; /* Current locale for string comparisons */
97 LPHEADCOMBO lphc
; /* ComboLBox */
101 #define IS_OWNERDRAW(descr) \
102 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
104 #define HAS_STRINGS(descr) \
105 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
108 #define IS_MULTISELECT(descr) \
109 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
111 #define SEND_NOTIFICATION(hwnd,descr,code) \
112 (SendMessageW( (descr)->owner, WM_COMMAND, \
113 MAKEWPARAM( GetWindowLongA((hwnd),GWL_ID), (code)), (LPARAM)(hwnd) ))
115 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
117 /* Current timer status */
127 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
129 static LRESULT WINAPI
ComboLBWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
130 static LRESULT WINAPI
ComboLBWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
131 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
132 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
134 static LRESULT
LISTBOX_GetItemRect( LB_DESCR
*descr
, INT index
, RECT
*rect
);
136 /*********************************************************************
137 * listbox class descriptor
139 const struct builtin_class_descr LISTBOX_builtin_class
=
141 "ListBox", /* name */
142 CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
143 ListBoxWndProcA
, /* procA */
144 ListBoxWndProcW
, /* procW */
145 sizeof(LB_DESCR
*), /* extra */
146 IDC_ARROW
, /* cursor */
151 /*********************************************************************
152 * combolbox class descriptor
154 const struct builtin_class_descr COMBOLBOX_builtin_class
=
156 "ComboLBox", /* name */
157 CS_DBLCLKS
| CS_SAVEBITS
, /* style */
158 ComboLBWndProcA
, /* procA */
159 ComboLBWndProcW
, /* procW */
160 sizeof(LB_DESCR
*), /* extra */
161 IDC_ARROW
, /* cursor */
166 /* check whether app is a Win 3.1 app */
167 inline static BOOL
is_old_app( HWND hwnd
)
169 return (GetExpWinVer16( GetWindowLongA(hwnd
,GWL_HINSTANCE
) ) & 0xFF00 ) == 0x0300;
173 /***********************************************************************
176 void LISTBOX_Dump( HWND hwnd
)
180 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 );
182 TRACE( "Listbox:\n" );
183 TRACE( "hwnd=%p descr=%08x items=%d top=%d\n",
184 hwnd
, (UINT
)descr
, descr
->nb_items
, descr
->top_item
);
185 for (i
= 0, item
= descr
->items
; i
< descr
->nb_items
; i
++, item
++)
187 TRACE( "%4d: %-40s %d %08lx %3d\n",
188 i
, debugstr_w(item
->str
), item
->selected
, item
->data
, item
->height
);
193 /***********************************************************************
194 * LISTBOX_GetCurrentPageSize
196 * Return the current page size
198 static INT
LISTBOX_GetCurrentPageSize( LB_DESCR
*descr
)
201 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
202 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
204 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
206 if (i
== descr
->top_item
) return 1;
207 else return i
- descr
->top_item
;
211 /***********************************************************************
212 * LISTBOX_GetMaxTopIndex
214 * Return the maximum possible index for the top of the listbox.
216 static INT
LISTBOX_GetMaxTopIndex( LB_DESCR
*descr
)
220 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
222 page
= descr
->height
;
223 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
224 if ((page
-= descr
->items
[max
].height
) < 0) break;
225 if (max
< descr
->nb_items
- 1) max
++;
227 else if (descr
->style
& LBS_MULTICOLUMN
)
229 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
230 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
231 max
= (max
- page
) * descr
->page_size
;
235 max
= descr
->nb_items
- descr
->page_size
;
237 if (max
< 0) max
= 0;
242 /***********************************************************************
243 * LISTBOX_UpdateScroll
245 * Update the scrollbars. Should be called whenever the content
246 * of the listbox changes.
248 static void LISTBOX_UpdateScroll( HWND hwnd
, LB_DESCR
*descr
)
252 /* Check the listbox scroll bar flags individually before we call
253 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
254 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
255 scroll bar when we do not need one.
256 if (!(descr->style & WS_VSCROLL)) return;
259 /* It is important that we check descr->style, and not wnd->dwStyle,
260 for WS_VSCROLL, as the former is exactly the one passed in
261 argument to CreateWindow.
262 In Windows (and from now on in Wine :) a listbox created
263 with such a style (no WS_SCROLL) does not update
264 the scrollbar with listbox-related data, thus letting
265 the programmer use it for his/her own purposes. */
267 if (descr
->style
& LBS_NOREDRAW
) return;
268 info
.cbSize
= sizeof(info
);
270 if (descr
->style
& LBS_MULTICOLUMN
)
273 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
274 info
.nPos
= descr
->top_item
/ descr
->page_size
;
275 info
.nPage
= descr
->width
/ descr
->column_width
;
276 if (info
.nPage
< 1) info
.nPage
= 1;
277 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
278 if (descr
->style
& LBS_DISABLENOSCROLL
)
279 info
.fMask
|= SIF_DISABLENOSCROLL
;
280 if (descr
->style
& WS_HSCROLL
)
281 SetScrollInfo( hwnd
, SB_HORZ
, &info
, TRUE
);
283 info
.fMask
= SIF_RANGE
;
284 if (descr
->style
& WS_VSCROLL
)
285 SetScrollInfo( hwnd
, SB_VERT
, &info
, TRUE
);
290 info
.nMax
= descr
->nb_items
- 1;
291 info
.nPos
= descr
->top_item
;
292 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
293 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
294 if (descr
->style
& LBS_DISABLENOSCROLL
)
295 info
.fMask
|= SIF_DISABLENOSCROLL
;
296 if (descr
->style
& WS_VSCROLL
)
297 SetScrollInfo( hwnd
, SB_VERT
, &info
, TRUE
);
299 if (descr
->horz_extent
)
302 info
.nMax
= descr
->horz_extent
- 1;
303 info
.nPos
= descr
->horz_pos
;
304 info
.nPage
= descr
->width
;
305 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
306 if (descr
->style
& LBS_DISABLENOSCROLL
)
307 info
.fMask
|= SIF_DISABLENOSCROLL
;
308 if (descr
->style
& WS_HSCROLL
)
309 SetScrollInfo( hwnd
, SB_HORZ
, &info
, TRUE
);
315 /***********************************************************************
318 * Set the top item of the listbox, scrolling up or down if necessary.
320 static LRESULT
LISTBOX_SetTopItem( HWND hwnd
, LB_DESCR
*descr
, INT index
,
323 INT max
= LISTBOX_GetMaxTopIndex( descr
);
324 if (index
> max
) index
= max
;
325 if (index
< 0) index
= 0;
326 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
327 if (descr
->top_item
== index
) return LB_OKAY
;
328 if (descr
->style
& LBS_MULTICOLUMN
)
330 INT diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
331 if (scroll
&& (abs(diff
) < descr
->width
))
332 ScrollWindowEx( hwnd
, diff
, 0, NULL
, NULL
, 0, NULL
,
333 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
341 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
345 if (index
> descr
->top_item
)
347 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
348 diff
-= descr
->items
[i
].height
;
352 for (i
= index
; i
< descr
->top_item
; i
++)
353 diff
+= descr
->items
[i
].height
;
357 diff
= (descr
->top_item
- index
) * descr
->item_height
;
359 if (abs(diff
) < descr
->height
)
360 ScrollWindowEx( hwnd
, 0, diff
, NULL
, NULL
, 0, NULL
,
361 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
365 if (!scroll
) InvalidateRect( hwnd
, NULL
, TRUE
);
366 descr
->top_item
= index
;
367 LISTBOX_UpdateScroll( hwnd
, descr
);
372 /***********************************************************************
375 * Update the page size. Should be called when the size of
376 * the client area or the item height changes.
378 static void LISTBOX_UpdatePage( HWND hwnd
, LB_DESCR
*descr
)
382 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
384 if (page_size
== descr
->page_size
) return;
385 descr
->page_size
= page_size
;
386 if (descr
->style
& LBS_MULTICOLUMN
)
387 InvalidateRect( hwnd
, NULL
, TRUE
);
388 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
, FALSE
);
392 /***********************************************************************
395 * Update the size of the listbox. Should be called when the size of
396 * the client area changes.
398 static void LISTBOX_UpdateSize( HWND hwnd
, LB_DESCR
*descr
)
402 GetClientRect( hwnd
, &rect
);
403 descr
->width
= rect
.right
- rect
.left
;
404 descr
->height
= rect
.bottom
- rect
.top
;
405 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
410 GetWindowRect( hwnd
, &rect
);
411 if(descr
->item_height
!= 0)
412 remaining
= descr
->height
% descr
->item_height
;
415 if ((descr
->height
> descr
->item_height
) && remaining
)
417 if (is_old_app(hwnd
))
418 { /* give a margin for error to 16 bits programs - if we need
419 less than the height of the nonclient area, round to the
420 *next* number of items */
421 int ncheight
= rect
.bottom
- rect
.top
- descr
->height
;
422 if ((descr
->item_height
- remaining
) <= ncheight
)
423 remaining
= remaining
- descr
->item_height
;
425 TRACE("[%p]: changing height %d -> %d\n",
426 hwnd
, descr
->height
, descr
->height
- remaining
);
427 SetWindowPos( hwnd
, 0, 0, 0, rect
.right
- rect
.left
,
428 rect
.bottom
- rect
.top
- remaining
,
429 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
433 TRACE("[%p]: new size = %d,%d\n", hwnd
, descr
->width
, descr
->height
);
434 LISTBOX_UpdatePage( hwnd
, descr
);
435 LISTBOX_UpdateScroll( hwnd
, descr
);
437 /* Invalidate the focused item so it will be repainted correctly */
438 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
440 InvalidateRect( hwnd
, &rect
, FALSE
);
445 /***********************************************************************
446 * LISTBOX_GetItemRect
448 * Get the rectangle enclosing an item, in listbox client coordinates.
449 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
451 static LRESULT
LISTBOX_GetItemRect( LB_DESCR
*descr
, INT index
, RECT
*rect
)
453 /* Index <= 0 is legal even on empty listboxes */
454 if (index
&& (index
>= descr
->nb_items
)) return -1;
455 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
456 if (descr
->style
& LBS_MULTICOLUMN
)
458 INT col
= (index
/ descr
->page_size
) -
459 (descr
->top_item
/ descr
->page_size
);
460 rect
->left
+= col
* descr
->column_width
;
461 rect
->right
= rect
->left
+ descr
->column_width
;
462 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
463 rect
->bottom
= rect
->top
+ descr
->item_height
;
465 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
468 rect
->right
+= descr
->horz_pos
;
469 if ((index
>= 0) && (index
< descr
->nb_items
))
471 if (index
< descr
->top_item
)
473 for (i
= descr
->top_item
-1; i
>= index
; i
--)
474 rect
->top
-= descr
->items
[i
].height
;
478 for (i
= descr
->top_item
; i
< index
; i
++)
479 rect
->top
+= descr
->items
[i
].height
;
481 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
487 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
488 rect
->bottom
= rect
->top
+ descr
->item_height
;
489 rect
->right
+= descr
->horz_pos
;
492 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
493 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
497 /***********************************************************************
498 * LISTBOX_GetItemFromPoint
500 * Return the item nearest from point (x,y) (in client coordinates).
502 static INT
LISTBOX_GetItemFromPoint( LB_DESCR
*descr
, INT x
, INT y
)
504 INT index
= descr
->top_item
;
506 if (!descr
->nb_items
) return -1; /* No items */
507 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
512 while (index
< descr
->nb_items
)
514 if ((pos
+= descr
->items
[index
].height
) > y
) break;
523 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
527 else if (descr
->style
& LBS_MULTICOLUMN
)
529 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
530 if (y
>= 0) index
+= y
/ descr
->item_height
;
531 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
532 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
536 index
+= (y
/ descr
->item_height
);
538 if (index
< 0) return 0;
539 if (index
>= descr
->nb_items
) return -1;
544 /***********************************************************************
549 static void LISTBOX_PaintItem( HWND hwnd
, LB_DESCR
*descr
, HDC hdc
,
550 const RECT
*rect
, INT index
, UINT action
, BOOL ignoreFocus
)
552 LB_ITEMDATA
*item
= NULL
;
553 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
555 if (IS_OWNERDRAW(descr
))
560 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
564 if (action
== ODA_FOCUS
)
565 DrawFocusRect( hdc
, rect
);
567 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
571 /* some programs mess with the clipping region when
572 drawing the item, *and* restore the previous region
573 after they are done, so a region has better to exist
574 else everything ends clipped */
575 GetClientRect(hwnd
, &r
);
576 hrgn
= CreateRectRgnIndirect(&r
);
577 SelectClipRgn( hdc
, hrgn
);
578 DeleteObject( hrgn
);
580 dis
.CtlType
= ODT_LISTBOX
;
583 dis
.itemAction
= action
;
587 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
588 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
590 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
591 if (!IsWindowEnabled(hwnd
)) dis
.itemState
|= ODS_DISABLED
;
592 dis
.itemData
= item
? item
->data
: 0;
594 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%ld,%ld-%ld,%ld\n",
595 hwnd
, index
, item
? debugstr_w(item
->str
) : "", action
,
596 dis
.itemState
, rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
597 SendMessageW(descr
->owner
, WM_DRAWITEM
, id
, (LPARAM
)&dis
);
601 COLORREF oldText
= 0, oldBk
= 0;
603 if (action
== ODA_FOCUS
)
605 DrawFocusRect( hdc
, rect
);
608 if (item
&& item
->selected
)
610 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
611 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
614 TRACE("[%p]: painting %d (%s) action=%02x rect=%ld,%ld-%ld,%ld\n",
615 hwnd
, index
, item
? debugstr_w(item
->str
) : "", action
,
616 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
618 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
619 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
620 else if (!(descr
->style
& LBS_USETABSTOPS
))
621 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
622 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
623 strlenW(item
->str
), NULL
);
626 /* Output empty string to paint background in the full width. */
627 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
628 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
629 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
630 item
->str
, strlenW(item
->str
),
631 descr
->nb_tabs
, descr
->tabs
, 0);
633 if (item
&& item
->selected
)
635 SetBkColor( hdc
, oldBk
);
636 SetTextColor( hdc
, oldText
);
638 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
640 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
645 /***********************************************************************
648 * Change the redraw flag.
650 static void LISTBOX_SetRedraw( HWND hwnd
, LB_DESCR
*descr
, BOOL on
)
654 if (!(descr
->style
& LBS_NOREDRAW
)) return;
655 descr
->style
&= ~LBS_NOREDRAW
;
656 if (descr
->style
& LBS_DISPLAYCHANGED
)
657 { /* page was changed while setredraw false, refresh automatically */
658 InvalidateRect(hwnd
, NULL
, TRUE
);
659 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
660 { /* reset top of page if less than number of items/page */
661 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
662 if (descr
->top_item
< 0) descr
->top_item
= 0;
664 descr
->style
&= ~LBS_DISPLAYCHANGED
;
666 LISTBOX_UpdateScroll( hwnd
, descr
);
668 else descr
->style
|= LBS_NOREDRAW
;
672 /***********************************************************************
673 * LISTBOX_RepaintItem
675 * Repaint a single item synchronously.
677 static void LISTBOX_RepaintItem( HWND hwnd
, LB_DESCR
*descr
, INT index
,
683 HBRUSH hbrush
, oldBrush
= 0;
685 /* Do not repaint the item if the item is not visible */
686 if (!IsWindowVisible(hwnd
)) return;
687 if (descr
->style
& LBS_NOREDRAW
)
689 descr
->style
|= LBS_DISPLAYCHANGED
;
692 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
693 if (!(hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
))) return;
694 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
695 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
696 (WPARAM
)hdc
, (LPARAM
)hwnd
);
697 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
698 if (!IsWindowEnabled(hwnd
))
699 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
700 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
701 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, index
, action
, FALSE
);
702 if (oldFont
) SelectObject( hdc
, oldFont
);
703 if (oldBrush
) SelectObject( hdc
, oldBrush
);
704 ReleaseDC( hwnd
, hdc
);
708 /***********************************************************************
709 * LISTBOX_InitStorage
711 static LRESULT
LISTBOX_InitStorage( HWND hwnd
, LB_DESCR
*descr
, INT nb_items
)
715 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
716 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
718 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
719 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
720 nb_items
* sizeof(LB_ITEMDATA
));
723 item
= HeapAlloc( GetProcessHeap(), 0,
724 nb_items
* sizeof(LB_ITEMDATA
));
729 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
737 /***********************************************************************
738 * LISTBOX_SetTabStops
740 static BOOL
LISTBOX_SetTabStops( HWND hwnd
, LB_DESCR
*descr
, INT count
,
741 LPINT tabs
, BOOL short_ints
)
743 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
744 if (descr
->tabs
) HeapFree( GetProcessHeap(), 0, descr
->tabs
);
745 if (!(descr
->nb_tabs
= count
))
750 /* FIXME: count = 1 */
751 if (!(descr
->tabs
= (INT
*)HeapAlloc( GetProcessHeap(), 0,
752 descr
->nb_tabs
* sizeof(INT
) )))
757 LPINT16 p
= (LPINT16
)tabs
;
759 TRACE("[%p]: settabstops ", hwnd
);
760 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
761 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
762 if (TRACE_ON(listbox
)) TRACE("%hd ", descr
->tabs
[i
]);
764 if (TRACE_ON(listbox
)) TRACE("\n");
766 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
767 /* FIXME: repaint the window? */
772 /***********************************************************************
775 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPARAM lParam
, BOOL unicode
)
777 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
778 if (HAS_STRINGS(descr
))
781 return strlenW(descr
->items
[index
].str
);
783 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
787 LPWSTR buffer
= (LPWSTR
)lParam
;
788 strcpyW( buffer
, descr
->items
[index
].str
);
789 return strlenW(buffer
);
793 LPSTR buffer
= (LPSTR
)lParam
;
794 return WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1, buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
798 *((LPDWORD
)lParam
)=*(LPDWORD
)(&descr
->items
[index
].data
);
799 return sizeof(DWORD
);
804 /***********************************************************************
805 * LISTBOX_FindStringPos
807 * Find the nearest string located before a given string in sort order.
808 * If 'exact' is TRUE, return an error if we don't get an exact match.
810 static INT
LISTBOX_FindStringPos( HWND hwnd
, LB_DESCR
*descr
, LPCWSTR str
,
813 INT index
, min
, max
, res
= -1;
815 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
817 max
= descr
->nb_items
;
820 index
= (min
+ max
) / 2;
821 if (HAS_STRINGS(descr
))
822 res
= lstrcmpiW( str
, descr
->items
[index
].str
);
825 COMPAREITEMSTRUCT cis
;
826 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
828 cis
.CtlType
= ODT_LISTBOX
;
831 /* note that some application (MetaStock) expects the second item
832 * to be in the listbox */
834 cis
.itemData1
= (DWORD
)str
;
836 cis
.itemData2
= descr
->items
[index
].data
;
837 cis
.dwLocaleId
= descr
->locale
;
838 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
840 if (!res
) return index
;
841 if (res
< 0) max
= index
;
842 else min
= index
+ 1;
844 return exact
? -1 : max
;
848 /***********************************************************************
849 * LISTBOX_FindFileStrPos
851 * Find the nearest string located before a given string in directory
852 * sort order (i.e. first files, then directories, then drives).
854 static INT
LISTBOX_FindFileStrPos( HWND hwnd
, LB_DESCR
*descr
, LPCWSTR str
)
856 INT min
, max
, res
= -1;
858 if (!HAS_STRINGS(descr
))
859 return LISTBOX_FindStringPos( hwnd
, descr
, str
, FALSE
);
861 max
= descr
->nb_items
;
864 INT index
= (min
+ max
) / 2;
865 LPCWSTR p
= descr
->items
[index
].str
;
866 if (*p
== '[') /* drive or directory */
868 if (*str
!= '[') res
= -1;
869 else if (p
[1] == '-') /* drive */
871 if (str
[1] == '-') res
= str
[2] - p
[2];
876 if (str
[1] == '-') res
= 1;
877 else res
= lstrcmpiW( str
, p
);
882 if (*str
== '[') res
= 1;
883 else res
= lstrcmpiW( str
, p
);
885 if (!res
) return index
;
886 if (res
< 0) max
= index
;
887 else min
= index
+ 1;
893 /***********************************************************************
896 * Find the item beginning with a given string.
898 static INT
LISTBOX_FindString( HWND hwnd
, LB_DESCR
*descr
, INT start
,
899 LPCWSTR str
, BOOL exact
)
904 if (start
>= descr
->nb_items
) start
= -1;
905 item
= descr
->items
+ start
+ 1;
906 if (HAS_STRINGS(descr
))
908 if (!str
|| ! str
[0] ) return LB_ERR
;
911 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
912 if (!lstrcmpiW( str
, item
->str
)) return i
;
913 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
914 if (!lstrcmpiW( str
, item
->str
)) return i
;
918 /* Special case for drives and directories: ignore prefix */
919 #define CHECK_DRIVE(item) \
920 if ((item)->str[0] == '[') \
922 if (!strncmpiW( str, (item)->str+1, len )) return i; \
923 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
927 INT len
= strlenW(str
);
928 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
930 if (!strncmpiW( str
, item
->str
, len
)) return i
;
933 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
935 if (!strncmpiW( str
, item
->str
, len
)) return i
;
943 if (exact
&& (descr
->style
& LBS_SORT
))
944 /* If sorted, use a WM_COMPAREITEM binary search */
945 return LISTBOX_FindStringPos( hwnd
, descr
, str
, TRUE
);
947 /* Otherwise use a linear search */
948 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
949 if (item
->data
== (DWORD
)str
) return i
;
950 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
951 if (item
->data
== (DWORD
)str
) return i
;
957 /***********************************************************************
958 * LISTBOX_GetSelCount
960 static LRESULT
LISTBOX_GetSelCount( LB_DESCR
*descr
)
963 LB_ITEMDATA
*item
= descr
->items
;
965 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
966 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
967 if (item
->selected
) count
++;
972 /***********************************************************************
973 * LISTBOX_GetSelItems16
975 static LRESULT
LISTBOX_GetSelItems16( LB_DESCR
*descr
, INT16 max
, LPINT16 array
)
978 LB_ITEMDATA
*item
= descr
->items
;
980 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
981 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
982 if (item
->selected
) array
[count
++] = (INT16
)i
;
987 /***********************************************************************
988 * LISTBOX_GetSelItems
990 static LRESULT
LISTBOX_GetSelItems( LB_DESCR
*descr
, INT max
, LPINT array
)
993 LB_ITEMDATA
*item
= descr
->items
;
995 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
996 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
997 if (item
->selected
) array
[count
++] = i
;
1002 /***********************************************************************
1005 static LRESULT
LISTBOX_Paint( HWND hwnd
, LB_DESCR
*descr
, HDC hdc
)
1007 INT i
, col_pos
= descr
->page_size
- 1;
1009 RECT focusRect
= {-1, -1, -1, -1};
1011 HBRUSH hbrush
, oldBrush
= 0;
1013 if (descr
->style
& LBS_NOREDRAW
) return 0;
1015 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1016 if (descr
->style
& LBS_MULTICOLUMN
)
1017 rect
.right
= rect
.left
+ descr
->column_width
;
1018 else if (descr
->horz_pos
)
1020 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1021 rect
.right
+= descr
->horz_pos
;
1024 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1025 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
1026 (WPARAM
)hdc
, (LPARAM
)hwnd
);
1027 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1028 if (!IsWindowEnabled(hwnd
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1030 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1033 /* Special case for empty listbox: paint focus rect */
1034 rect
.bottom
= rect
.top
+ descr
->item_height
;
1035 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, descr
->focus_item
,
1037 rect
.top
= rect
.bottom
;
1040 /* Paint all the item, regarding the selection
1041 Focus state will be painted after */
1043 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1045 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1046 rect
.bottom
= rect
.top
+ descr
->item_height
;
1048 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1050 if (i
== descr
->focus_item
)
1052 /* keep the focus rect, to paint the focus item after */
1053 focusRect
.left
= rect
.left
;
1054 focusRect
.right
= rect
.right
;
1055 focusRect
.top
= rect
.top
;
1056 focusRect
.bottom
= rect
.bottom
;
1058 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1059 rect
.top
= rect
.bottom
;
1061 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1063 if (!IS_OWNERDRAW(descr
))
1065 /* Clear the bottom of the column */
1066 if (rect
.top
< descr
->height
)
1068 rect
.bottom
= descr
->height
;
1069 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1070 &rect
, NULL
, 0, NULL
);
1074 /* Go to the next column */
1075 rect
.left
+= descr
->column_width
;
1076 rect
.right
+= descr
->column_width
;
1078 col_pos
= descr
->page_size
- 1;
1083 if (rect
.top
>= descr
->height
) break;
1087 /* Paint the focus item now */
1088 if (focusRect
.top
!= focusRect
.bottom
&& descr
->caret_on
)
1089 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1091 if (!IS_OWNERDRAW(descr
))
1093 /* Clear the remainder of the client area */
1094 if (rect
.top
< descr
->height
)
1096 rect
.bottom
= descr
->height
;
1097 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1098 &rect
, NULL
, 0, NULL
);
1100 if (rect
.right
< descr
->width
)
1102 rect
.left
= rect
.right
;
1103 rect
.right
= descr
->width
;
1105 rect
.bottom
= descr
->height
;
1106 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1107 &rect
, NULL
, 0, NULL
);
1110 if (oldFont
) SelectObject( hdc
, oldFont
);
1111 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1116 /***********************************************************************
1117 * LISTBOX_InvalidateItems
1119 * Invalidate all items from a given item. If the specified item is not
1120 * visible, nothing happens.
1122 static void LISTBOX_InvalidateItems( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1126 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1128 if (descr
->style
& LBS_NOREDRAW
)
1130 descr
->style
|= LBS_DISPLAYCHANGED
;
1133 rect
.bottom
= descr
->height
;
1134 InvalidateRect( hwnd
, &rect
, TRUE
);
1135 if (descr
->style
& LBS_MULTICOLUMN
)
1137 /* Repaint the other columns */
1138 rect
.left
= rect
.right
;
1139 rect
.right
= descr
->width
;
1141 InvalidateRect( hwnd
, &rect
, TRUE
);
1146 static void LISTBOX_InvalidateItemRect( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1150 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1151 InvalidateRect( hwnd
, &rect
, TRUE
);
1154 /***********************************************************************
1155 * LISTBOX_GetItemHeight
1157 static LRESULT
LISTBOX_GetItemHeight( LB_DESCR
*descr
, INT index
)
1159 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1161 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1162 return descr
->items
[index
].height
;
1164 else return descr
->item_height
;
1168 /***********************************************************************
1169 * LISTBOX_SetItemHeight
1171 static LRESULT
LISTBOX_SetItemHeight( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1172 INT height
, BOOL repaint
)
1174 if (!height
) height
= 1;
1176 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1178 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1179 TRACE("[%p]: item %d height = %d\n", hwnd
, index
, height
);
1180 descr
->items
[index
].height
= height
;
1181 LISTBOX_UpdateScroll( hwnd
, descr
);
1183 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1185 else if (height
!= descr
->item_height
)
1187 TRACE("[%p]: new height = %d\n", hwnd
, height
);
1188 descr
->item_height
= height
;
1189 LISTBOX_UpdatePage( hwnd
, descr
);
1190 LISTBOX_UpdateScroll( hwnd
, descr
);
1192 InvalidateRect( hwnd
, 0, TRUE
);
1198 /***********************************************************************
1199 * LISTBOX_SetHorizontalPos
1201 static void LISTBOX_SetHorizontalPos( HWND hwnd
, LB_DESCR
*descr
, INT pos
)
1205 if (pos
> descr
->horz_extent
- descr
->width
)
1206 pos
= descr
->horz_extent
- descr
->width
;
1207 if (pos
< 0) pos
= 0;
1208 if (!(diff
= descr
->horz_pos
- pos
)) return;
1209 TRACE("[%p]: new horz pos = %d\n", hwnd
, pos
);
1210 descr
->horz_pos
= pos
;
1211 LISTBOX_UpdateScroll( hwnd
, descr
);
1212 if (abs(diff
) < descr
->width
)
1213 ScrollWindowEx( hwnd
, diff
, 0, NULL
, NULL
, 0, NULL
,
1214 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1216 InvalidateRect( hwnd
, NULL
, TRUE
);
1220 /***********************************************************************
1221 * LISTBOX_SetHorizontalExtent
1223 static LRESULT
LISTBOX_SetHorizontalExtent( HWND hwnd
, LB_DESCR
*descr
,
1226 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1228 if (extent
<= 0) extent
= 1;
1229 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1230 TRACE("[%p]: new horz extent = %d\n", hwnd
, extent
);
1231 descr
->horz_extent
= extent
;
1232 if (descr
->horz_pos
> extent
- descr
->width
)
1233 LISTBOX_SetHorizontalPos( hwnd
, descr
, extent
- descr
->width
);
1235 LISTBOX_UpdateScroll( hwnd
, descr
);
1240 /***********************************************************************
1241 * LISTBOX_SetColumnWidth
1243 static LRESULT
LISTBOX_SetColumnWidth( HWND hwnd
, LB_DESCR
*descr
, INT width
)
1245 if (width
== descr
->column_width
) return LB_OKAY
;
1246 TRACE("[%p]: new column width = %d\n", hwnd
, width
);
1247 descr
->column_width
= width
;
1248 LISTBOX_UpdatePage( hwnd
, descr
);
1253 /***********************************************************************
1256 * Returns the item height.
1258 static INT
LISTBOX_SetFont( HWND hwnd
, LB_DESCR
*descr
, HFONT font
)
1266 if (!(hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
)))
1268 ERR("unable to get DC.\n" );
1271 if (font
) oldFont
= SelectObject( hdc
, font
);
1272 GetTextMetricsW( hdc
, &tm
);
1273 if (oldFont
) SelectObject( hdc
, oldFont
);
1274 ReleaseDC( hwnd
, hdc
);
1275 if (!IS_OWNERDRAW(descr
))
1276 LISTBOX_SetItemHeight( hwnd
, descr
, 0, tm
.tmHeight
, FALSE
);
1277 return tm
.tmHeight
;
1281 /***********************************************************************
1282 * LISTBOX_MakeItemVisible
1284 * Make sure that a given item is partially or fully visible.
1286 static void LISTBOX_MakeItemVisible( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1291 if (index
<= descr
->top_item
) top
= index
;
1292 else if (descr
->style
& LBS_MULTICOLUMN
)
1294 INT cols
= descr
->width
;
1295 if (!fully
) cols
+= descr
->column_width
- 1;
1296 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1298 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1299 top
= index
- descr
->page_size
* (cols
- 1);
1301 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1303 INT height
= fully
? descr
->items
[index
].height
: 1;
1304 for (top
= index
; top
> descr
->top_item
; top
--)
1305 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1309 if (index
< descr
->top_item
+ descr
->page_size
) return;
1310 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1311 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1312 top
= index
- descr
->page_size
+ 1;
1314 LISTBOX_SetTopItem( hwnd
, descr
, top
, TRUE
);
1317 /***********************************************************************
1318 * LISTBOX_SetCaretIndex
1321 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1324 static LRESULT
LISTBOX_SetCaretIndex( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1325 BOOL fully_visible
)
1327 INT oldfocus
= descr
->focus_item
;
1329 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1330 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1331 if (index
== oldfocus
) return LB_OKAY
;
1332 descr
->focus_item
= index
;
1333 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1334 LISTBOX_RepaintItem( hwnd
, descr
, oldfocus
, ODA_FOCUS
);
1336 LISTBOX_MakeItemVisible( hwnd
, descr
, index
, fully_visible
);
1337 if (descr
->caret_on
&& (descr
->in_focus
))
1338 LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_FOCUS
);
1344 /***********************************************************************
1345 * LISTBOX_SelectItemRange
1347 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1349 static LRESULT
LISTBOX_SelectItemRange( HWND hwnd
, LB_DESCR
*descr
, INT first
,
1354 /* A few sanity checks */
1356 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1357 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1358 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1359 if (last
== -1) last
= descr
->nb_items
- 1;
1360 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1361 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1362 /* selected_item reflects last selected/unselected item on multiple sel */
1363 descr
->selected_item
= last
;
1365 if (on
) /* Turn selection on */
1367 for (i
= first
; i
<= last
; i
++)
1369 if (descr
->items
[i
].selected
) continue;
1370 descr
->items
[i
].selected
= TRUE
;
1371 LISTBOX_InvalidateItemRect(hwnd
, descr
, i
);
1373 LISTBOX_SetCaretIndex( hwnd
, descr
, last
, TRUE
);
1375 else /* Turn selection off */
1377 for (i
= first
; i
<= last
; i
++)
1379 if (!descr
->items
[i
].selected
) continue;
1380 descr
->items
[i
].selected
= FALSE
;
1381 LISTBOX_InvalidateItemRect(hwnd
, descr
, i
);
1387 /***********************************************************************
1388 * LISTBOX_SetSelection
1390 static LRESULT
LISTBOX_SetSelection( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1391 BOOL on
, BOOL send_notify
)
1393 TRACE( "index=%d notify=%s\n", index
, send_notify
? "YES" : "NO" );
1395 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1396 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1397 if (descr
->style
& LBS_MULTIPLESEL
)
1399 if (index
== -1) /* Select all items */
1400 return LISTBOX_SelectItemRange( hwnd
, descr
, 0, -1, on
);
1401 else /* Only one item */
1402 return LISTBOX_SelectItemRange( hwnd
, descr
, index
, index
, on
);
1406 INT oldsel
= descr
->selected_item
;
1407 if (index
== oldsel
) return LB_OKAY
;
1408 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1409 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1410 descr
->selected_item
= index
;
1411 if (oldsel
!= -1) LISTBOX_RepaintItem( hwnd
, descr
, oldsel
, ODA_SELECT
);
1412 if (index
!= -1) LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_SELECT
);
1413 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( hwnd
, descr
,
1414 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1416 if( descr
->lphc
) /* set selection change flag for parent combo */
1417 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1423 /***********************************************************************
1426 * Change the caret position and extend the selection to the new caret.
1428 static void LISTBOX_MoveCaret( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1429 BOOL fully_visible
)
1431 INT oldfocus
= descr
->focus_item
;
1433 if ((index
< 0) || (index
>= descr
->nb_items
))
1436 /* Important, repaint needs to be done in this order if
1437 you want to mimic Windows behavior:
1438 1. Remove the focus and paint the item
1439 2. Remove the selection and paint the item(s)
1440 3. Set the selection and repaint the item(s)
1441 4. Set the focus to 'index' and repaint the item */
1443 /* 1. remove the focus and repaint the item */
1444 descr
->focus_item
= -1;
1445 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1446 LISTBOX_RepaintItem( hwnd
, descr
, oldfocus
, ODA_FOCUS
);
1448 /* 2. then turn off the previous selection */
1449 /* 3. repaint the new selected item */
1450 if (descr
->style
& LBS_EXTENDEDSEL
)
1452 if (descr
->anchor_item
!= -1)
1454 INT first
= min( index
, descr
->anchor_item
);
1455 INT last
= max( index
, descr
->anchor_item
);
1457 LISTBOX_SelectItemRange( hwnd
, descr
, 0, first
- 1, FALSE
);
1458 LISTBOX_SelectItemRange( hwnd
, descr
, last
+ 1, -1, FALSE
);
1459 LISTBOX_SelectItemRange( hwnd
, descr
, first
, last
, TRUE
);
1462 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1464 /* Set selection to new caret item */
1465 LISTBOX_SetSelection( hwnd
, descr
, index
, TRUE
, FALSE
);
1468 /* 4. repaint the new item with the focus */
1469 descr
->focus_item
= index
;
1470 LISTBOX_MakeItemVisible( hwnd
, descr
, index
, fully_visible
);
1471 if (descr
->caret_on
&& (descr
->in_focus
))
1472 LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_FOCUS
);
1476 /***********************************************************************
1477 * LISTBOX_InsertItem
1479 static LRESULT
LISTBOX_InsertItem( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1480 LPWSTR str
, DWORD data
)
1484 INT oldfocus
= descr
->focus_item
;
1486 if (index
== -1) index
= descr
->nb_items
;
1487 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1488 if (!descr
->items
) max_items
= 0;
1489 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1490 if (descr
->nb_items
== max_items
)
1492 /* We need to grow the array */
1493 max_items
+= LB_ARRAY_GRANULARITY
;
1495 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1496 max_items
* sizeof(LB_ITEMDATA
) );
1498 item
= HeapAlloc( GetProcessHeap(), 0,
1499 max_items
* sizeof(LB_ITEMDATA
) );
1502 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
1505 descr
->items
= item
;
1508 /* Insert the item structure */
1510 item
= &descr
->items
[index
];
1511 if (index
< descr
->nb_items
)
1512 RtlMoveMemory( item
+ 1, item
,
1513 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1517 item
->selected
= FALSE
;
1520 /* Get item height */
1522 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1524 MEASUREITEMSTRUCT mis
;
1525 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
1527 mis
.CtlType
= ODT_LISTBOX
;
1530 mis
.itemData
= descr
->items
[index
].data
;
1531 mis
.itemHeight
= descr
->item_height
;
1532 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1533 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1534 TRACE("[%p]: measure item %d (%s) = %d\n",
1535 hwnd
, index
, str
? debugstr_w(str
) : "", item
->height
);
1538 /* Repaint the items */
1540 LISTBOX_UpdateScroll( hwnd
, descr
);
1541 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1543 /* Move selection and focused item */
1544 /* If listbox was empty, set focus to the first item */
1545 if (descr
->nb_items
== 1)
1546 LISTBOX_SetCaretIndex( hwnd
, descr
, 0, FALSE
);
1547 /* single select don't change selection index in win31 */
1548 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1550 descr
->selected_item
++;
1551 LISTBOX_SetSelection( hwnd
, descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1555 if (index
<= descr
->selected_item
)
1557 descr
->selected_item
++;
1558 descr
->focus_item
= oldfocus
; /* focus not changed */
1565 /***********************************************************************
1566 * LISTBOX_InsertString
1568 static LRESULT
LISTBOX_InsertString( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1571 LPWSTR new_str
= NULL
;
1575 if (HAS_STRINGS(descr
))
1577 static const WCHAR empty_stringW
[] = { 0 };
1578 if (!str
) str
= empty_stringW
;
1579 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1581 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
1584 strcpyW(new_str
, str
);
1586 else data
= (DWORD
)str
;
1588 if (index
== -1) index
= descr
->nb_items
;
1589 if ((ret
= LISTBOX_InsertItem( hwnd
, descr
, index
, new_str
, data
)) != 0)
1591 if (new_str
) HeapFree( GetProcessHeap(), 0, new_str
);
1595 TRACE("[%p]: added item %d %s\n",
1596 hwnd
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1601 /***********************************************************************
1602 * LISTBOX_DeleteItem
1604 * Delete the content of an item. 'index' must be a valid index.
1606 static void LISTBOX_DeleteItem( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1608 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1609 * while Win95 sends it for all items with user data.
1610 * It's probably better to send it too often than not
1611 * often enough, so this is what we do here.
1613 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1615 DELETEITEMSTRUCT dis
;
1616 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
1618 dis
.CtlType
= ODT_LISTBOX
;
1621 dis
.hwndItem
= hwnd
;
1622 dis
.itemData
= descr
->items
[index
].data
;
1623 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1625 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1626 HeapFree( GetProcessHeap(), 0, descr
->items
[index
].str
);
1630 /***********************************************************************
1631 * LISTBOX_RemoveItem
1633 * Remove an item from the listbox and delete its content.
1635 static LRESULT
LISTBOX_RemoveItem( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1640 if ((index
== -1) && (descr
->nb_items
> 0)) index
= descr
->nb_items
- 1;
1641 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1643 /* We need to invalidate the original rect instead of the updated one. */
1644 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1646 LISTBOX_DeleteItem( hwnd
, descr
, index
);
1648 /* Remove the item */
1650 item
= &descr
->items
[index
];
1651 if (index
< descr
->nb_items
-1)
1652 RtlMoveMemory( item
, item
+ 1,
1653 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1655 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1657 /* Shrink the item array if possible */
1659 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1660 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1662 max_items
-= LB_ARRAY_GRANULARITY
;
1663 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1664 max_items
* sizeof(LB_ITEMDATA
) );
1665 if (item
) descr
->items
= item
;
1667 /* Repaint the items */
1669 LISTBOX_UpdateScroll( hwnd
, descr
);
1670 /* if we removed the scrollbar, reset the top of the list
1671 (correct for owner-drawn ???) */
1672 if (descr
->nb_items
== descr
->page_size
)
1673 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1675 /* Move selection and focused item */
1676 if (!IS_MULTISELECT(descr
))
1678 if (index
== descr
->selected_item
)
1679 descr
->selected_item
= -1;
1680 else if (index
< descr
->selected_item
)
1682 descr
->selected_item
--;
1683 if (ISWIN31
) /* win 31 do not change the selected item number */
1684 LISTBOX_SetSelection( hwnd
, descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1688 if (descr
->focus_item
>= descr
->nb_items
)
1690 descr
->focus_item
= descr
->nb_items
- 1;
1691 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1697 /***********************************************************************
1698 * LISTBOX_ResetContent
1700 static void LISTBOX_ResetContent( HWND hwnd
, LB_DESCR
*descr
)
1704 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( hwnd
, descr
, i
);
1705 if (descr
->items
) HeapFree( GetProcessHeap(), 0, descr
->items
);
1706 descr
->nb_items
= 0;
1707 descr
->top_item
= 0;
1708 descr
->selected_item
= -1;
1709 descr
->focus_item
= 0;
1710 descr
->anchor_item
= -1;
1711 descr
->items
= NULL
;
1715 /***********************************************************************
1718 static LRESULT
LISTBOX_SetCount( HWND hwnd
, LB_DESCR
*descr
, INT count
)
1722 if (HAS_STRINGS(descr
)) return LB_ERR
;
1723 /* FIXME: this is far from optimal... */
1724 if (count
> descr
->nb_items
)
1726 while (count
> descr
->nb_items
)
1727 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, -1, 0 )) < 0)
1730 else if (count
< descr
->nb_items
)
1732 while (count
< descr
->nb_items
)
1733 if ((ret
= LISTBOX_RemoveItem( hwnd
, descr
, -1 )) < 0)
1740 /***********************************************************************
1743 static LRESULT
LISTBOX_Directory( HWND hwnd
, LB_DESCR
*descr
, UINT attrib
,
1744 LPCWSTR filespec
, BOOL long_names
)
1747 LRESULT ret
= LB_OKAY
;
1748 WIN32_FIND_DATAW entry
;
1751 /* don't scan directory if we just want drives exclusively */
1752 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1753 /* scan directory */
1754 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1756 int le
= GetLastError();
1757 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1764 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1766 static const WCHAR bracketW
[] = { ']',0 };
1767 static const WCHAR dotW
[] = { '.',0 };
1768 if (!(attrib
& DDL_DIRECTORY
) ||
1769 !strcmpW( entry
.cAlternateFileName
, dotW
)) continue;
1771 if (long_names
) strcpyW( buffer
+ 1, entry
.cFileName
);
1772 else strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1773 strcatW(buffer
, bracketW
);
1775 else /* not a directory */
1777 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1778 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1780 if ((attrib
& DDL_EXCLUSIVE
) &&
1781 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1784 if (long_names
) strcpyW( buffer
, entry
.cFileName
);
1785 else strcpyW( buffer
, entry
.cAlternateFileName
);
1787 if (!long_names
) CharLowerW( buffer
);
1788 pos
= LISTBOX_FindFileStrPos( hwnd
, descr
, buffer
);
1789 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, pos
, buffer
)) < 0)
1791 } while (FindNextFileW( handle
, &entry
));
1792 FindClose( handle
);
1797 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1799 WCHAR buffer
[] = {'[','-','a','-',']',0};
1800 WCHAR root
[] = {'A',':','\\',0};
1802 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1804 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1805 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, -1, buffer
)) < 0)
1813 /***********************************************************************
1814 * LISTBOX_HandleVScroll
1816 static LRESULT
LISTBOX_HandleVScroll( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1820 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1821 switch(LOWORD(wParam
))
1824 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
- 1, TRUE
);
1827 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+ 1, TRUE
);
1830 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
-
1831 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1834 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+
1835 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1837 case SB_THUMBPOSITION
:
1838 LISTBOX_SetTopItem( hwnd
, descr
, HIWORD(wParam
), TRUE
);
1841 info
.cbSize
= sizeof(info
);
1842 info
.fMask
= SIF_TRACKPOS
;
1843 GetScrollInfo( hwnd
, SB_VERT
, &info
);
1844 LISTBOX_SetTopItem( hwnd
, descr
, info
.nTrackPos
, TRUE
);
1847 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1850 LISTBOX_SetTopItem( hwnd
, descr
, descr
->nb_items
, TRUE
);
1857 /***********************************************************************
1858 * LISTBOX_HandleHScroll
1860 static LRESULT
LISTBOX_HandleHScroll( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1865 if (descr
->style
& LBS_MULTICOLUMN
)
1867 switch(LOWORD(wParam
))
1870 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
-descr
->page_size
,
1874 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+descr
->page_size
,
1878 page
= descr
->width
/ descr
->column_width
;
1879 if (page
< 1) page
= 1;
1880 LISTBOX_SetTopItem( hwnd
, descr
,
1881 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1884 page
= descr
->width
/ descr
->column_width
;
1885 if (page
< 1) page
= 1;
1886 LISTBOX_SetTopItem( hwnd
, descr
,
1887 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1889 case SB_THUMBPOSITION
:
1890 LISTBOX_SetTopItem( hwnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1894 info
.cbSize
= sizeof(info
);
1895 info
.fMask
= SIF_TRACKPOS
;
1896 GetScrollInfo( hwnd
, SB_VERT
, &info
);
1897 LISTBOX_SetTopItem( hwnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1901 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1904 LISTBOX_SetTopItem( hwnd
, descr
, descr
->nb_items
, TRUE
);
1908 else if (descr
->horz_extent
)
1910 switch(LOWORD(wParam
))
1913 LISTBOX_SetHorizontalPos( hwnd
, descr
, descr
->horz_pos
- 1 );
1916 LISTBOX_SetHorizontalPos( hwnd
, descr
, descr
->horz_pos
+ 1 );
1919 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1920 descr
->horz_pos
- descr
->width
);
1923 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1924 descr
->horz_pos
+ descr
->width
);
1926 case SB_THUMBPOSITION
:
1927 LISTBOX_SetHorizontalPos( hwnd
, descr
, HIWORD(wParam
) );
1930 info
.cbSize
= sizeof(info
);
1931 info
.fMask
= SIF_TRACKPOS
;
1932 GetScrollInfo( hwnd
, SB_HORZ
, &info
);
1933 LISTBOX_SetHorizontalPos( hwnd
, descr
, info
.nTrackPos
);
1936 LISTBOX_SetHorizontalPos( hwnd
, descr
, 0 );
1939 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1940 descr
->horz_extent
- descr
->width
);
1947 static LRESULT
LISTBOX_HandleMouseWheel(HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1949 short gcWheelDelta
= 0;
1950 UINT pulScrollLines
= 3;
1952 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1954 gcWheelDelta
-= (short) HIWORD(wParam
);
1956 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
1958 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
1959 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
1960 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+ cLineScroll
, TRUE
);
1965 /***********************************************************************
1966 * LISTBOX_HandleLButtonDown
1968 static LRESULT
LISTBOX_HandleLButtonDown( HWND hwnd
, LB_DESCR
*descr
,
1969 WPARAM wParam
, INT x
, INT y
)
1971 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
1972 TRACE("[%p]: lbuttondown %d,%d item %d\n", hwnd
, x
, y
, index
);
1973 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
1975 if (!descr
->in_focus
)
1977 if( !descr
->lphc
) SetFocus( hwnd
);
1978 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
1981 if (index
== -1) return 0;
1983 if (descr
->style
& LBS_EXTENDEDSEL
)
1985 /* we should perhaps make sure that all items are deselected
1986 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1987 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1988 LISTBOX_SetSelection( hwnd, descr, -1, FALSE, FALSE);
1991 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1992 if (wParam
& MK_CONTROL
)
1994 LISTBOX_SetCaretIndex( hwnd
, descr
, index
, FALSE
);
1995 LISTBOX_SetSelection( hwnd
, descr
, index
,
1996 !descr
->items
[index
].selected
,
1997 (descr
->style
& LBS_NOTIFY
) != 0);
1999 else LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
2003 LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
2004 LISTBOX_SetSelection( hwnd
, descr
, index
,
2005 (!(descr
->style
& LBS_MULTIPLESEL
) ||
2006 !descr
->items
[index
].selected
),
2007 (descr
->style
& LBS_NOTIFY
) != 0 );
2010 descr
->captured
= TRUE
;
2015 if (descr
->style
& LBS_NOTIFY
)
2016 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2017 MAKELPARAM( x
, y
) );
2018 if (GetWindowLongA( hwnd
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2025 if (DragDetect( hwnd
, pt
))
2026 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2033 /*************************************************************************
2034 * LISTBOX_HandleLButtonDownCombo [Internal]
2036 * Process LButtonDown message for the ComboListBox
2039 * pWnd [I] The windows internal structure
2040 * pDescr [I] The ListBox internal structure
2041 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2042 * x [I] X Mouse Coordinate
2043 * y [I] Y Mouse Coordinate
2046 * 0 since we are processing the WM_LBUTTONDOWN Message
2049 * This function is only to be used when a ListBox is a ComboListBox
2052 static LRESULT
LISTBOX_HandleLButtonDownCombo( HWND hwnd
, LB_DESCR
*pDescr
,
2053 UINT msg
, WPARAM wParam
, INT x
, INT y
)
2055 RECT clientRect
, screenRect
;
2061 GetClientRect(hwnd
, &clientRect
);
2063 if(PtInRect(&clientRect
, mousePos
))
2065 /* MousePos is in client, resume normal processing */
2066 if (msg
== WM_LBUTTONDOWN
)
2068 pDescr
->lphc
->droppedIndex
= pDescr
->nb_items
? pDescr
->selected_item
: -1;
2069 return LISTBOX_HandleLButtonDown( hwnd
, pDescr
, wParam
, x
, y
);
2071 else if (pDescr
->style
& LBS_NOTIFY
)
2072 SEND_NOTIFICATION( hwnd
, pDescr
, LBN_DBLCLK
);
2077 POINT screenMousePos
;
2078 HWND hWndOldCapture
;
2080 /* Check the Non-Client Area */
2081 screenMousePos
= mousePos
;
2082 hWndOldCapture
= GetCapture();
2084 GetWindowRect(hwnd
, &screenRect
);
2085 ClientToScreen(hwnd
, &screenMousePos
);
2087 if(!PtInRect(&screenRect
, screenMousePos
))
2089 LISTBOX_SetCaretIndex( hwnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
);
2090 LISTBOX_SetSelection( hwnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2091 COMBO_FlipListbox( pDescr
->lphc
, FALSE
, FALSE
);
2096 /* Check to see the NC is a scrollbar */
2098 LONG style
= GetWindowLongA( hwnd
, GWL_STYLE
);
2099 /* Check Vertical scroll bar */
2100 if (style
& WS_VSCROLL
)
2102 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2103 if (PtInRect( &clientRect
, mousePos
))
2105 nHitTestType
= HTVSCROLL
;
2108 /* Check horizontal scroll bar */
2109 if (style
& WS_HSCROLL
)
2111 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2112 if (PtInRect( &clientRect
, mousePos
))
2114 nHitTestType
= HTHSCROLL
;
2117 /* Windows sends this message when a scrollbar is clicked
2120 if(nHitTestType
!= 0)
2122 SendMessageW(hwnd
, WM_NCLBUTTONDOWN
, nHitTestType
,
2123 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2125 /* Resume the Capture after scrolling is complete
2127 if(hWndOldCapture
!= 0)
2129 SetCapture(hWndOldCapture
);
2136 /***********************************************************************
2137 * LISTBOX_HandleLButtonUp
2139 static LRESULT
LISTBOX_HandleLButtonUp( HWND hwnd
, LB_DESCR
*descr
)
2141 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2142 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2143 LISTBOX_Timer
= LB_TIMER_NONE
;
2144 if (descr
->captured
)
2146 descr
->captured
= FALSE
;
2147 if (GetCapture() == hwnd
) ReleaseCapture();
2148 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2149 SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2155 /***********************************************************************
2156 * LISTBOX_HandleTimer
2158 * Handle scrolling upon a timer event.
2159 * Return TRUE if scrolling should continue.
2161 static LRESULT
LISTBOX_HandleTimer( HWND hwnd
, LB_DESCR
*descr
,
2162 INT index
, TIMER_DIRECTION dir
)
2167 if (descr
->top_item
) index
= descr
->top_item
- 1;
2171 if (descr
->top_item
) index
-= descr
->page_size
;
2174 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2175 if (index
== descr
->focus_item
) index
++;
2176 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2178 case LB_TIMER_RIGHT
:
2179 if (index
+ descr
->page_size
< descr
->nb_items
)
2180 index
+= descr
->page_size
;
2185 if (index
== descr
->focus_item
) return FALSE
;
2186 LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
2191 /***********************************************************************
2192 * LISTBOX_HandleSystemTimer
2194 * WM_SYSTIMER handler.
2196 static LRESULT
LISTBOX_HandleSystemTimer( HWND hwnd
, LB_DESCR
*descr
)
2198 if (!LISTBOX_HandleTimer( hwnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
2200 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2201 LISTBOX_Timer
= LB_TIMER_NONE
;
2207 /***********************************************************************
2208 * LISTBOX_HandleMouseMove
2210 * WM_MOUSEMOVE handler.
2212 static void LISTBOX_HandleMouseMove( HWND hwnd
, LB_DESCR
*descr
,
2216 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2218 if (!descr
->captured
) return;
2220 if (descr
->style
& LBS_MULTICOLUMN
)
2223 else if (y
>= descr
->item_height
* descr
->page_size
)
2224 y
= descr
->item_height
* descr
->page_size
- 1;
2228 dir
= LB_TIMER_LEFT
;
2231 else if (x
>= descr
->width
)
2233 dir
= LB_TIMER_RIGHT
;
2234 x
= descr
->width
- 1;
2239 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2240 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2243 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2244 if (index
== -1) index
= descr
->focus_item
;
2245 if (!LISTBOX_HandleTimer( hwnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2247 /* Start/stop the system timer */
2249 if (dir
!= LB_TIMER_NONE
)
2250 SetSystemTimer( hwnd
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2251 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2252 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2253 LISTBOX_Timer
= dir
;
2257 /***********************************************************************
2258 * LISTBOX_HandleKeyDown
2260 static LRESULT
LISTBOX_HandleKeyDown( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
2263 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2264 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2265 bForceSelection
= FALSE
; /* only for single select list */
2267 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2269 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2270 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2272 if (caret
== -2) return 0;
2274 if (caret
== -1) switch(wParam
)
2277 if (descr
->style
& LBS_MULTICOLUMN
)
2279 bForceSelection
= FALSE
;
2280 if (descr
->focus_item
>= descr
->page_size
)
2281 caret
= descr
->focus_item
- descr
->page_size
;
2286 caret
= descr
->focus_item
- 1;
2287 if (caret
< 0) caret
= 0;
2290 if (descr
->style
& LBS_MULTICOLUMN
)
2292 bForceSelection
= FALSE
;
2293 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2294 caret
= descr
->focus_item
+ descr
->page_size
;
2299 caret
= descr
->focus_item
+ 1;
2300 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2304 if (descr
->style
& LBS_MULTICOLUMN
)
2306 INT page
= descr
->width
/ descr
->column_width
;
2307 if (page
< 1) page
= 1;
2308 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2310 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2311 if (caret
< 0) caret
= 0;
2314 if (descr
->style
& LBS_MULTICOLUMN
)
2316 INT page
= descr
->width
/ descr
->column_width
;
2317 if (page
< 1) page
= 1;
2318 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2320 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2321 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2327 caret
= descr
->nb_items
- 1;
2330 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2331 else if (descr
->style
& LBS_MULTIPLESEL
)
2333 LISTBOX_SetSelection( hwnd
, descr
, descr
->focus_item
,
2334 !descr
->items
[descr
->focus_item
].selected
,
2335 (descr
->style
& LBS_NOTIFY
) != 0 );
2339 bForceSelection
= FALSE
;
2341 if (bForceSelection
) /* focused item is used instead of key */
2342 caret
= descr
->focus_item
;
2345 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
2346 !(GetKeyState( VK_SHIFT
) & 0x8000))
2347 descr
->anchor_item
= caret
;
2348 LISTBOX_MoveCaret( hwnd
, descr
, caret
, TRUE
);
2349 LISTBOX_SetSelection( hwnd
, descr
, caret
, TRUE
, FALSE
);
2350 if (descr
->style
& LBS_NOTIFY
)
2354 /* make sure that combo parent doesn't hide us */
2355 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2357 if (descr
->nb_items
) SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2364 /***********************************************************************
2365 * LISTBOX_HandleChar
2367 static LRESULT
LISTBOX_HandleChar( HWND hwnd
, LB_DESCR
*descr
, WCHAR charW
)
2375 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2377 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2378 MAKEWPARAM(charW
, descr
->focus_item
),
2380 if (caret
== -2) return 0;
2383 caret
= LISTBOX_FindString( hwnd
, descr
, descr
->focus_item
, str
, FALSE
);
2386 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2387 LISTBOX_SetSelection( hwnd
, descr
, caret
, TRUE
, FALSE
);
2388 LISTBOX_MoveCaret( hwnd
, descr
, caret
, TRUE
);
2389 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2390 SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2396 /***********************************************************************
2399 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2402 MEASUREITEMSTRUCT mis
;
2405 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2408 GetClientRect( hwnd
, &rect
);
2409 descr
->owner
= GetParent( hwnd
);
2410 descr
->style
= GetWindowLongA( hwnd
, GWL_STYLE
);
2411 descr
->width
= rect
.right
- rect
.left
;
2412 descr
->height
= rect
.bottom
- rect
.top
;
2413 descr
->items
= NULL
;
2414 descr
->nb_items
= 0;
2415 descr
->top_item
= 0;
2416 descr
->selected_item
= -1;
2417 descr
->focus_item
= 0;
2418 descr
->anchor_item
= -1;
2419 descr
->item_height
= 1;
2420 descr
->page_size
= 1;
2421 descr
->column_width
= 150;
2422 descr
->horz_extent
= (descr
->style
& WS_HSCROLL
) ? 1 : 0;
2423 descr
->horz_pos
= 0;
2426 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2427 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2428 descr
->in_focus
= FALSE
;
2429 descr
->captured
= FALSE
;
2431 descr
->locale
= 0; /* FIXME */
2434 if (is_old_app(hwnd
) && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2436 /* Win95 document "List Box Differences" from MSDN:
2437 If a list box in a version 3.x application has either the
2438 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2439 horizontal and vertical scroll bars.
2441 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2446 TRACE_(combo
)("[%p]: resetting owner %p -> %p\n", hwnd
, descr
->owner
, lphc
->self
);
2447 descr
->owner
= lphc
->self
;
2450 SetWindowLongA( hwnd
, 0, (LONG
)descr
);
2452 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2454 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2455 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2456 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2457 descr
->item_height
= LISTBOX_SetFont( hwnd
, descr
, 0 );
2459 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2461 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2463 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2464 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2468 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
2469 mis
.CtlType
= ODT_LISTBOX
;
2474 mis
.itemHeight
= descr
->item_height
;
2475 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2476 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2480 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2485 /***********************************************************************
2488 static BOOL
LISTBOX_Destroy( HWND hwnd
, LB_DESCR
*descr
)
2490 LISTBOX_ResetContent( hwnd
, descr
);
2491 SetWindowLongA( hwnd
, 0, 0 );
2492 HeapFree( GetProcessHeap(), 0, descr
);
2497 /***********************************************************************
2498 * ListBoxWndProc_common
2500 static LRESULT WINAPI
ListBoxWndProc_common( HWND hwnd
, UINT msg
,
2501 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2506 if (!(descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 )))
2508 if (msg
== WM_CREATE
)
2510 if (!LISTBOX_Create( hwnd
, NULL
))
2512 TRACE("creating wnd=%p descr=%lx\n", hwnd
, GetWindowLongA( hwnd
, 0 ) );
2515 /* Ignore all other messages before we get a WM_CREATE */
2516 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2517 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2520 TRACE("[%p]: msg %s wp %08x lp %08lx\n",
2521 hwnd
, SPY_GetMsgName(msg
, hwnd
), wParam
, lParam
);
2524 case LB_RESETCONTENT16
:
2525 case LB_RESETCONTENT
:
2526 LISTBOX_ResetContent( hwnd
, descr
);
2527 LISTBOX_UpdateScroll( hwnd
, descr
);
2528 InvalidateRect( hwnd
, NULL
, TRUE
);
2531 case LB_ADDSTRING16
:
2532 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2538 if(unicode
|| !HAS_STRINGS(descr
))
2539 textW
= (LPWSTR
)lParam
;
2542 LPSTR textA
= (LPSTR
)lParam
;
2543 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2544 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2545 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2547 wParam
= LISTBOX_FindStringPos( hwnd
, descr
, textW
, FALSE
);
2548 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2549 if (!unicode
&& HAS_STRINGS(descr
))
2550 HeapFree(GetProcessHeap(), 0, textW
);
2554 case LB_INSERTSTRING16
:
2555 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2556 wParam
= (INT
)(INT16
)wParam
;
2558 case LB_INSERTSTRING
:
2562 if(unicode
|| !HAS_STRINGS(descr
))
2563 textW
= (LPWSTR
)lParam
;
2566 LPSTR textA
= (LPSTR
)lParam
;
2567 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2568 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2569 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2571 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2572 if(!unicode
&& HAS_STRINGS(descr
))
2573 HeapFree(GetProcessHeap(), 0, textW
);
2578 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2584 if(unicode
|| !HAS_STRINGS(descr
))
2585 textW
= (LPWSTR
)lParam
;
2588 LPSTR textA
= (LPSTR
)lParam
;
2589 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2590 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2591 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2593 wParam
= LISTBOX_FindFileStrPos( hwnd
, descr
, textW
);
2594 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2595 if(!unicode
&& HAS_STRINGS(descr
))
2596 HeapFree(GetProcessHeap(), 0, textW
);
2600 case LB_DELETESTRING16
:
2601 case LB_DELETESTRING
:
2602 if (LISTBOX_RemoveItem( hwnd
, descr
, wParam
) != LB_ERR
)
2603 return descr
->nb_items
;
2607 case LB_GETITEMDATA16
:
2608 case LB_GETITEMDATA
:
2609 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2611 return descr
->items
[wParam
].data
;
2613 case LB_SETITEMDATA16
:
2614 case LB_SETITEMDATA
:
2615 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2617 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2622 return descr
->nb_items
;
2625 lParam
= (LPARAM
)MapSL(lParam
);
2628 return LISTBOX_GetText( descr
, wParam
, lParam
, unicode
);
2630 case LB_GETTEXTLEN16
:
2633 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2635 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2636 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2637 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2638 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2640 case LB_GETCURSEL16
:
2642 if (descr
->nb_items
==0)
2644 if (!IS_MULTISELECT(descr
))
2645 return descr
->selected_item
;
2647 if (descr
->selected_item
!=-1)
2648 return descr
->selected_item
;
2650 return descr
->focus_item
;
2651 /* otherwise, if the user tries to move the selection with the */
2652 /* arrow keys, we will give the application something to choke on */
2653 case LB_GETTOPINDEX16
:
2654 case LB_GETTOPINDEX
:
2655 return descr
->top_item
;
2657 case LB_GETITEMHEIGHT16
:
2658 case LB_GETITEMHEIGHT
:
2659 return LISTBOX_GetItemHeight( descr
, wParam
);
2661 case LB_SETITEMHEIGHT16
:
2662 lParam
= LOWORD(lParam
);
2664 case LB_SETITEMHEIGHT
:
2665 return LISTBOX_SetItemHeight( hwnd
, descr
, wParam
, lParam
, TRUE
);
2667 case LB_ITEMFROMPOINT
:
2672 pt
.x
= LOWORD(lParam
);
2673 pt
.y
= HIWORD(lParam
);
2676 rect
.right
= descr
->width
;
2677 rect
.bottom
= descr
->height
;
2679 return MAKELONG( LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
),
2680 !PtInRect( &rect
, pt
) );
2683 case LB_SETCARETINDEX16
:
2684 case LB_SETCARETINDEX
:
2685 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2686 if (LISTBOX_SetCaretIndex( hwnd
, descr
, wParam
, !lParam
) == LB_ERR
)
2693 case LB_GETCARETINDEX16
:
2694 case LB_GETCARETINDEX
:
2695 return descr
->focus_item
;
2697 case LB_SETTOPINDEX16
:
2698 case LB_SETTOPINDEX
:
2699 return LISTBOX_SetTopItem( hwnd
, descr
, wParam
, TRUE
);
2701 case LB_SETCOLUMNWIDTH16
:
2702 case LB_SETCOLUMNWIDTH
:
2703 return LISTBOX_SetColumnWidth( hwnd
, descr
, wParam
);
2705 case LB_GETITEMRECT16
:
2708 ret
= LISTBOX_GetItemRect( descr
, (INT16
)wParam
, &rect
);
2709 CONV_RECT32TO16( &rect
, MapSL(lParam
) );
2713 case LB_GETITEMRECT
:
2714 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2716 case LB_FINDSTRING16
:
2717 wParam
= (INT
)(INT16
)wParam
;
2718 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2724 if(unicode
|| !HAS_STRINGS(descr
))
2725 textW
= (LPWSTR
)lParam
;
2728 LPSTR textA
= (LPSTR
)lParam
;
2729 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2730 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2731 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2733 ret
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, FALSE
);
2734 if(!unicode
&& HAS_STRINGS(descr
))
2735 HeapFree(GetProcessHeap(), 0, textW
);
2739 case LB_FINDSTRINGEXACT16
:
2740 wParam
= (INT
)(INT16
)wParam
;
2741 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2743 case LB_FINDSTRINGEXACT
:
2747 if(unicode
|| !HAS_STRINGS(descr
))
2748 textW
= (LPWSTR
)lParam
;
2751 LPSTR textA
= (LPSTR
)lParam
;
2752 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2753 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2754 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2756 ret
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, TRUE
);
2757 if(!unicode
&& HAS_STRINGS(descr
))
2758 HeapFree(GetProcessHeap(), 0, textW
);
2762 case LB_SELECTSTRING16
:
2763 wParam
= (INT
)(INT16
)wParam
;
2764 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2766 case LB_SELECTSTRING
:
2771 if(HAS_STRINGS(descr
))
2772 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2773 debugstr_a((LPSTR
)lParam
));
2774 if(unicode
|| !HAS_STRINGS(descr
))
2775 textW
= (LPWSTR
)lParam
;
2778 LPSTR textA
= (LPSTR
)lParam
;
2779 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2780 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2781 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2783 index
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, FALSE
);
2784 if(!unicode
&& HAS_STRINGS(descr
))
2785 HeapFree(GetProcessHeap(), 0, textW
);
2786 if (index
!= LB_ERR
)
2788 LISTBOX_SetCaretIndex( hwnd
, descr
, index
, TRUE
);
2789 LISTBOX_SetSelection( hwnd
, descr
, index
, TRUE
, FALSE
);
2795 wParam
= (INT
)(INT16
)wParam
;
2798 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2800 return descr
->items
[wParam
].selected
;
2803 lParam
= (INT
)(INT16
)lParam
;
2806 return LISTBOX_SetSelection( hwnd
, descr
, lParam
, wParam
, FALSE
);
2808 case LB_SETCURSEL16
:
2809 wParam
= (INT
)(INT16
)wParam
;
2812 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2813 LISTBOX_SetCaretIndex( hwnd
, descr
, wParam
, TRUE
);
2814 return LISTBOX_SetSelection( hwnd
, descr
, wParam
, TRUE
, FALSE
);
2816 case LB_GETSELCOUNT16
:
2817 case LB_GETSELCOUNT
:
2818 return LISTBOX_GetSelCount( descr
);
2820 case LB_GETSELITEMS16
:
2821 return LISTBOX_GetSelItems16( descr
, wParam
, (LPINT16
)MapSL(lParam
) );
2823 case LB_GETSELITEMS
:
2824 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2826 case LB_SELITEMRANGE16
:
2827 case LB_SELITEMRANGE
:
2828 if (LOWORD(lParam
) <= HIWORD(lParam
))
2829 return LISTBOX_SelectItemRange( hwnd
, descr
, LOWORD(lParam
),
2830 HIWORD(lParam
), wParam
);
2832 return LISTBOX_SelectItemRange( hwnd
, descr
, HIWORD(lParam
),
2833 LOWORD(lParam
), wParam
);
2835 case LB_SELITEMRANGEEX16
:
2836 case LB_SELITEMRANGEEX
:
2837 if ((INT
)lParam
>= (INT
)wParam
)
2838 return LISTBOX_SelectItemRange( hwnd
, descr
, wParam
, lParam
, TRUE
);
2840 return LISTBOX_SelectItemRange( hwnd
, descr
, lParam
, wParam
, FALSE
);
2842 case LB_GETHORIZONTALEXTENT16
:
2843 case LB_GETHORIZONTALEXTENT
:
2844 return descr
->horz_extent
;
2846 case LB_SETHORIZONTALEXTENT16
:
2847 case LB_SETHORIZONTALEXTENT
:
2848 return LISTBOX_SetHorizontalExtent( hwnd
, descr
, wParam
);
2850 case LB_GETANCHORINDEX16
:
2851 case LB_GETANCHORINDEX
:
2852 return descr
->anchor_item
;
2854 case LB_SETANCHORINDEX16
:
2855 wParam
= (INT
)(INT16
)wParam
;
2857 case LB_SETANCHORINDEX
:
2858 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2860 descr
->anchor_item
= (INT
)wParam
;
2864 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2865 * be set automatically (this is different in Win32) */
2866 if (wParam
& DDL_DRIVES
) wParam
|= DDL_EXCLUSIVE
;
2867 lParam
= (LPARAM
)MapSL(lParam
);
2874 textW
= (LPWSTR
)lParam
;
2877 LPSTR textA
= (LPSTR
)lParam
;
2878 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2879 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2880 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2882 ret
= LISTBOX_Directory( hwnd
, descr
, wParam
, textW
, msg
== LB_DIR
);
2884 HeapFree(GetProcessHeap(), 0, textW
);
2889 return descr
->locale
;
2892 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2895 case LB_INITSTORAGE
:
2896 return LISTBOX_InitStorage( hwnd
, descr
, wParam
);
2899 return LISTBOX_SetCount( hwnd
, descr
, (INT
)wParam
);
2901 case LB_SETTABSTOPS16
:
2902 return LISTBOX_SetTabStops( hwnd
, descr
, (INT
)(INT16
)wParam
, MapSL(lParam
), TRUE
);
2904 case LB_SETTABSTOPS
:
2905 return LISTBOX_SetTabStops( hwnd
, descr
, wParam
, (LPINT
)lParam
, FALSE
);
2909 if (descr
->caret_on
)
2911 descr
->caret_on
= TRUE
;
2912 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2913 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2918 if (!descr
->caret_on
)
2920 descr
->caret_on
= FALSE
;
2921 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2922 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2926 return LISTBOX_Destroy( hwnd
, descr
);
2929 InvalidateRect( hwnd
, NULL
, TRUE
);
2933 LISTBOX_SetRedraw( hwnd
, descr
, wParam
!= 0 );
2937 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2942 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( hwnd
, &ps
);
2943 ret
= LISTBOX_Paint( hwnd
, descr
, hdc
);
2944 if( !wParam
) EndPaint( hwnd
, &ps
);
2948 LISTBOX_UpdateSize( hwnd
, descr
);
2951 return (LRESULT
)descr
->font
;
2953 LISTBOX_SetFont( hwnd
, descr
, (HFONT
)wParam
);
2954 if (lParam
) InvalidateRect( hwnd
, 0, TRUE
);
2957 descr
->in_focus
= TRUE
;
2958 descr
->caret_on
= TRUE
;
2959 if (descr
->focus_item
!= -1)
2960 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2961 SEND_NOTIFICATION( hwnd
, descr
, LBN_SETFOCUS
);
2964 descr
->in_focus
= FALSE
;
2965 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2966 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2967 SEND_NOTIFICATION( hwnd
, descr
, LBN_KILLFOCUS
);
2970 return LISTBOX_HandleHScroll( hwnd
, descr
, wParam
);
2972 return LISTBOX_HandleVScroll( hwnd
, descr
, wParam
);
2974 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2975 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
2976 return LISTBOX_HandleMouseWheel( hwnd
, descr
, wParam
);
2977 case WM_LBUTTONDOWN
:
2978 return LISTBOX_HandleLButtonDown( hwnd
, descr
, wParam
,
2979 (INT16
)LOWORD(lParam
),
2980 (INT16
)HIWORD(lParam
) );
2981 case WM_LBUTTONDBLCLK
:
2982 if (descr
->style
& LBS_NOTIFY
)
2983 SEND_NOTIFICATION( hwnd
, descr
, LBN_DBLCLK
);
2986 if (GetCapture() == hwnd
)
2987 LISTBOX_HandleMouseMove( hwnd
, descr
, (INT16
)LOWORD(lParam
),
2988 (INT16
)HIWORD(lParam
) );
2991 return LISTBOX_HandleLButtonUp( hwnd
, descr
);
2993 return LISTBOX_HandleKeyDown( hwnd
, descr
, wParam
);
2998 charW
= (WCHAR
)wParam
;
3001 CHAR charA
= (CHAR
)wParam
;
3002 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
3004 return LISTBOX_HandleChar( hwnd
, descr
, charW
);
3007 return LISTBOX_HandleSystemTimer( hwnd
, descr
);
3009 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3012 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3013 wParam
, (LPARAM
)hwnd
);
3014 TRACE("hbrush = %p\n", hbrush
);
3016 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3019 GetClientRect(hwnd
, &rect
);
3020 FillRect((HDC
)wParam
, &rect
, hbrush
);
3026 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3027 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3031 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3032 WARN("[%p]: unknown msg %04x wp %08x lp %08lx\n",
3033 hwnd
, msg
, wParam
, lParam
);
3034 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3035 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3040 /***********************************************************************
3043 * This is just a wrapper for the real wndproc, it only does window locking
3046 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3048 if (!IsWindow(hwnd
)) return 0;
3049 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3052 /***********************************************************************
3055 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3057 if (!IsWindow(hwnd
)) return 0;
3058 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);
3061 /***********************************************************************
3062 * ComboLBWndProc_common
3064 * The real combo listbox wndproc
3066 static LRESULT WINAPI
ComboLBWndProc_common( HWND hwnd
, UINT msg
,
3067 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
3073 if (!(descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 )))
3075 if (msg
== WM_CREATE
)
3077 CREATESTRUCTA
*lpcs
= (CREATESTRUCTA
*)lParam
;
3078 TRACE_(combo
)("\tpassed parent handle = %p\n",lpcs
->lpCreateParams
);
3079 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
3080 return LISTBOX_Create( hwnd
, lphc
);
3082 /* Ignore all other messages before we get a WM_CREATE */
3083 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3084 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3087 TRACE_(combo
)("[%p]: msg %s wp %08x lp %08lx\n",
3088 hwnd
, SPY_GetMsgName(msg
, hwnd
), wParam
, lParam
);
3090 if ((lphc
= descr
->lphc
) != NULL
)
3095 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
3096 (CB_GETTYPE(lphc
) != CBS_SIMPLE
) )
3102 mousePos
.x
= (INT16
)LOWORD(lParam
);
3103 mousePos
.y
= (INT16
)HIWORD(lParam
);
3106 * If we are in a dropdown combobox, we simulate that
3107 * the mouse is captured to show the tracking of the item.
3109 GetClientRect(hwnd
, &clientRect
);
3111 if (PtInRect( &clientRect
, mousePos
))
3113 captured
= descr
->captured
;
3114 descr
->captured
= TRUE
;
3116 LISTBOX_HandleMouseMove( hwnd
, descr
,
3117 mousePos
.x
, mousePos
.y
);
3119 descr
->captured
= captured
;
3124 LISTBOX_HandleMouseMove( hwnd
, descr
,
3125 mousePos
.x
, mousePos
.y
);
3131 /* else we are in Win3.1 look, go with the default behavior. */
3135 if (TWEAK_WineLook
> WIN31_LOOK
)
3141 * If the mouse button "up" is not in the listbox,
3142 * we make sure there is no selection by re-selecting the
3143 * item that was selected when the listbox was made visible.
3145 mousePos
.x
= (INT16
)LOWORD(lParam
);
3146 mousePos
.y
= (INT16
)HIWORD(lParam
);
3148 GetClientRect(hwnd
, &clientRect
);
3151 * When the user clicks outside the combobox and the focus
3152 * is lost, the owning combobox will send a fake buttonup with
3153 * 0xFFFFFFF as the mouse location, we must also revert the
3154 * selection to the original selection.
3156 if ( (lParam
== (LPARAM
)-1) ||
3157 (!PtInRect( &clientRect
, mousePos
)) )
3159 LISTBOX_MoveCaret( hwnd
, descr
, lphc
->droppedIndex
, FALSE
);
3162 return LISTBOX_HandleLButtonUp( hwnd
, descr
);
3163 case WM_LBUTTONDBLCLK
:
3164 case WM_LBUTTONDOWN
:
3165 return LISTBOX_HandleLButtonDownCombo(hwnd
, descr
, msg
, wParam
,
3166 (INT16
)LOWORD(lParam
),
3167 (INT16
)HIWORD(lParam
) );
3171 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3173 /* for some reason(?) Windows makes it possible to
3174 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3176 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3177 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3178 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3180 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3184 return LISTBOX_HandleKeyDown( hwnd
, descr
, wParam
);
3186 case LB_SETCURSEL16
:
3188 lRet
= unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3189 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3190 lRet
=(lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
3193 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3199 /* default handling: call listbox wnd proc */
3200 lRet
= unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3201 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3203 TRACE_(combo
)("\t default on msg [%04x]\n", (UINT16
)msg
);
3208 /***********************************************************************
3211 * NOTE: in Windows, winproc address of the ComboLBox is the same
3212 * as that of the Listbox.
3214 LRESULT WINAPI
ComboLBWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3216 if (!IsWindow(hwnd
)) return 0;
3217 return ComboLBWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3220 /***********************************************************************
3223 LRESULT WINAPI
ComboLBWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3225 if (!IsWindow(hwnd
)) return 0;
3226 return ComboLBWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);