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
26 #include "wine/winuser16.h"
27 #include "wine/winbase16.h"
28 #include "wine/unicode.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(listbox
);
37 WINE_DECLARE_DEBUG_CHANNEL(combo
);
43 * Probably needs improvement:
47 /* Items array granularity */
48 #define LB_ARRAY_GRANULARITY 16
50 /* Scrolling timeout in ms */
51 #define LB_SCROLL_TIMEOUT 50
53 /* Listbox system timer id */
56 /* flag listbox changed while setredraw false - internal style */
57 #define LBS_DISPLAYCHANGED 0x80000000
62 LPWSTR str
; /* Item text */
63 BOOL selected
; /* Is item selected? */
64 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
65 DWORD data
; /* User data */
68 /* Listbox structure */
71 HWND owner
; /* Owner window to send notifications to */
72 UINT style
; /* Window style */
73 INT width
; /* Window width */
74 INT height
; /* Window height */
75 LB_ITEMDATA
*items
; /* Array of items */
76 INT nb_items
; /* Number of items */
77 INT top_item
; /* Top visible item */
78 INT selected_item
; /* Selected item */
79 INT focus_item
; /* Item that has the focus */
80 INT anchor_item
; /* Anchor item for extended selection */
81 INT item_height
; /* Default item height */
82 INT page_size
; /* Items per listbox page */
83 INT column_width
; /* Column width for multi-column listboxes */
84 INT horz_extent
; /* Horizontal extent (0 if no hscroll) */
85 INT horz_pos
; /* Horizontal position */
86 INT nb_tabs
; /* Number of tabs in array */
87 INT
*tabs
; /* Array of tabs */
88 BOOL caret_on
; /* Is caret on? */
89 BOOL captured
; /* Is mouse captured? */
91 HFONT font
; /* Current font */
92 LCID locale
; /* Current locale for string comparisons */
93 LPHEADCOMBO lphc
; /* ComboLBox */
97 #define IS_OWNERDRAW(descr) \
98 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
100 #define HAS_STRINGS(descr) \
101 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
104 #define IS_MULTISELECT(descr) \
105 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
107 #define SEND_NOTIFICATION(hwnd,descr,code) \
108 (SendMessageW( (descr)->owner, WM_COMMAND, \
109 MAKEWPARAM( GetWindowLongA((hwnd),GWL_ID), (code)), (hwnd) ))
111 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
113 /* Current timer status */
123 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
125 static LRESULT WINAPI
ComboLBWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
126 static LRESULT WINAPI
ComboLBWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
127 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
128 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
130 static LRESULT
LISTBOX_GetItemRect( LB_DESCR
*descr
, INT index
, RECT
*rect
);
132 /*********************************************************************
133 * listbox class descriptor
135 const struct builtin_class_descr LISTBOX_builtin_class
=
137 "ListBox", /* name */
138 CS_GLOBALCLASS
| CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
139 ListBoxWndProcA
, /* procA */
140 ListBoxWndProcW
, /* procW */
141 sizeof(LB_DESCR
*), /* extra */
142 IDC_ARROWA
, /* cursor */
147 /*********************************************************************
148 * combolbox class descriptor
150 const struct builtin_class_descr COMBOLBOX_builtin_class
=
152 "ComboLBox", /* name */
153 CS_GLOBALCLASS
| CS_DBLCLKS
| CS_SAVEBITS
, /* style */
154 ComboLBWndProcA
, /* procA */
155 ComboLBWndProcW
, /* procW */
156 sizeof(LB_DESCR
*), /* extra */
157 IDC_ARROWA
, /* cursor */
162 /* check whether app is a Win 3.1 app */
163 inline static BOOL
is_old_app( HWND hwnd
)
165 return (GetExpWinVer16( GetWindowLongA(hwnd
,GWL_HINSTANCE
) ) & 0xFF00 ) == 0x0300;
169 /***********************************************************************
172 void LISTBOX_Dump( HWND hwnd
)
176 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 );
178 TRACE( "Listbox:\n" );
179 TRACE( "hwnd=%04x descr=%08x items=%d top=%d\n",
180 hwnd
, (UINT
)descr
, descr
->nb_items
,
182 for (i
= 0, item
= descr
->items
; i
< descr
->nb_items
; i
++, item
++)
184 TRACE( "%4d: %-40s %d %08lx %3d\n",
185 i
, debugstr_w(item
->str
), item
->selected
, item
->data
, item
->height
);
190 /***********************************************************************
191 * LISTBOX_GetCurrentPageSize
193 * Return the current page size
195 static INT
LISTBOX_GetCurrentPageSize( LB_DESCR
*descr
)
198 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
199 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
201 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
203 if (i
== descr
->top_item
) return 1;
204 else return i
- descr
->top_item
;
208 /***********************************************************************
209 * LISTBOX_GetMaxTopIndex
211 * Return the maximum possible index for the top of the listbox.
213 static INT
LISTBOX_GetMaxTopIndex( LB_DESCR
*descr
)
217 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
219 page
= descr
->height
;
220 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
221 if ((page
-= descr
->items
[max
].height
) < 0) break;
222 if (max
< descr
->nb_items
- 1) max
++;
224 else if (descr
->style
& LBS_MULTICOLUMN
)
226 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
227 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
228 max
= (max
- page
) * descr
->page_size
;
232 max
= descr
->nb_items
- descr
->page_size
;
234 if (max
< 0) max
= 0;
239 /***********************************************************************
240 * LISTBOX_UpdateScroll
242 * Update the scrollbars. Should be called whenever the content
243 * of the listbox changes.
245 static void LISTBOX_UpdateScroll( HWND hwnd
, LB_DESCR
*descr
)
249 /* Check the listbox scroll bar flags individually before we call
250 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
251 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
252 scroll bar when we do not need one.
253 if (!(descr->style & WS_VSCROLL)) return;
256 /* It is important that we check descr->style, and not wnd->dwStyle,
257 for WS_VSCROLL, as the former is exactly the one passed in
258 argument to CreateWindow.
259 In Windows (and from now on in Wine :) a listbox created
260 with such a style (no WS_SCROLL) does not update
261 the scrollbar with listbox-related data, thus letting
262 the programmer use it for his/her own purposes. */
264 if (descr
->style
& LBS_NOREDRAW
) return;
265 info
.cbSize
= sizeof(info
);
267 if (descr
->style
& LBS_MULTICOLUMN
)
270 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
271 info
.nPos
= descr
->top_item
/ descr
->page_size
;
272 info
.nPage
= descr
->width
/ descr
->column_width
;
273 if (info
.nPage
< 1) info
.nPage
= 1;
274 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
275 if (descr
->style
& LBS_DISABLENOSCROLL
)
276 info
.fMask
|= SIF_DISABLENOSCROLL
;
277 if (descr
->style
& WS_HSCROLL
)
278 SetScrollInfo( hwnd
, SB_HORZ
, &info
, TRUE
);
280 info
.fMask
= SIF_RANGE
;
281 if (descr
->style
& WS_VSCROLL
)
282 SetScrollInfo( hwnd
, SB_VERT
, &info
, TRUE
);
287 info
.nMax
= descr
->nb_items
- 1;
288 info
.nPos
= descr
->top_item
;
289 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
290 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
291 if (descr
->style
& LBS_DISABLENOSCROLL
)
292 info
.fMask
|= SIF_DISABLENOSCROLL
;
293 if (descr
->style
& WS_VSCROLL
)
294 SetScrollInfo( hwnd
, SB_VERT
, &info
, TRUE
);
296 if (descr
->horz_extent
)
299 info
.nMax
= descr
->horz_extent
- 1;
300 info
.nPos
= descr
->horz_pos
;
301 info
.nPage
= descr
->width
;
302 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
303 if (descr
->style
& LBS_DISABLENOSCROLL
)
304 info
.fMask
|= SIF_DISABLENOSCROLL
;
305 if (descr
->style
& WS_HSCROLL
)
306 SetScrollInfo( hwnd
, SB_HORZ
, &info
, TRUE
);
312 /***********************************************************************
315 * Set the top item of the listbox, scrolling up or down if necessary.
317 static LRESULT
LISTBOX_SetTopItem( HWND hwnd
, LB_DESCR
*descr
, INT index
,
320 INT max
= LISTBOX_GetMaxTopIndex( descr
);
321 if (index
> max
) index
= max
;
322 if (index
< 0) index
= 0;
323 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
324 if (descr
->top_item
== index
) return LB_OKAY
;
325 if (descr
->style
& LBS_MULTICOLUMN
)
327 INT diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
328 if (scroll
&& (abs(diff
) < descr
->width
))
329 ScrollWindowEx( hwnd
, diff
, 0, NULL
, NULL
, 0, NULL
,
330 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
338 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
342 if (index
> descr
->top_item
)
344 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
345 diff
-= descr
->items
[i
].height
;
349 for (i
= index
; i
< descr
->top_item
; i
++)
350 diff
+= descr
->items
[i
].height
;
354 diff
= (descr
->top_item
- index
) * descr
->item_height
;
356 if (abs(diff
) < descr
->height
)
357 ScrollWindowEx( hwnd
, 0, diff
, NULL
, NULL
, 0, NULL
,
358 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
362 if (!scroll
) InvalidateRect( hwnd
, NULL
, TRUE
);
363 descr
->top_item
= index
;
364 LISTBOX_UpdateScroll( hwnd
, descr
);
369 /***********************************************************************
372 * Update the page size. Should be called when the size of
373 * the client area or the item height changes.
375 static void LISTBOX_UpdatePage( HWND hwnd
, LB_DESCR
*descr
)
379 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
381 if (page_size
== descr
->page_size
) return;
382 descr
->page_size
= page_size
;
383 if (descr
->style
& LBS_MULTICOLUMN
)
384 InvalidateRect( hwnd
, NULL
, TRUE
);
385 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
, FALSE
);
389 /***********************************************************************
392 * Update the size of the listbox. Should be called when the size of
393 * the client area changes.
395 static void LISTBOX_UpdateSize( HWND hwnd
, LB_DESCR
*descr
)
399 GetClientRect( hwnd
, &rect
);
400 descr
->width
= rect
.right
- rect
.left
;
401 descr
->height
= rect
.bottom
- rect
.top
;
402 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
407 GetWindowRect( hwnd
, &rect
);
408 if(descr
->item_height
!= 0)
409 remaining
= descr
->height
% descr
->item_height
;
412 if ((descr
->height
> descr
->item_height
) && remaining
)
414 if (is_old_app(hwnd
))
415 { /* give a margin for error to 16 bits programs - if we need
416 less than the height of the nonclient area, round to the
417 *next* number of items */
418 int ncheight
= rect
.bottom
- rect
.top
- descr
->height
;
419 if ((descr
->item_height
- remaining
) <= ncheight
)
420 remaining
= remaining
- descr
->item_height
;
422 TRACE("[%04x]: changing height %d -> %d\n",
423 hwnd
, descr
->height
, descr
->height
- remaining
);
424 SetWindowPos( hwnd
, 0, 0, 0, rect
.right
- rect
.left
,
425 rect
.bottom
- rect
.top
- remaining
,
426 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
430 TRACE("[%04x]: new size = %d,%d\n", hwnd
, descr
->width
, descr
->height
);
431 LISTBOX_UpdatePage( hwnd
, descr
);
432 LISTBOX_UpdateScroll( hwnd
, descr
);
434 /* Invalidate the focused item so it will be repainted correctly */
435 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
437 InvalidateRect( hwnd
, &rect
, FALSE
);
442 /***********************************************************************
443 * LISTBOX_GetItemRect
445 * Get the rectangle enclosing an item, in listbox client coordinates.
446 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
448 static LRESULT
LISTBOX_GetItemRect( LB_DESCR
*descr
, INT index
, RECT
*rect
)
450 /* Index <= 0 is legal even on empty listboxes */
451 if (index
&& (index
>= descr
->nb_items
)) return -1;
452 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
453 if (descr
->style
& LBS_MULTICOLUMN
)
455 INT col
= (index
/ descr
->page_size
) -
456 (descr
->top_item
/ descr
->page_size
);
457 rect
->left
+= col
* descr
->column_width
;
458 rect
->right
= rect
->left
+ descr
->column_width
;
459 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
460 rect
->bottom
= rect
->top
+ descr
->item_height
;
462 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
465 rect
->right
+= descr
->horz_pos
;
466 if ((index
>= 0) && (index
< descr
->nb_items
))
468 if (index
< descr
->top_item
)
470 for (i
= descr
->top_item
-1; i
>= index
; i
--)
471 rect
->top
-= descr
->items
[i
].height
;
475 for (i
= descr
->top_item
; i
< index
; i
++)
476 rect
->top
+= descr
->items
[i
].height
;
478 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
484 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
485 rect
->bottom
= rect
->top
+ descr
->item_height
;
486 rect
->right
+= descr
->horz_pos
;
489 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
490 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
494 /***********************************************************************
495 * LISTBOX_GetItemFromPoint
497 * Return the item nearest from point (x,y) (in client coordinates).
499 static INT
LISTBOX_GetItemFromPoint( LB_DESCR
*descr
, INT x
, INT y
)
501 INT index
= descr
->top_item
;
503 if (!descr
->nb_items
) return -1; /* No items */
504 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
509 while (index
< descr
->nb_items
)
511 if ((pos
+= descr
->items
[index
].height
) > y
) break;
520 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
524 else if (descr
->style
& LBS_MULTICOLUMN
)
526 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
527 if (y
>= 0) index
+= y
/ descr
->item_height
;
528 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
529 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
533 index
+= (y
/ descr
->item_height
);
535 if (index
< 0) return 0;
536 if (index
>= descr
->nb_items
) return -1;
541 /***********************************************************************
546 static void LISTBOX_PaintItem( HWND hwnd
, LB_DESCR
*descr
, HDC hdc
,
547 const RECT
*rect
, INT index
, UINT action
, BOOL ignoreFocus
)
549 LB_ITEMDATA
*item
= NULL
;
550 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
552 if (IS_OWNERDRAW(descr
))
557 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
561 if (action
== ODA_FOCUS
)
562 DrawFocusRect( hdc
, rect
);
564 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
568 /* some programs mess with the clipping region when
569 drawing the item, *and* restore the previous region
570 after they are done, so a region has better to exist
571 else everything ends clipped */
572 GetClientRect(hwnd
, &r
);
573 hrgn
= CreateRectRgnIndirect(&r
);
574 SelectClipRgn( hdc
, hrgn
);
575 DeleteObject( hrgn
);
577 dis
.CtlType
= ODT_LISTBOX
;
580 dis
.itemAction
= action
;
584 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
585 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
587 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
588 if (!IsWindowEnabled(hwnd
)) dis
.itemState
|= ODS_DISABLED
;
589 dis
.itemData
= item
? item
->data
: 0;
591 TRACE("[%04x]: drawitem %d (%s) action=%02x state=%02x rect=%d,%d-%d,%d\n",
592 hwnd
, index
, item
? debugstr_w(item
->str
) : "", action
,
593 dis
.itemState
, rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
594 SendMessageW(descr
->owner
, WM_DRAWITEM
, id
, (LPARAM
)&dis
);
598 COLORREF oldText
= 0, oldBk
= 0;
600 if (action
== ODA_FOCUS
)
602 DrawFocusRect( hdc
, rect
);
605 if (item
&& item
->selected
)
607 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
608 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
611 TRACE("[%04x]: painting %d (%s) action=%02x rect=%d,%d-%d,%d\n",
612 hwnd
, index
, item
? debugstr_w(item
->str
) : "", action
,
613 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
615 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
616 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
617 else if (!(descr
->style
& LBS_USETABSTOPS
))
618 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
619 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
620 strlenW(item
->str
), NULL
);
623 /* Output empty string to paint background in the full width. */
624 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
625 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
626 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
627 item
->str
, strlenW(item
->str
),
628 descr
->nb_tabs
, descr
->tabs
, 0);
630 if (item
&& item
->selected
)
632 SetBkColor( hdc
, oldBk
);
633 SetTextColor( hdc
, oldText
);
635 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
637 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
642 /***********************************************************************
645 * Change the redraw flag.
647 static void LISTBOX_SetRedraw( HWND hwnd
, LB_DESCR
*descr
, BOOL on
)
651 if (!(descr
->style
& LBS_NOREDRAW
)) return;
652 descr
->style
&= ~LBS_NOREDRAW
;
653 if (descr
->style
& LBS_DISPLAYCHANGED
)
654 { /* page was changed while setredraw false, refresh automatically */
655 InvalidateRect(hwnd
, NULL
, TRUE
);
656 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
657 { /* reset top of page if less than number of items/page */
658 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
659 if (descr
->top_item
< 0) descr
->top_item
= 0;
661 descr
->style
&= ~LBS_DISPLAYCHANGED
;
663 LISTBOX_UpdateScroll( hwnd
, descr
);
665 else descr
->style
|= LBS_NOREDRAW
;
669 /***********************************************************************
670 * LISTBOX_RepaintItem
672 * Repaint a single item synchronously.
674 static void LISTBOX_RepaintItem( HWND hwnd
, LB_DESCR
*descr
, INT index
,
680 HBRUSH hbrush
, oldBrush
= 0;
682 /* Do not repaint the item if the item is not visible */
683 if (!IsWindowVisible(hwnd
)) return;
684 if (descr
->style
& LBS_NOREDRAW
)
686 descr
->style
|= LBS_DISPLAYCHANGED
;
689 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
690 if (!(hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
))) return;
691 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
692 hbrush
= SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
694 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
695 if (!IsWindowEnabled(hwnd
))
696 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
697 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
698 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, index
, action
, FALSE
);
699 if (oldFont
) SelectObject( hdc
, oldFont
);
700 if (oldBrush
) SelectObject( hdc
, oldBrush
);
701 ReleaseDC( hwnd
, hdc
);
705 /***********************************************************************
706 * LISTBOX_InitStorage
708 static LRESULT
LISTBOX_InitStorage( HWND hwnd
, LB_DESCR
*descr
, INT nb_items
)
712 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
713 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
715 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
716 if (!(item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
717 nb_items
* sizeof(LB_ITEMDATA
) )))
719 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
727 /***********************************************************************
728 * LISTBOX_SetTabStops
730 static BOOL
LISTBOX_SetTabStops( HWND hwnd
, LB_DESCR
*descr
, INT count
,
731 LPINT tabs
, BOOL short_ints
)
733 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
734 if (descr
->tabs
) HeapFree( GetProcessHeap(), 0, descr
->tabs
);
735 if (!(descr
->nb_tabs
= count
))
740 /* FIXME: count = 1 */
741 if (!(descr
->tabs
= (INT
*)HeapAlloc( GetProcessHeap(), 0,
742 descr
->nb_tabs
* sizeof(INT
) )))
747 LPINT16 p
= (LPINT16
)tabs
;
749 TRACE("[%04x]: settabstops ", hwnd
);
750 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
751 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
752 if (TRACE_ON(listbox
)) DPRINTF("%hd ", descr
->tabs
[i
]);
754 if (TRACE_ON(listbox
)) DPRINTF("\n");
756 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
757 /* FIXME: repaint the window? */
762 /***********************************************************************
765 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPARAM lParam
, BOOL unicode
)
767 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
768 if (HAS_STRINGS(descr
))
771 return strlenW(descr
->items
[index
].str
);
773 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
777 LPWSTR buffer
= (LPWSTR
)lParam
;
778 strcpyW( buffer
, descr
->items
[index
].str
);
779 return strlenW(buffer
);
783 LPSTR buffer
= (LPSTR
)lParam
;
784 return WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1, buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
788 *((LPDWORD
)lParam
)=*(LPDWORD
)(&descr
->items
[index
].data
);
789 return sizeof(DWORD
);
794 /***********************************************************************
795 * LISTBOX_FindStringPos
797 * Find the nearest string located before a given string in sort order.
798 * If 'exact' is TRUE, return an error if we don't get an exact match.
800 static INT
LISTBOX_FindStringPos( HWND hwnd
, LB_DESCR
*descr
, LPCWSTR str
,
803 INT index
, min
, max
, res
= -1;
805 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
807 max
= descr
->nb_items
;
810 index
= (min
+ max
) / 2;
811 if (HAS_STRINGS(descr
))
812 res
= lstrcmpiW( descr
->items
[index
].str
, str
);
815 COMPAREITEMSTRUCT cis
;
816 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
818 cis
.CtlType
= ODT_LISTBOX
;
822 cis
.itemData1
= descr
->items
[index
].data
;
824 cis
.itemData2
= (DWORD
)str
;
825 cis
.dwLocaleId
= descr
->locale
;
826 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
828 if (!res
) return index
;
829 if (res
> 0) max
= index
;
830 else min
= index
+ 1;
832 return exact
? -1 : max
;
836 /***********************************************************************
837 * LISTBOX_FindFileStrPos
839 * Find the nearest string located before a given string in directory
840 * sort order (i.e. first files, then directories, then drives).
842 static INT
LISTBOX_FindFileStrPos( HWND hwnd
, LB_DESCR
*descr
, LPCWSTR str
)
844 INT min
, max
, res
= -1;
846 if (!HAS_STRINGS(descr
))
847 return LISTBOX_FindStringPos( hwnd
, descr
, str
, FALSE
);
849 max
= descr
->nb_items
;
852 INT index
= (min
+ max
) / 2;
853 LPCWSTR p
= descr
->items
[index
].str
;
854 if (*p
== '[') /* drive or directory */
856 if (*str
!= '[') res
= -1;
857 else if (p
[1] == '-') /* drive */
859 if (str
[1] == '-') res
= str
[2] - p
[2];
864 if (str
[1] == '-') res
= 1;
865 else res
= lstrcmpiW( str
, p
);
870 if (*str
== '[') res
= 1;
871 else res
= lstrcmpiW( str
, p
);
873 if (!res
) return index
;
874 if (res
< 0) max
= index
;
875 else min
= index
+ 1;
881 /***********************************************************************
884 * Find the item beginning with a given string.
886 static INT
LISTBOX_FindString( HWND hwnd
, LB_DESCR
*descr
, INT start
,
887 LPCWSTR str
, BOOL exact
)
892 if (start
>= descr
->nb_items
) start
= -1;
893 item
= descr
->items
+ start
+ 1;
894 if (HAS_STRINGS(descr
))
896 if (!str
|| ! str
[0] ) return LB_ERR
;
899 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
900 if (!lstrcmpiW( str
, item
->str
)) return i
;
901 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
902 if (!lstrcmpiW( str
, item
->str
)) return i
;
906 /* Special case for drives and directories: ignore prefix */
907 #define CHECK_DRIVE(item) \
908 if ((item)->str[0] == '[') \
910 if (!strncmpiW( str, (item)->str+1, len )) return i; \
911 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
915 INT len
= strlenW(str
);
916 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
918 if (!strncmpiW( str
, item
->str
, len
)) return i
;
921 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
923 if (!strncmpiW( str
, item
->str
, len
)) return i
;
931 if (exact
&& (descr
->style
& LBS_SORT
))
932 /* If sorted, use a WM_COMPAREITEM binary search */
933 return LISTBOX_FindStringPos( hwnd
, descr
, str
, TRUE
);
935 /* Otherwise use a linear search */
936 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
937 if (item
->data
== (DWORD
)str
) return i
;
938 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
939 if (item
->data
== (DWORD
)str
) return i
;
945 /***********************************************************************
946 * LISTBOX_GetSelCount
948 static LRESULT
LISTBOX_GetSelCount( LB_DESCR
*descr
)
951 LB_ITEMDATA
*item
= descr
->items
;
953 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
954 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
955 if (item
->selected
) count
++;
960 /***********************************************************************
961 * LISTBOX_GetSelItems16
963 static LRESULT
LISTBOX_GetSelItems16( LB_DESCR
*descr
, INT16 max
, LPINT16 array
)
966 LB_ITEMDATA
*item
= descr
->items
;
968 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
969 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
970 if (item
->selected
) array
[count
++] = (INT16
)i
;
975 /***********************************************************************
976 * LISTBOX_GetSelItems
978 static LRESULT
LISTBOX_GetSelItems( LB_DESCR
*descr
, INT max
, LPINT array
)
981 LB_ITEMDATA
*item
= descr
->items
;
983 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
984 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
985 if (item
->selected
) array
[count
++] = i
;
990 /***********************************************************************
993 static LRESULT
LISTBOX_Paint( HWND hwnd
, LB_DESCR
*descr
, HDC hdc
)
995 INT i
, col_pos
= descr
->page_size
- 1;
997 RECT focusRect
= {-1, -1, -1, -1};
999 HBRUSH hbrush
, oldBrush
= 0;
1001 if (descr
->style
& LBS_NOREDRAW
) return 0;
1003 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1004 if (descr
->style
& LBS_MULTICOLUMN
)
1005 rect
.right
= rect
.left
+ descr
->column_width
;
1006 else if (descr
->horz_pos
)
1008 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1009 rect
.right
+= descr
->horz_pos
;
1012 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1013 hbrush
= SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
1014 hdc
, (LPARAM
)hwnd
);
1015 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1016 if (!IsWindowEnabled(hwnd
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1018 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1021 /* Special case for empty listbox: paint focus rect */
1022 rect
.bottom
= rect
.top
+ descr
->item_height
;
1023 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, descr
->focus_item
,
1025 rect
.top
= rect
.bottom
;
1028 /* Paint all the item, regarding the selection
1029 Focus state will be painted after */
1031 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1033 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1034 rect
.bottom
= rect
.top
+ descr
->item_height
;
1036 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1038 if (i
== descr
->focus_item
)
1040 /* keep the focus rect, to paint the focus item after */
1041 focusRect
.left
= rect
.left
;
1042 focusRect
.right
= rect
.right
;
1043 focusRect
.top
= rect
.top
;
1044 focusRect
.bottom
= rect
.bottom
;
1046 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1047 rect
.top
= rect
.bottom
;
1049 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1051 if (!IS_OWNERDRAW(descr
))
1053 /* Clear the bottom of the column */
1054 if (rect
.top
< descr
->height
)
1056 rect
.bottom
= descr
->height
;
1057 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1058 &rect
, NULL
, 0, NULL
);
1062 /* Go to the next column */
1063 rect
.left
+= descr
->column_width
;
1064 rect
.right
+= descr
->column_width
;
1066 col_pos
= descr
->page_size
- 1;
1071 if (rect
.top
>= descr
->height
) break;
1075 /* Paint the focus item now */
1076 if (focusRect
.top
!= focusRect
.bottom
&& descr
->caret_on
)
1077 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1079 if (!IS_OWNERDRAW(descr
))
1081 /* Clear the remainder of the client area */
1082 if (rect
.top
< descr
->height
)
1084 rect
.bottom
= descr
->height
;
1085 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1086 &rect
, NULL
, 0, NULL
);
1088 if (rect
.right
< descr
->width
)
1090 rect
.left
= rect
.right
;
1091 rect
.right
= descr
->width
;
1093 rect
.bottom
= descr
->height
;
1094 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1095 &rect
, NULL
, 0, NULL
);
1098 if (oldFont
) SelectObject( hdc
, oldFont
);
1099 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1104 /***********************************************************************
1105 * LISTBOX_InvalidateItems
1107 * Invalidate all items from a given item. If the specified item is not
1108 * visible, nothing happens.
1110 static void LISTBOX_InvalidateItems( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1114 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1116 if (descr
->style
& LBS_NOREDRAW
)
1118 descr
->style
|= LBS_DISPLAYCHANGED
;
1121 rect
.bottom
= descr
->height
;
1122 InvalidateRect( hwnd
, &rect
, TRUE
);
1123 if (descr
->style
& LBS_MULTICOLUMN
)
1125 /* Repaint the other columns */
1126 rect
.left
= rect
.right
;
1127 rect
.right
= descr
->width
;
1129 InvalidateRect( hwnd
, &rect
, TRUE
);
1135 /***********************************************************************
1136 * LISTBOX_GetItemHeight
1138 static LRESULT
LISTBOX_GetItemHeight( LB_DESCR
*descr
, INT index
)
1140 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1142 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1143 return descr
->items
[index
].height
;
1145 else return descr
->item_height
;
1149 /***********************************************************************
1150 * LISTBOX_SetItemHeight
1152 static LRESULT
LISTBOX_SetItemHeight( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1153 INT height
, BOOL repaint
)
1155 if (!height
) height
= 1;
1157 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1159 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1160 TRACE("[%04x]: item %d height = %d\n", hwnd
, index
, height
);
1161 descr
->items
[index
].height
= height
;
1162 LISTBOX_UpdateScroll( hwnd
, descr
);
1164 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1166 else if (height
!= descr
->item_height
)
1168 TRACE("[%04x]: new height = %d\n", hwnd
, height
);
1169 descr
->item_height
= height
;
1170 LISTBOX_UpdatePage( hwnd
, descr
);
1171 LISTBOX_UpdateScroll( hwnd
, descr
);
1173 InvalidateRect( hwnd
, 0, TRUE
);
1179 /***********************************************************************
1180 * LISTBOX_SetHorizontalPos
1182 static void LISTBOX_SetHorizontalPos( HWND hwnd
, LB_DESCR
*descr
, INT pos
)
1186 if (pos
> descr
->horz_extent
- descr
->width
)
1187 pos
= descr
->horz_extent
- descr
->width
;
1188 if (pos
< 0) pos
= 0;
1189 if (!(diff
= descr
->horz_pos
- pos
)) return;
1190 TRACE("[%04x]: new horz pos = %d\n", hwnd
, pos
);
1191 descr
->horz_pos
= pos
;
1192 LISTBOX_UpdateScroll( hwnd
, descr
);
1193 if (abs(diff
) < descr
->width
)
1194 ScrollWindowEx( hwnd
, diff
, 0, NULL
, NULL
, 0, NULL
,
1195 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1197 InvalidateRect( hwnd
, NULL
, TRUE
);
1201 /***********************************************************************
1202 * LISTBOX_SetHorizontalExtent
1204 static LRESULT
LISTBOX_SetHorizontalExtent( HWND hwnd
, LB_DESCR
*descr
,
1207 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1209 if (extent
<= 0) extent
= 1;
1210 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1211 TRACE("[%04x]: new horz extent = %d\n", hwnd
, extent
);
1212 descr
->horz_extent
= extent
;
1213 if (descr
->horz_pos
> extent
- descr
->width
)
1214 LISTBOX_SetHorizontalPos( hwnd
, descr
, extent
- descr
->width
);
1216 LISTBOX_UpdateScroll( hwnd
, descr
);
1221 /***********************************************************************
1222 * LISTBOX_SetColumnWidth
1224 static LRESULT
LISTBOX_SetColumnWidth( HWND hwnd
, LB_DESCR
*descr
, INT width
)
1226 if (width
== descr
->column_width
) return LB_OKAY
;
1227 TRACE("[%04x]: new column width = %d\n", hwnd
, width
);
1228 descr
->column_width
= width
;
1229 LISTBOX_UpdatePage( hwnd
, descr
);
1234 /***********************************************************************
1237 * Returns the item height.
1239 static INT
LISTBOX_SetFont( HWND hwnd
, LB_DESCR
*descr
, HFONT font
)
1247 if (!(hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
)))
1249 ERR("unable to get DC.\n" );
1252 if (font
) oldFont
= SelectObject( hdc
, font
);
1253 GetTextMetricsW( hdc
, &tm
);
1254 if (oldFont
) SelectObject( hdc
, oldFont
);
1255 ReleaseDC( hwnd
, hdc
);
1256 if (!IS_OWNERDRAW(descr
))
1257 LISTBOX_SetItemHeight( hwnd
, descr
, 0, tm
.tmHeight
, FALSE
);
1258 return tm
.tmHeight
;
1262 /***********************************************************************
1263 * LISTBOX_MakeItemVisible
1265 * Make sure that a given item is partially or fully visible.
1267 static void LISTBOX_MakeItemVisible( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1272 if (index
<= descr
->top_item
) top
= index
;
1273 else if (descr
->style
& LBS_MULTICOLUMN
)
1275 INT cols
= descr
->width
;
1276 if (!fully
) cols
+= descr
->column_width
- 1;
1277 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1279 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1280 top
= index
- descr
->page_size
* (cols
- 1);
1282 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1284 INT height
= fully
? descr
->items
[index
].height
: 1;
1285 for (top
= index
; top
> descr
->top_item
; top
--)
1286 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1290 if (index
< descr
->top_item
+ descr
->page_size
) return;
1291 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1292 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1293 top
= index
- descr
->page_size
+ 1;
1295 LISTBOX_SetTopItem( hwnd
, descr
, top
, TRUE
);
1298 /***********************************************************************
1299 * LISTBOX_SetCaretIndex
1302 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1305 static LRESULT
LISTBOX_SetCaretIndex( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1306 BOOL fully_visible
)
1308 INT oldfocus
= descr
->focus_item
;
1310 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1311 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1312 if (index
== oldfocus
) return LB_OKAY
;
1313 descr
->focus_item
= index
;
1314 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1315 LISTBOX_RepaintItem( hwnd
, descr
, oldfocus
, ODA_FOCUS
);
1317 LISTBOX_MakeItemVisible( hwnd
, descr
, index
, fully_visible
);
1318 if (descr
->caret_on
&& (descr
->in_focus
))
1319 LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_FOCUS
);
1325 /***********************************************************************
1326 * LISTBOX_SelectItemRange
1328 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1330 static LRESULT
LISTBOX_SelectItemRange( HWND hwnd
, LB_DESCR
*descr
, INT first
,
1335 /* A few sanity checks */
1337 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1338 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1339 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1340 if (last
== -1) last
= descr
->nb_items
- 1;
1341 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1342 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1343 /* selected_item reflects last selected/unselected item on multiple sel */
1344 descr
->selected_item
= last
;
1346 if (on
) /* Turn selection on */
1348 for (i
= first
; i
<= last
; i
++)
1350 if (descr
->items
[i
].selected
) continue;
1351 descr
->items
[i
].selected
= TRUE
;
1352 LISTBOX_RepaintItem( hwnd
, descr
, i
, ODA_SELECT
);
1354 LISTBOX_SetCaretIndex( hwnd
, descr
, last
, TRUE
);
1356 else /* Turn selection off */
1358 for (i
= first
; i
<= last
; i
++)
1360 if (!descr
->items
[i
].selected
) continue;
1361 descr
->items
[i
].selected
= FALSE
;
1362 LISTBOX_RepaintItem( hwnd
, descr
, i
, ODA_SELECT
);
1368 /***********************************************************************
1369 * LISTBOX_SetSelection
1371 static LRESULT
LISTBOX_SetSelection( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1372 BOOL on
, BOOL send_notify
)
1374 TRACE( "index=%d notify=%s\n", index
, send_notify
? "YES" : "NO" );
1376 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1377 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1378 if (descr
->style
& LBS_MULTIPLESEL
)
1380 if (index
== -1) /* Select all items */
1381 return LISTBOX_SelectItemRange( hwnd
, descr
, 0, -1, on
);
1382 else /* Only one item */
1383 return LISTBOX_SelectItemRange( hwnd
, descr
, index
, index
, on
);
1387 INT oldsel
= descr
->selected_item
;
1388 if (index
== oldsel
) return LB_OKAY
;
1389 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1390 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1391 descr
->selected_item
= index
;
1392 if (oldsel
!= -1) LISTBOX_RepaintItem( hwnd
, descr
, oldsel
, ODA_SELECT
);
1393 if (index
!= -1) LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_SELECT
);
1394 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( hwnd
, descr
,
1395 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1397 if( descr
->lphc
) /* set selection change flag for parent combo */
1398 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1404 /***********************************************************************
1407 * Change the caret position and extend the selection to the new caret.
1409 static void LISTBOX_MoveCaret( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1410 BOOL fully_visible
)
1412 INT oldfocus
= descr
->focus_item
;
1414 if ((index
< 0) || (index
>= descr
->nb_items
))
1417 /* Important, repaint needs to be done in this order if
1418 you want to mimic Windows behavior:
1419 1. Remove the focus and paint the item
1420 2. Remove the selection and paint the item(s)
1421 3. Set the selection and repaint the item(s)
1422 4. Set the focus to 'index' and repaint the item */
1424 /* 1. remove the focus and repaint the item */
1425 descr
->focus_item
= -1;
1426 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1427 LISTBOX_RepaintItem( hwnd
, descr
, oldfocus
, ODA_FOCUS
);
1429 /* 2. then turn off the previous selection */
1430 /* 3. repaint the new selected item */
1431 if (descr
->style
& LBS_EXTENDEDSEL
)
1433 if (descr
->anchor_item
!= -1)
1435 INT first
= min( index
, descr
->anchor_item
);
1436 INT last
= max( index
, descr
->anchor_item
);
1438 LISTBOX_SelectItemRange( hwnd
, descr
, 0, first
- 1, FALSE
);
1439 LISTBOX_SelectItemRange( hwnd
, descr
, last
+ 1, -1, FALSE
);
1440 LISTBOX_SelectItemRange( hwnd
, descr
, first
, last
, TRUE
);
1443 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1445 /* Set selection to new caret item */
1446 LISTBOX_SetSelection( hwnd
, descr
, index
, TRUE
, FALSE
);
1449 /* 4. repaint the new item with the focus */
1450 descr
->focus_item
= index
;
1451 LISTBOX_MakeItemVisible( hwnd
, descr
, index
, fully_visible
);
1452 if (descr
->caret_on
&& (descr
->in_focus
))
1453 LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_FOCUS
);
1457 /***********************************************************************
1458 * LISTBOX_InsertItem
1460 static LRESULT
LISTBOX_InsertItem( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1461 LPWSTR str
, DWORD data
)
1465 INT oldfocus
= descr
->focus_item
;
1467 if (index
== -1) index
= descr
->nb_items
;
1468 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1469 if (!descr
->items
) max_items
= 0;
1470 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1471 if (descr
->nb_items
== max_items
)
1473 /* We need to grow the array */
1474 max_items
+= LB_ARRAY_GRANULARITY
;
1475 if (!(item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1476 max_items
* sizeof(LB_ITEMDATA
) )))
1478 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
1481 descr
->items
= item
;
1484 /* Insert the item structure */
1486 item
= &descr
->items
[index
];
1487 if (index
< descr
->nb_items
)
1488 RtlMoveMemory( item
+ 1, item
,
1489 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1493 item
->selected
= FALSE
;
1496 /* Get item height */
1498 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1500 MEASUREITEMSTRUCT mis
;
1501 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
1503 mis
.CtlType
= ODT_LISTBOX
;
1506 mis
.itemData
= descr
->items
[index
].data
;
1507 mis
.itemHeight
= descr
->item_height
;
1508 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1509 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1510 TRACE("[%04x]: measure item %d (%s) = %d\n",
1511 hwnd
, index
, str
? debugstr_w(str
) : "", item
->height
);
1514 /* Repaint the items */
1516 LISTBOX_UpdateScroll( hwnd
, descr
);
1517 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1519 /* Move selection and focused item */
1520 /* If listbox was empty, set focus to the first item */
1521 if (descr
->nb_items
== 1)
1522 LISTBOX_SetCaretIndex( hwnd
, descr
, 0, FALSE
);
1523 /* single select don't change selection index in win31 */
1524 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1526 descr
->selected_item
++;
1527 LISTBOX_SetSelection( hwnd
, descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1531 if (index
<= descr
->selected_item
)
1533 descr
->selected_item
++;
1534 descr
->focus_item
= oldfocus
; /* focus not changed */
1541 /***********************************************************************
1542 * LISTBOX_InsertString
1544 static LRESULT
LISTBOX_InsertString( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1547 LPWSTR new_str
= NULL
;
1551 if (HAS_STRINGS(descr
))
1553 static const WCHAR empty_stringW
[] = { 0 };
1554 if (!str
) str
= empty_stringW
;
1555 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1557 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
1560 strcpyW(new_str
, str
);
1562 else data
= (DWORD
)str
;
1564 if (index
== -1) index
= descr
->nb_items
;
1565 if ((ret
= LISTBOX_InsertItem( hwnd
, descr
, index
, new_str
, data
)) != 0)
1567 if (new_str
) HeapFree( GetProcessHeap(), 0, new_str
);
1571 TRACE("[%04x]: added item %d %s\n",
1572 hwnd
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1577 /***********************************************************************
1578 * LISTBOX_DeleteItem
1580 * Delete the content of an item. 'index' must be a valid index.
1582 static void LISTBOX_DeleteItem( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1584 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1585 * while Win95 sends it for all items with user data.
1586 * It's probably better to send it too often than not
1587 * often enough, so this is what we do here.
1589 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1591 DELETEITEMSTRUCT dis
;
1592 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
1594 dis
.CtlType
= ODT_LISTBOX
;
1597 dis
.hwndItem
= hwnd
;
1598 dis
.itemData
= descr
->items
[index
].data
;
1599 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1601 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1602 HeapFree( GetProcessHeap(), 0, descr
->items
[index
].str
);
1606 /***********************************************************************
1607 * LISTBOX_RemoveItem
1609 * Remove an item from the listbox and delete its content.
1611 static LRESULT
LISTBOX_RemoveItem( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1616 if ((index
== -1) && (descr
->nb_items
> 0)) index
= descr
->nb_items
- 1;
1617 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1619 /* We need to invalidate the original rect instead of the updated one. */
1620 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1622 LISTBOX_DeleteItem( hwnd
, descr
, index
);
1624 /* Remove the item */
1626 item
= &descr
->items
[index
];
1627 if (index
< descr
->nb_items
-1)
1628 RtlMoveMemory( item
, item
+ 1,
1629 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1631 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1633 /* Shrink the item array if possible */
1635 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1636 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1638 max_items
-= LB_ARRAY_GRANULARITY
;
1639 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1640 max_items
* sizeof(LB_ITEMDATA
) );
1641 if (item
) descr
->items
= item
;
1643 /* Repaint the items */
1645 LISTBOX_UpdateScroll( hwnd
, descr
);
1646 /* if we removed the scrollbar, reset the top of the list
1647 (correct for owner-drawn ???) */
1648 if (descr
->nb_items
== descr
->page_size
)
1649 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1651 /* Move selection and focused item */
1652 if (!IS_MULTISELECT(descr
))
1654 if (index
== descr
->selected_item
)
1655 descr
->selected_item
= -1;
1656 else if (index
< descr
->selected_item
)
1658 descr
->selected_item
--;
1659 if (ISWIN31
) /* win 31 do not change the selected item number */
1660 LISTBOX_SetSelection( hwnd
, descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1664 if (descr
->focus_item
>= descr
->nb_items
)
1666 descr
->focus_item
= descr
->nb_items
- 1;
1667 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1673 /***********************************************************************
1674 * LISTBOX_ResetContent
1676 static void LISTBOX_ResetContent( HWND hwnd
, LB_DESCR
*descr
)
1680 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( hwnd
, descr
, i
);
1681 if (descr
->items
) HeapFree( GetProcessHeap(), 0, descr
->items
);
1682 descr
->nb_items
= 0;
1683 descr
->top_item
= 0;
1684 descr
->selected_item
= -1;
1685 descr
->focus_item
= 0;
1686 descr
->anchor_item
= -1;
1687 descr
->items
= NULL
;
1691 /***********************************************************************
1694 static LRESULT
LISTBOX_SetCount( HWND hwnd
, LB_DESCR
*descr
, INT count
)
1698 if (HAS_STRINGS(descr
)) return LB_ERR
;
1699 /* FIXME: this is far from optimal... */
1700 if (count
> descr
->nb_items
)
1702 while (count
> descr
->nb_items
)
1703 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, -1, 0 )) < 0)
1706 else if (count
< descr
->nb_items
)
1708 while (count
< descr
->nb_items
)
1709 if ((ret
= LISTBOX_RemoveItem( hwnd
, descr
, -1 )) < 0)
1716 /***********************************************************************
1719 static LRESULT
LISTBOX_Directory( HWND hwnd
, LB_DESCR
*descr
, UINT attrib
,
1720 LPCWSTR filespec
, BOOL long_names
)
1723 LRESULT ret
= LB_OKAY
;
1724 WIN32_FIND_DATAW entry
;
1727 /* don't scan directory if we just want drives exclusively */
1728 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1729 /* scan directory */
1730 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1732 if (GetLastError() != ERROR_NO_MORE_FILES
) return LB_ERR
;
1739 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1741 static const WCHAR bracketW
[] = { ']',0 };
1742 static const WCHAR dotW
[] = { '.',0 };
1743 if (!(attrib
& DDL_DIRECTORY
) ||
1744 !strcmpW( entry
.cAlternateFileName
, dotW
)) continue;
1746 if (long_names
) strcpyW( buffer
+ 1, entry
.cFileName
);
1747 else strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1748 strcatW(buffer
, bracketW
);
1750 else /* not a directory */
1752 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1753 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1755 if ((attrib
& DDL_EXCLUSIVE
) &&
1756 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1759 if (long_names
) strcpyW( buffer
, entry
.cFileName
);
1760 else strcpyW( buffer
, entry
.cAlternateFileName
);
1762 if (!long_names
) CharLowerW( buffer
);
1763 pos
= LISTBOX_FindFileStrPos( hwnd
, descr
, buffer
);
1764 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, pos
, buffer
)) < 0)
1766 } while (FindNextFileW( handle
, &entry
));
1767 FindClose( handle
);
1772 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1774 WCHAR buffer
[] = {'[','-','a','-',']',0};
1775 WCHAR root
[] = {'A',':','\\',0};
1777 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1779 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1780 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, -1, buffer
)) < 0)
1788 /***********************************************************************
1789 * LISTBOX_HandleVScroll
1791 static LRESULT
LISTBOX_HandleVScroll( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1795 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1796 switch(LOWORD(wParam
))
1799 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
- 1, TRUE
);
1802 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+ 1, TRUE
);
1805 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
-
1806 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1809 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+
1810 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1812 case SB_THUMBPOSITION
:
1813 LISTBOX_SetTopItem( hwnd
, descr
, HIWORD(wParam
), TRUE
);
1816 info
.cbSize
= sizeof(info
);
1817 info
.fMask
= SIF_TRACKPOS
;
1818 GetScrollInfo( hwnd
, SB_VERT
, &info
);
1819 LISTBOX_SetTopItem( hwnd
, descr
, info
.nTrackPos
, TRUE
);
1822 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1825 LISTBOX_SetTopItem( hwnd
, descr
, descr
->nb_items
, TRUE
);
1832 /***********************************************************************
1833 * LISTBOX_HandleHScroll
1835 static LRESULT
LISTBOX_HandleHScroll( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1840 if (descr
->style
& LBS_MULTICOLUMN
)
1842 switch(LOWORD(wParam
))
1845 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
-descr
->page_size
,
1849 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+descr
->page_size
,
1853 page
= descr
->width
/ descr
->column_width
;
1854 if (page
< 1) page
= 1;
1855 LISTBOX_SetTopItem( hwnd
, descr
,
1856 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1859 page
= descr
->width
/ descr
->column_width
;
1860 if (page
< 1) page
= 1;
1861 LISTBOX_SetTopItem( hwnd
, descr
,
1862 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1864 case SB_THUMBPOSITION
:
1865 LISTBOX_SetTopItem( hwnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1869 info
.cbSize
= sizeof(info
);
1870 info
.fMask
= SIF_TRACKPOS
;
1871 GetScrollInfo( hwnd
, SB_VERT
, &info
);
1872 LISTBOX_SetTopItem( hwnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1876 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1879 LISTBOX_SetTopItem( hwnd
, descr
, descr
->nb_items
, TRUE
);
1883 else if (descr
->horz_extent
)
1885 switch(LOWORD(wParam
))
1888 LISTBOX_SetHorizontalPos( hwnd
, descr
, descr
->horz_pos
- 1 );
1891 LISTBOX_SetHorizontalPos( hwnd
, descr
, descr
->horz_pos
+ 1 );
1894 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1895 descr
->horz_pos
- descr
->width
);
1898 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1899 descr
->horz_pos
+ descr
->width
);
1901 case SB_THUMBPOSITION
:
1902 LISTBOX_SetHorizontalPos( hwnd
, descr
, HIWORD(wParam
) );
1905 info
.cbSize
= sizeof(info
);
1906 info
.fMask
= SIF_TRACKPOS
;
1907 GetScrollInfo( hwnd
, SB_HORZ
, &info
);
1908 LISTBOX_SetHorizontalPos( hwnd
, descr
, info
.nTrackPos
);
1911 LISTBOX_SetHorizontalPos( hwnd
, descr
, 0 );
1914 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1915 descr
->horz_extent
- descr
->width
);
1922 static LRESULT
LISTBOX_HandleMouseWheel(HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1924 short gcWheelDelta
= 0;
1925 UINT pulScrollLines
= 3;
1927 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1929 gcWheelDelta
-= (short) HIWORD(wParam
);
1931 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
1933 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
1934 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
1935 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+ cLineScroll
, TRUE
);
1940 /***********************************************************************
1941 * LISTBOX_HandleLButtonDown
1943 static LRESULT
LISTBOX_HandleLButtonDown( HWND hwnd
, LB_DESCR
*descr
,
1944 WPARAM wParam
, INT x
, INT y
)
1946 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
1947 TRACE("[%04x]: lbuttondown %d,%d item %d\n", hwnd
, x
, y
, index
);
1948 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
1950 if (!descr
->in_focus
)
1952 if( !descr
->lphc
) SetFocus( hwnd
);
1953 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
1956 if (index
== -1) return 0;
1958 if (descr
->style
& LBS_EXTENDEDSEL
)
1960 /* we should perhaps make sure that all items are deselected
1961 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1962 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1963 LISTBOX_SetSelection( hwnd, descr, -1, FALSE, FALSE);
1966 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1967 if (wParam
& MK_CONTROL
)
1969 LISTBOX_SetCaretIndex( hwnd
, descr
, index
, FALSE
);
1970 LISTBOX_SetSelection( hwnd
, descr
, index
,
1971 !descr
->items
[index
].selected
,
1972 (descr
->style
& LBS_NOTIFY
) != 0);
1974 else LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
1978 LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
1979 LISTBOX_SetSelection( hwnd
, descr
, index
,
1980 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1981 !descr
->items
[index
].selected
),
1982 (descr
->style
& LBS_NOTIFY
) != 0 );
1985 descr
->captured
= TRUE
;
1990 if (descr
->style
& LBS_NOTIFY
)
1991 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
1992 MAKELPARAM( x
, y
) );
1993 if (GetWindowLongA( hwnd
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2000 if (DragDetect( hwnd
, pt
))
2001 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2008 /*************************************************************************
2009 * LISTBOX_HandleLButtonDownCombo [Internal]
2011 * Process LButtonDown message for the ComboListBox
2014 * pWnd [I] The windows internal structure
2015 * pDescr [I] The ListBox internal structure
2016 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2017 * x [I] X Mouse Coordinate
2018 * y [I] Y Mouse Coordinate
2021 * 0 since we are processing the WM_LBUTTONDOWN Message
2024 * This function is only to be used when a ListBox is a ComboListBox
2027 static LRESULT
LISTBOX_HandleLButtonDownCombo( HWND hwnd
, LB_DESCR
*pDescr
,
2028 UINT msg
, WPARAM wParam
, INT x
, INT y
)
2030 RECT clientRect
, screenRect
;
2036 GetClientRect(hwnd
, &clientRect
);
2038 if(PtInRect(&clientRect
, mousePos
))
2040 /* MousePos is in client, resume normal processing */
2041 if (msg
== WM_LBUTTONDOWN
)
2043 pDescr
->lphc
->droppedIndex
= pDescr
->nb_items
? pDescr
->selected_item
: -1;
2044 return LISTBOX_HandleLButtonDown( hwnd
, pDescr
, wParam
, x
, y
);
2046 else if (pDescr
->style
& LBS_NOTIFY
)
2047 SEND_NOTIFICATION( hwnd
, pDescr
, LBN_DBLCLK
);
2052 POINT screenMousePos
;
2053 HWND hWndOldCapture
;
2055 /* Check the Non-Client Area */
2056 screenMousePos
= mousePos
;
2057 hWndOldCapture
= GetCapture();
2059 GetWindowRect(hwnd
, &screenRect
);
2060 ClientToScreen(hwnd
, &screenMousePos
);
2062 if(!PtInRect(&screenRect
, screenMousePos
))
2064 LISTBOX_SetCaretIndex( hwnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
);
2065 LISTBOX_SetSelection( hwnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2066 COMBO_FlipListbox( pDescr
->lphc
, FALSE
, FALSE
);
2071 /* Check to see the NC is a scrollbar */
2073 LONG style
= GetWindowLongA( hwnd
, GWL_STYLE
);
2074 /* Check Vertical scroll bar */
2075 if (style
& WS_VSCROLL
)
2077 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2078 if (PtInRect( &clientRect
, mousePos
))
2080 nHitTestType
= HTVSCROLL
;
2083 /* Check horizontal scroll bar */
2084 if (style
& WS_HSCROLL
)
2086 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2087 if (PtInRect( &clientRect
, mousePos
))
2089 nHitTestType
= HTHSCROLL
;
2092 /* Windows sends this message when a scrollbar is clicked
2095 if(nHitTestType
!= 0)
2097 SendMessageW(hwnd
, WM_NCLBUTTONDOWN
, nHitTestType
,
2098 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2100 /* Resume the Capture after scrolling is complete
2102 if(hWndOldCapture
!= 0)
2104 SetCapture(hWndOldCapture
);
2111 /***********************************************************************
2112 * LISTBOX_HandleLButtonUp
2114 static LRESULT
LISTBOX_HandleLButtonUp( HWND hwnd
, LB_DESCR
*descr
)
2116 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2117 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2118 LISTBOX_Timer
= LB_TIMER_NONE
;
2119 if (descr
->captured
)
2121 descr
->captured
= FALSE
;
2122 if (GetCapture() == hwnd
) ReleaseCapture();
2123 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2124 SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2130 /***********************************************************************
2131 * LISTBOX_HandleTimer
2133 * Handle scrolling upon a timer event.
2134 * Return TRUE if scrolling should continue.
2136 static LRESULT
LISTBOX_HandleTimer( HWND hwnd
, LB_DESCR
*descr
,
2137 INT index
, TIMER_DIRECTION dir
)
2142 if (descr
->top_item
) index
= descr
->top_item
- 1;
2146 if (descr
->top_item
) index
-= descr
->page_size
;
2149 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2150 if (index
== descr
->focus_item
) index
++;
2151 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2153 case LB_TIMER_RIGHT
:
2154 if (index
+ descr
->page_size
< descr
->nb_items
)
2155 index
+= descr
->page_size
;
2160 if (index
== descr
->focus_item
) return FALSE
;
2161 LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
2166 /***********************************************************************
2167 * LISTBOX_HandleSystemTimer
2169 * WM_SYSTIMER handler.
2171 static LRESULT
LISTBOX_HandleSystemTimer( HWND hwnd
, LB_DESCR
*descr
)
2173 if (!LISTBOX_HandleTimer( hwnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
2175 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2176 LISTBOX_Timer
= LB_TIMER_NONE
;
2182 /***********************************************************************
2183 * LISTBOX_HandleMouseMove
2185 * WM_MOUSEMOVE handler.
2187 static void LISTBOX_HandleMouseMove( HWND hwnd
, LB_DESCR
*descr
,
2191 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2193 if (!descr
->captured
) return;
2195 if (descr
->style
& LBS_MULTICOLUMN
)
2198 else if (y
>= descr
->item_height
* descr
->page_size
)
2199 y
= descr
->item_height
* descr
->page_size
- 1;
2203 dir
= LB_TIMER_LEFT
;
2206 else if (x
>= descr
->width
)
2208 dir
= LB_TIMER_RIGHT
;
2209 x
= descr
->width
- 1;
2214 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2215 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2218 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2219 if (index
== -1) index
= descr
->focus_item
;
2220 if (!LISTBOX_HandleTimer( hwnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2222 /* Start/stop the system timer */
2224 if (dir
!= LB_TIMER_NONE
)
2225 SetSystemTimer( hwnd
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2226 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2227 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2228 LISTBOX_Timer
= dir
;
2232 /***********************************************************************
2233 * LISTBOX_HandleKeyDown
2235 static LRESULT
LISTBOX_HandleKeyDown( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
2238 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2239 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2240 bForceSelection
= FALSE
; /* only for single select list */
2242 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2244 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2245 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2247 if (caret
== -2) return 0;
2249 if (caret
== -1) switch(wParam
)
2252 if (descr
->style
& LBS_MULTICOLUMN
)
2254 bForceSelection
= FALSE
;
2255 if (descr
->focus_item
>= descr
->page_size
)
2256 caret
= descr
->focus_item
- descr
->page_size
;
2261 caret
= descr
->focus_item
- 1;
2262 if (caret
< 0) caret
= 0;
2265 if (descr
->style
& LBS_MULTICOLUMN
)
2267 bForceSelection
= FALSE
;
2268 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2269 caret
= descr
->focus_item
+ descr
->page_size
;
2274 caret
= descr
->focus_item
+ 1;
2275 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2279 if (descr
->style
& LBS_MULTICOLUMN
)
2281 INT page
= descr
->width
/ descr
->column_width
;
2282 if (page
< 1) page
= 1;
2283 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2285 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2286 if (caret
< 0) caret
= 0;
2289 if (descr
->style
& LBS_MULTICOLUMN
)
2291 INT page
= descr
->width
/ descr
->column_width
;
2292 if (page
< 1) page
= 1;
2293 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2295 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2296 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2302 caret
= descr
->nb_items
- 1;
2305 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2306 else if (descr
->style
& LBS_MULTIPLESEL
)
2308 LISTBOX_SetSelection( hwnd
, descr
, descr
->focus_item
,
2309 !descr
->items
[descr
->focus_item
].selected
,
2310 (descr
->style
& LBS_NOTIFY
) != 0 );
2314 bForceSelection
= FALSE
;
2316 if (bForceSelection
) /* focused item is used instead of key */
2317 caret
= descr
->focus_item
;
2320 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
2321 !(GetKeyState( VK_SHIFT
) & 0x8000))
2322 descr
->anchor_item
= caret
;
2323 LISTBOX_MoveCaret( hwnd
, descr
, caret
, TRUE
);
2324 LISTBOX_SetSelection( hwnd
, descr
, caret
, TRUE
, FALSE
);
2325 if (descr
->style
& LBS_NOTIFY
)
2329 /* make sure that combo parent doesn't hide us */
2330 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2332 if (descr
->nb_items
) SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2339 /***********************************************************************
2340 * LISTBOX_HandleChar
2342 static LRESULT
LISTBOX_HandleChar( HWND hwnd
, LB_DESCR
*descr
, WCHAR charW
)
2350 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2352 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2353 MAKEWPARAM(charW
, descr
->focus_item
),
2355 if (caret
== -2) return 0;
2358 caret
= LISTBOX_FindString( hwnd
, descr
, descr
->focus_item
, str
, FALSE
);
2361 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2362 LISTBOX_SetSelection( hwnd
, descr
, caret
, TRUE
, FALSE
);
2363 LISTBOX_MoveCaret( hwnd
, descr
, caret
, TRUE
);
2364 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2365 SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2371 /***********************************************************************
2374 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2377 MEASUREITEMSTRUCT mis
;
2380 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2383 GetClientRect( hwnd
, &rect
);
2384 descr
->owner
= GetParent( hwnd
);
2385 descr
->style
= GetWindowLongA( hwnd
, GWL_STYLE
);
2386 descr
->width
= rect
.right
- rect
.left
;
2387 descr
->height
= rect
.bottom
- rect
.top
;
2388 descr
->items
= NULL
;
2389 descr
->nb_items
= 0;
2390 descr
->top_item
= 0;
2391 descr
->selected_item
= -1;
2392 descr
->focus_item
= 0;
2393 descr
->anchor_item
= -1;
2394 descr
->item_height
= 1;
2395 descr
->page_size
= 1;
2396 descr
->column_width
= 150;
2397 descr
->horz_extent
= (descr
->style
& WS_HSCROLL
) ? 1 : 0;
2398 descr
->horz_pos
= 0;
2401 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2402 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2403 descr
->in_focus
= FALSE
;
2404 descr
->captured
= FALSE
;
2406 descr
->locale
= 0; /* FIXME */
2409 if (is_old_app(hwnd
) && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2411 /* Win95 document "List Box Differences" from MSDN:
2412 If a list box in a version 3.x application has either the
2413 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2414 horizontal and vertical scroll bars.
2416 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2421 TRACE_(combo
)("[%04x]: resetting owner %04x -> %04x\n",
2422 hwnd
, descr
->owner
, lphc
->self
);
2423 descr
->owner
= lphc
->self
;
2426 SetWindowLongA( hwnd
, 0, (LONG
)descr
);
2428 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2430 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2431 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2432 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2433 descr
->item_height
= LISTBOX_SetFont( hwnd
, descr
, 0 );
2435 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2437 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2439 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2440 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2444 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
2445 mis
.CtlType
= ODT_LISTBOX
;
2450 mis
.itemHeight
= descr
->item_height
;
2451 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2452 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2456 TRACE("owner: %04x, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2461 /***********************************************************************
2464 static BOOL
LISTBOX_Destroy( HWND hwnd
, LB_DESCR
*descr
)
2466 LISTBOX_ResetContent( hwnd
, descr
);
2467 SetWindowLongA( hwnd
, 0, 0 );
2468 HeapFree( GetProcessHeap(), 0, descr
);
2473 /***********************************************************************
2474 * ListBoxWndProc_common
2476 static LRESULT WINAPI
ListBoxWndProc_common( HWND hwnd
, UINT msg
,
2477 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2482 if (!(descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 )))
2484 if (msg
== WM_CREATE
)
2486 if (!LISTBOX_Create( hwnd
, NULL
))
2488 TRACE("creating wnd=%04x descr=%lx\n", hwnd
, GetWindowLongA( hwnd
, 0 ) );
2491 /* Ignore all other messages before we get a WM_CREATE */
2492 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2493 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2496 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2497 hwnd
, SPY_GetMsgName(msg
, hwnd
), wParam
, lParam
);
2500 case LB_RESETCONTENT16
:
2501 case LB_RESETCONTENT
:
2502 LISTBOX_ResetContent( hwnd
, descr
);
2503 LISTBOX_UpdateScroll( hwnd
, descr
);
2504 InvalidateRect( hwnd
, NULL
, TRUE
);
2507 case LB_ADDSTRING16
:
2508 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2514 if(unicode
|| !HAS_STRINGS(descr
))
2515 textW
= (LPWSTR
)lParam
;
2518 LPSTR textA
= (LPSTR
)lParam
;
2519 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2520 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2521 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2523 wParam
= LISTBOX_FindStringPos( hwnd
, descr
, textW
, FALSE
);
2524 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2525 if (!unicode
&& HAS_STRINGS(descr
))
2526 HeapFree(GetProcessHeap(), 0, textW
);
2530 case LB_INSERTSTRING16
:
2531 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2532 wParam
= (INT
)(INT16
)wParam
;
2534 case LB_INSERTSTRING
:
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 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2548 if(!unicode
&& HAS_STRINGS(descr
))
2549 HeapFree(GetProcessHeap(), 0, textW
);
2554 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2560 if(unicode
|| !HAS_STRINGS(descr
))
2561 textW
= (LPWSTR
)lParam
;
2564 LPSTR textA
= (LPSTR
)lParam
;
2565 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2566 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2567 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2569 wParam
= LISTBOX_FindFileStrPos( hwnd
, descr
, textW
);
2570 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2571 if(!unicode
&& HAS_STRINGS(descr
))
2572 HeapFree(GetProcessHeap(), 0, textW
);
2576 case LB_DELETESTRING16
:
2577 case LB_DELETESTRING
:
2578 if (LISTBOX_RemoveItem( hwnd
, descr
, wParam
) != LB_ERR
)
2579 return descr
->nb_items
;
2583 case LB_GETITEMDATA16
:
2584 case LB_GETITEMDATA
:
2585 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2587 return descr
->items
[wParam
].data
;
2589 case LB_SETITEMDATA16
:
2590 case LB_SETITEMDATA
:
2591 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2593 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2598 return descr
->nb_items
;
2601 lParam
= (LPARAM
)MapSL(lParam
);
2604 return LISTBOX_GetText( descr
, wParam
, lParam
, unicode
);
2606 case LB_GETTEXTLEN16
:
2609 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2611 return (HAS_STRINGS(descr
) ? strlenW(descr
->items
[wParam
].str
)
2614 case LB_GETCURSEL16
:
2616 if (descr
->nb_items
==0)
2618 if (!IS_MULTISELECT(descr
))
2619 return descr
->selected_item
;
2621 if (descr
->selected_item
!=-1)
2622 return descr
->selected_item
;
2624 return descr
->focus_item
;
2625 /* otherwise, if the user tries to move the selection with the */
2626 /* arrow keys, we will give the application something to choke on */
2627 case LB_GETTOPINDEX16
:
2628 case LB_GETTOPINDEX
:
2629 return descr
->top_item
;
2631 case LB_GETITEMHEIGHT16
:
2632 case LB_GETITEMHEIGHT
:
2633 return LISTBOX_GetItemHeight( descr
, wParam
);
2635 case LB_SETITEMHEIGHT16
:
2636 lParam
= LOWORD(lParam
);
2638 case LB_SETITEMHEIGHT
:
2639 return LISTBOX_SetItemHeight( hwnd
, descr
, wParam
, lParam
, TRUE
);
2641 case LB_ITEMFROMPOINT
:
2646 pt
.x
= LOWORD(lParam
);
2647 pt
.y
= HIWORD(lParam
);
2650 rect
.right
= descr
->width
;
2651 rect
.bottom
= descr
->height
;
2653 return MAKELONG( LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
),
2654 !PtInRect( &rect
, pt
) );
2657 case LB_SETCARETINDEX16
:
2658 case LB_SETCARETINDEX
:
2659 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2660 if (LISTBOX_SetCaretIndex( hwnd
, descr
, wParam
, !lParam
) == LB_ERR
)
2667 case LB_GETCARETINDEX16
:
2668 case LB_GETCARETINDEX
:
2669 return descr
->focus_item
;
2671 case LB_SETTOPINDEX16
:
2672 case LB_SETTOPINDEX
:
2673 return LISTBOX_SetTopItem( hwnd
, descr
, wParam
, TRUE
);
2675 case LB_SETCOLUMNWIDTH16
:
2676 case LB_SETCOLUMNWIDTH
:
2677 return LISTBOX_SetColumnWidth( hwnd
, descr
, wParam
);
2679 case LB_GETITEMRECT16
:
2682 ret
= LISTBOX_GetItemRect( descr
, (INT16
)wParam
, &rect
);
2683 CONV_RECT32TO16( &rect
, MapSL(lParam
) );
2687 case LB_GETITEMRECT
:
2688 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2690 case LB_FINDSTRING16
:
2691 wParam
= (INT
)(INT16
)wParam
;
2692 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2698 if(unicode
|| !HAS_STRINGS(descr
))
2699 textW
= (LPWSTR
)lParam
;
2702 LPSTR textA
= (LPSTR
)lParam
;
2703 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2704 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2705 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2707 ret
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, FALSE
);
2708 if(!unicode
&& HAS_STRINGS(descr
))
2709 HeapFree(GetProcessHeap(), 0, textW
);
2713 case LB_FINDSTRINGEXACT16
:
2714 wParam
= (INT
)(INT16
)wParam
;
2715 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2717 case LB_FINDSTRINGEXACT
:
2721 if(unicode
|| !HAS_STRINGS(descr
))
2722 textW
= (LPWSTR
)lParam
;
2725 LPSTR textA
= (LPSTR
)lParam
;
2726 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2727 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2728 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2730 ret
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, TRUE
);
2731 if(!unicode
&& HAS_STRINGS(descr
))
2732 HeapFree(GetProcessHeap(), 0, textW
);
2736 case LB_SELECTSTRING16
:
2737 wParam
= (INT
)(INT16
)wParam
;
2738 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2740 case LB_SELECTSTRING
:
2745 if(HAS_STRINGS(descr
))
2746 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2747 debugstr_a((LPSTR
)lParam
));
2748 if(unicode
|| !HAS_STRINGS(descr
))
2749 textW
= (LPWSTR
)lParam
;
2752 LPSTR textA
= (LPSTR
)lParam
;
2753 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2754 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2755 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2757 index
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, FALSE
);
2758 if(!unicode
&& HAS_STRINGS(descr
))
2759 HeapFree(GetProcessHeap(), 0, textW
);
2760 if (index
!= LB_ERR
)
2762 LISTBOX_SetCaretIndex( hwnd
, descr
, index
, TRUE
);
2763 LISTBOX_SetSelection( hwnd
, descr
, index
, TRUE
, FALSE
);
2769 wParam
= (INT
)(INT16
)wParam
;
2772 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2774 return descr
->items
[wParam
].selected
;
2777 lParam
= (INT
)(INT16
)lParam
;
2780 return LISTBOX_SetSelection( hwnd
, descr
, lParam
, wParam
, FALSE
);
2782 case LB_SETCURSEL16
:
2783 wParam
= (INT
)(INT16
)wParam
;
2786 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2787 LISTBOX_SetCaretIndex( hwnd
, descr
, wParam
, TRUE
);
2788 return LISTBOX_SetSelection( hwnd
, descr
, wParam
, TRUE
, FALSE
);
2790 case LB_GETSELCOUNT16
:
2791 case LB_GETSELCOUNT
:
2792 return LISTBOX_GetSelCount( descr
);
2794 case LB_GETSELITEMS16
:
2795 return LISTBOX_GetSelItems16( descr
, wParam
, (LPINT16
)MapSL(lParam
) );
2797 case LB_GETSELITEMS
:
2798 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2800 case LB_SELITEMRANGE16
:
2801 case LB_SELITEMRANGE
:
2802 if (LOWORD(lParam
) <= HIWORD(lParam
))
2803 return LISTBOX_SelectItemRange( hwnd
, descr
, LOWORD(lParam
),
2804 HIWORD(lParam
), wParam
);
2806 return LISTBOX_SelectItemRange( hwnd
, descr
, HIWORD(lParam
),
2807 LOWORD(lParam
), wParam
);
2809 case LB_SELITEMRANGEEX16
:
2810 case LB_SELITEMRANGEEX
:
2811 if ((INT
)lParam
>= (INT
)wParam
)
2812 return LISTBOX_SelectItemRange( hwnd
, descr
, wParam
, lParam
, TRUE
);
2814 return LISTBOX_SelectItemRange( hwnd
, descr
, lParam
, wParam
, FALSE
);
2816 case LB_GETHORIZONTALEXTENT16
:
2817 case LB_GETHORIZONTALEXTENT
:
2818 return descr
->horz_extent
;
2820 case LB_SETHORIZONTALEXTENT16
:
2821 case LB_SETHORIZONTALEXTENT
:
2822 return LISTBOX_SetHorizontalExtent( hwnd
, descr
, wParam
);
2824 case LB_GETANCHORINDEX16
:
2825 case LB_GETANCHORINDEX
:
2826 return descr
->anchor_item
;
2828 case LB_SETANCHORINDEX16
:
2829 wParam
= (INT
)(INT16
)wParam
;
2831 case LB_SETANCHORINDEX
:
2832 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2834 descr
->anchor_item
= (INT
)wParam
;
2838 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2839 * be set automatically (this is different in Win32) */
2840 if (wParam
& DDL_DRIVES
) wParam
|= DDL_EXCLUSIVE
;
2841 lParam
= (LPARAM
)MapSL(lParam
);
2848 textW
= (LPWSTR
)lParam
;
2851 LPSTR textA
= (LPSTR
)lParam
;
2852 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2853 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2854 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2856 ret
= LISTBOX_Directory( hwnd
, descr
, wParam
, textW
, msg
== LB_DIR
);
2858 HeapFree(GetProcessHeap(), 0, textW
);
2863 return descr
->locale
;
2866 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2869 case LB_INITSTORAGE
:
2870 return LISTBOX_InitStorage( hwnd
, descr
, wParam
);
2873 return LISTBOX_SetCount( hwnd
, descr
, (INT
)wParam
);
2875 case LB_SETTABSTOPS16
:
2876 return LISTBOX_SetTabStops( hwnd
, descr
, (INT
)(INT16
)wParam
, MapSL(lParam
), TRUE
);
2878 case LB_SETTABSTOPS
:
2879 return LISTBOX_SetTabStops( hwnd
, descr
, wParam
, (LPINT
)lParam
, FALSE
);
2883 if (descr
->caret_on
)
2885 descr
->caret_on
= TRUE
;
2886 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2887 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2892 if (!descr
->caret_on
)
2894 descr
->caret_on
= FALSE
;
2895 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2896 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2900 return LISTBOX_Destroy( hwnd
, descr
);
2903 InvalidateRect( hwnd
, NULL
, TRUE
);
2907 LISTBOX_SetRedraw( hwnd
, descr
, wParam
!= 0 );
2911 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2916 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( hwnd
, &ps
);
2917 ret
= LISTBOX_Paint( hwnd
, descr
, hdc
);
2918 if( !wParam
) EndPaint( hwnd
, &ps
);
2922 LISTBOX_UpdateSize( hwnd
, descr
);
2927 LISTBOX_SetFont( hwnd
, descr
, (HFONT
)wParam
);
2928 if (lParam
) InvalidateRect( hwnd
, 0, TRUE
);
2931 descr
->in_focus
= TRUE
;
2932 descr
->caret_on
= TRUE
;
2933 if (descr
->focus_item
!= -1)
2934 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2935 SEND_NOTIFICATION( hwnd
, descr
, LBN_SETFOCUS
);
2938 descr
->in_focus
= FALSE
;
2939 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2940 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2941 SEND_NOTIFICATION( hwnd
, descr
, LBN_KILLFOCUS
);
2944 return LISTBOX_HandleHScroll( hwnd
, descr
, wParam
);
2946 return LISTBOX_HandleVScroll( hwnd
, descr
, wParam
);
2948 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2949 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
2950 return LISTBOX_HandleMouseWheel( hwnd
, descr
, wParam
);
2951 case WM_LBUTTONDOWN
:
2952 return LISTBOX_HandleLButtonDown( hwnd
, descr
, wParam
,
2953 (INT16
)LOWORD(lParam
),
2954 (INT16
)HIWORD(lParam
) );
2955 case WM_LBUTTONDBLCLK
:
2956 if (descr
->style
& LBS_NOTIFY
)
2957 SEND_NOTIFICATION( hwnd
, descr
, LBN_DBLCLK
);
2960 if (GetCapture() == hwnd
)
2961 LISTBOX_HandleMouseMove( hwnd
, descr
, (INT16
)LOWORD(lParam
),
2962 (INT16
)HIWORD(lParam
) );
2965 return LISTBOX_HandleLButtonUp( hwnd
, descr
);
2967 return LISTBOX_HandleKeyDown( hwnd
, descr
, wParam
);
2972 charW
= (WCHAR
)wParam
;
2975 CHAR charA
= (CHAR
)wParam
;
2976 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
2978 return LISTBOX_HandleChar( hwnd
, descr
, charW
);
2981 return LISTBOX_HandleSystemTimer( hwnd
, descr
);
2983 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
2986 HBRUSH hbrush
= SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
2987 wParam
, (LPARAM
)hwnd
);
2988 TRACE("hbrush = %04x\n", hbrush
);
2990 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
2993 GetClientRect(hwnd
, &rect
);
2994 FillRect((HDC
)wParam
, &rect
, hbrush
);
3000 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3001 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3005 case WM_QUERYDROPOBJECT
:
3010 LPDRAGINFO16 dragInfo
= MapSL( lParam
);
3011 dragInfo
->l
= LISTBOX_GetItemFromPoint( descr
, dragInfo
->pt
.x
,
3013 return SendMessage16( descr
->owner
, msg
, wParam
, lParam
);
3018 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3019 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
3020 hwnd
, msg
, wParam
, lParam
);
3021 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3022 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3027 /***********************************************************************
3030 * This is just a wrapper for the real wndproc, it only does window locking
3033 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3035 if (!IsWindow(hwnd
)) return 0;
3036 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3039 /***********************************************************************
3042 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3044 if (!IsWindow(hwnd
)) return 0;
3045 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);
3048 /***********************************************************************
3049 * ComboLBWndProc_common
3051 * The real combo listbox wndproc
3053 static LRESULT WINAPI
ComboLBWndProc_common( HWND hwnd
, UINT msg
,
3054 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
3057 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 );
3059 TRACE_(combo
)("[%04x]: msg %s wp %08x lp %08lx\n",
3060 hwnd
, SPY_GetMsgName(msg
, hwnd
), wParam
, lParam
);
3062 if( descr
|| msg
== WM_CREATE
)
3064 LPHEADCOMBO lphc
= (descr
) ? descr
->lphc
: NULL
;
3070 CREATESTRUCTA
*lpcs
= (CREATESTRUCTA
*)lParam
;
3071 TRACE_(combo
)("\tpassed parent handle = %p\n",lpcs
->lpCreateParams
);
3072 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
3073 return LISTBOX_Create( hwnd
, lphc
);
3076 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
3077 (CB_GETTYPE(lphc
) != CBS_SIMPLE
) )
3083 mousePos
.x
= (INT16
)LOWORD(lParam
);
3084 mousePos
.y
= (INT16
)HIWORD(lParam
);
3087 * If we are in a dropdown combobox, we simulate that
3088 * the mouse is captured to show the tracking of the item.
3090 GetClientRect(hwnd
, &clientRect
);
3092 if (PtInRect( &clientRect
, mousePos
))
3094 captured
= descr
->captured
;
3095 descr
->captured
= TRUE
;
3097 LISTBOX_HandleMouseMove( hwnd
, descr
,
3098 mousePos
.x
, mousePos
.y
);
3100 descr
->captured
= captured
;
3105 LISTBOX_HandleMouseMove( hwnd
, descr
,
3106 mousePos
.x
, mousePos
.y
);
3115 * If we are in Win3.1 look, go with the default behavior.
3117 return unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3118 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3121 if (TWEAK_WineLook
> WIN31_LOOK
)
3127 * If the mouse button "up" is not in the listbox,
3128 * we make sure there is no selection by re-selecting the
3129 * item that was selected when the listbox was made visible.
3131 mousePos
.x
= (INT16
)LOWORD(lParam
);
3132 mousePos
.y
= (INT16
)HIWORD(lParam
);
3134 GetClientRect(hwnd
, &clientRect
);
3137 * When the user clicks outside the combobox and the focus
3138 * is lost, the owning combobox will send a fake buttonup with
3139 * 0xFFFFFFF as the mouse location, we must also revert the
3140 * selection to the original selection.
3142 if ( (lParam
== (LPARAM
)-1) ||
3143 (!PtInRect( &clientRect
, mousePos
)) )
3145 LISTBOX_MoveCaret( hwnd
, descr
, lphc
->droppedIndex
, FALSE
);
3148 return LISTBOX_HandleLButtonUp( hwnd
, descr
);
3149 case WM_LBUTTONDBLCLK
:
3150 case WM_LBUTTONDOWN
:
3151 return LISTBOX_HandleLButtonDownCombo(hwnd
, descr
, msg
, wParam
,
3152 (INT16
)LOWORD(lParam
),
3153 (INT16
)HIWORD(lParam
) );
3157 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3159 /* for some reason(?) Windows makes it possible to
3160 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3162 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3163 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3164 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3166 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3170 return LISTBOX_HandleKeyDown( hwnd
, descr
, wParam
);
3172 case LB_SETCURSEL16
:
3174 lRet
= unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3175 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3176 lRet
=(lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
3179 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3184 return unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3185 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3188 lRet
= unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3189 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3191 TRACE_(combo
)("\t default on msg [%04x]\n", (UINT16
)msg
);
3196 /***********************************************************************
3199 * NOTE: in Windows, winproc address of the ComboLBox is the same
3200 * as that of the Listbox.
3202 LRESULT WINAPI
ComboLBWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3204 if (!IsWindow(hwnd
)) return 0;
3205 return ComboLBWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3208 /***********************************************************************
3211 LRESULT WINAPI
ComboLBWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3213 if (!IsWindow(hwnd
)) return 0;
3214 return ComboLBWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);