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_GLOBALCLASS
| 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_GLOBALCLASS
| 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 if (!(item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
720 nb_items
* sizeof(LB_ITEMDATA
) )))
722 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
730 /***********************************************************************
731 * LISTBOX_SetTabStops
733 static BOOL
LISTBOX_SetTabStops( HWND hwnd
, LB_DESCR
*descr
, INT count
,
734 LPINT tabs
, BOOL short_ints
)
736 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
737 if (descr
->tabs
) HeapFree( GetProcessHeap(), 0, descr
->tabs
);
738 if (!(descr
->nb_tabs
= count
))
743 /* FIXME: count = 1 */
744 if (!(descr
->tabs
= (INT
*)HeapAlloc( GetProcessHeap(), 0,
745 descr
->nb_tabs
* sizeof(INT
) )))
750 LPINT16 p
= (LPINT16
)tabs
;
752 TRACE("[%p]: settabstops ", hwnd
);
753 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
754 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
755 if (TRACE_ON(listbox
)) TRACE("%hd ", descr
->tabs
[i
]);
757 if (TRACE_ON(listbox
)) TRACE("\n");
759 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
760 /* FIXME: repaint the window? */
765 /***********************************************************************
768 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPARAM lParam
, BOOL unicode
)
770 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
771 if (HAS_STRINGS(descr
))
774 return strlenW(descr
->items
[index
].str
);
776 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
780 LPWSTR buffer
= (LPWSTR
)lParam
;
781 strcpyW( buffer
, descr
->items
[index
].str
);
782 return strlenW(buffer
);
786 LPSTR buffer
= (LPSTR
)lParam
;
787 return WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1, buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
791 *((LPDWORD
)lParam
)=*(LPDWORD
)(&descr
->items
[index
].data
);
792 return sizeof(DWORD
);
797 /***********************************************************************
798 * LISTBOX_FindStringPos
800 * Find the nearest string located before a given string in sort order.
801 * If 'exact' is TRUE, return an error if we don't get an exact match.
803 static INT
LISTBOX_FindStringPos( HWND hwnd
, LB_DESCR
*descr
, LPCWSTR str
,
806 INT index
, min
, max
, res
= -1;
808 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
810 max
= descr
->nb_items
;
813 index
= (min
+ max
) / 2;
814 if (HAS_STRINGS(descr
))
815 res
= lstrcmpiW( str
, descr
->items
[index
].str
);
818 COMPAREITEMSTRUCT cis
;
819 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
821 cis
.CtlType
= ODT_LISTBOX
;
824 /* note that some application (MetaStock) expects the second item
825 * to be in the listbox */
827 cis
.itemData1
= (DWORD
)str
;
829 cis
.itemData2
= descr
->items
[index
].data
;
830 cis
.dwLocaleId
= descr
->locale
;
831 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
833 if (!res
) return index
;
834 if (res
< 0) max
= index
;
835 else min
= index
+ 1;
837 return exact
? -1 : max
;
841 /***********************************************************************
842 * LISTBOX_FindFileStrPos
844 * Find the nearest string located before a given string in directory
845 * sort order (i.e. first files, then directories, then drives).
847 static INT
LISTBOX_FindFileStrPos( HWND hwnd
, LB_DESCR
*descr
, LPCWSTR str
)
849 INT min
, max
, res
= -1;
851 if (!HAS_STRINGS(descr
))
852 return LISTBOX_FindStringPos( hwnd
, descr
, str
, FALSE
);
854 max
= descr
->nb_items
;
857 INT index
= (min
+ max
) / 2;
858 LPCWSTR p
= descr
->items
[index
].str
;
859 if (*p
== '[') /* drive or directory */
861 if (*str
!= '[') res
= -1;
862 else if (p
[1] == '-') /* drive */
864 if (str
[1] == '-') res
= str
[2] - p
[2];
869 if (str
[1] == '-') res
= 1;
870 else res
= lstrcmpiW( str
, p
);
875 if (*str
== '[') res
= 1;
876 else res
= lstrcmpiW( str
, p
);
878 if (!res
) return index
;
879 if (res
< 0) max
= index
;
880 else min
= index
+ 1;
886 /***********************************************************************
889 * Find the item beginning with a given string.
891 static INT
LISTBOX_FindString( HWND hwnd
, LB_DESCR
*descr
, INT start
,
892 LPCWSTR str
, BOOL exact
)
897 if (start
>= descr
->nb_items
) start
= -1;
898 item
= descr
->items
+ start
+ 1;
899 if (HAS_STRINGS(descr
))
901 if (!str
|| ! str
[0] ) return LB_ERR
;
904 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
905 if (!lstrcmpiW( str
, item
->str
)) return i
;
906 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
907 if (!lstrcmpiW( str
, item
->str
)) return i
;
911 /* Special case for drives and directories: ignore prefix */
912 #define CHECK_DRIVE(item) \
913 if ((item)->str[0] == '[') \
915 if (!strncmpiW( str, (item)->str+1, len )) return i; \
916 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
920 INT len
= strlenW(str
);
921 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
923 if (!strncmpiW( str
, item
->str
, len
)) return i
;
926 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
928 if (!strncmpiW( str
, item
->str
, len
)) return i
;
936 if (exact
&& (descr
->style
& LBS_SORT
))
937 /* If sorted, use a WM_COMPAREITEM binary search */
938 return LISTBOX_FindStringPos( hwnd
, descr
, str
, TRUE
);
940 /* Otherwise use a linear search */
941 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
942 if (item
->data
== (DWORD
)str
) return i
;
943 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
944 if (item
->data
== (DWORD
)str
) return i
;
950 /***********************************************************************
951 * LISTBOX_GetSelCount
953 static LRESULT
LISTBOX_GetSelCount( LB_DESCR
*descr
)
956 LB_ITEMDATA
*item
= descr
->items
;
958 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
959 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
960 if (item
->selected
) count
++;
965 /***********************************************************************
966 * LISTBOX_GetSelItems16
968 static LRESULT
LISTBOX_GetSelItems16( LB_DESCR
*descr
, INT16 max
, LPINT16 array
)
971 LB_ITEMDATA
*item
= descr
->items
;
973 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
974 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
975 if (item
->selected
) array
[count
++] = (INT16
)i
;
980 /***********************************************************************
981 * LISTBOX_GetSelItems
983 static LRESULT
LISTBOX_GetSelItems( LB_DESCR
*descr
, INT max
, LPINT array
)
986 LB_ITEMDATA
*item
= descr
->items
;
988 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
989 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
990 if (item
->selected
) array
[count
++] = i
;
995 /***********************************************************************
998 static LRESULT
LISTBOX_Paint( HWND hwnd
, LB_DESCR
*descr
, HDC hdc
)
1000 INT i
, col_pos
= descr
->page_size
- 1;
1002 RECT focusRect
= {-1, -1, -1, -1};
1004 HBRUSH hbrush
, oldBrush
= 0;
1006 if (descr
->style
& LBS_NOREDRAW
) return 0;
1008 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1009 if (descr
->style
& LBS_MULTICOLUMN
)
1010 rect
.right
= rect
.left
+ descr
->column_width
;
1011 else if (descr
->horz_pos
)
1013 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1014 rect
.right
+= descr
->horz_pos
;
1017 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1018 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
1019 (WPARAM
)hdc
, (LPARAM
)hwnd
);
1020 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1021 if (!IsWindowEnabled(hwnd
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1023 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1026 /* Special case for empty listbox: paint focus rect */
1027 rect
.bottom
= rect
.top
+ descr
->item_height
;
1028 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, descr
->focus_item
,
1030 rect
.top
= rect
.bottom
;
1033 /* Paint all the item, regarding the selection
1034 Focus state will be painted after */
1036 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1038 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1039 rect
.bottom
= rect
.top
+ descr
->item_height
;
1041 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1043 if (i
== descr
->focus_item
)
1045 /* keep the focus rect, to paint the focus item after */
1046 focusRect
.left
= rect
.left
;
1047 focusRect
.right
= rect
.right
;
1048 focusRect
.top
= rect
.top
;
1049 focusRect
.bottom
= rect
.bottom
;
1051 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1052 rect
.top
= rect
.bottom
;
1054 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1056 if (!IS_OWNERDRAW(descr
))
1058 /* Clear the bottom of the column */
1059 if (rect
.top
< descr
->height
)
1061 rect
.bottom
= descr
->height
;
1062 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1063 &rect
, NULL
, 0, NULL
);
1067 /* Go to the next column */
1068 rect
.left
+= descr
->column_width
;
1069 rect
.right
+= descr
->column_width
;
1071 col_pos
= descr
->page_size
- 1;
1076 if (rect
.top
>= descr
->height
) break;
1080 /* Paint the focus item now */
1081 if (focusRect
.top
!= focusRect
.bottom
&& descr
->caret_on
)
1082 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1084 if (!IS_OWNERDRAW(descr
))
1086 /* Clear the remainder of the client area */
1087 if (rect
.top
< descr
->height
)
1089 rect
.bottom
= descr
->height
;
1090 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1091 &rect
, NULL
, 0, NULL
);
1093 if (rect
.right
< descr
->width
)
1095 rect
.left
= rect
.right
;
1096 rect
.right
= descr
->width
;
1098 rect
.bottom
= descr
->height
;
1099 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1100 &rect
, NULL
, 0, NULL
);
1103 if (oldFont
) SelectObject( hdc
, oldFont
);
1104 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1109 /***********************************************************************
1110 * LISTBOX_InvalidateItems
1112 * Invalidate all items from a given item. If the specified item is not
1113 * visible, nothing happens.
1115 static void LISTBOX_InvalidateItems( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1119 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1121 if (descr
->style
& LBS_NOREDRAW
)
1123 descr
->style
|= LBS_DISPLAYCHANGED
;
1126 rect
.bottom
= descr
->height
;
1127 InvalidateRect( hwnd
, &rect
, TRUE
);
1128 if (descr
->style
& LBS_MULTICOLUMN
)
1130 /* Repaint the other columns */
1131 rect
.left
= rect
.right
;
1132 rect
.right
= descr
->width
;
1134 InvalidateRect( hwnd
, &rect
, TRUE
);
1140 /***********************************************************************
1141 * LISTBOX_GetItemHeight
1143 static LRESULT
LISTBOX_GetItemHeight( LB_DESCR
*descr
, INT index
)
1145 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1147 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1148 return descr
->items
[index
].height
;
1150 else return descr
->item_height
;
1154 /***********************************************************************
1155 * LISTBOX_SetItemHeight
1157 static LRESULT
LISTBOX_SetItemHeight( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1158 INT height
, BOOL repaint
)
1160 if (!height
) height
= 1;
1162 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1164 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1165 TRACE("[%p]: item %d height = %d\n", hwnd
, index
, height
);
1166 descr
->items
[index
].height
= height
;
1167 LISTBOX_UpdateScroll( hwnd
, descr
);
1169 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1171 else if (height
!= descr
->item_height
)
1173 TRACE("[%p]: new height = %d\n", hwnd
, height
);
1174 descr
->item_height
= height
;
1175 LISTBOX_UpdatePage( hwnd
, descr
);
1176 LISTBOX_UpdateScroll( hwnd
, descr
);
1178 InvalidateRect( hwnd
, 0, TRUE
);
1184 /***********************************************************************
1185 * LISTBOX_SetHorizontalPos
1187 static void LISTBOX_SetHorizontalPos( HWND hwnd
, LB_DESCR
*descr
, INT pos
)
1191 if (pos
> descr
->horz_extent
- descr
->width
)
1192 pos
= descr
->horz_extent
- descr
->width
;
1193 if (pos
< 0) pos
= 0;
1194 if (!(diff
= descr
->horz_pos
- pos
)) return;
1195 TRACE("[%p]: new horz pos = %d\n", hwnd
, pos
);
1196 descr
->horz_pos
= pos
;
1197 LISTBOX_UpdateScroll( hwnd
, descr
);
1198 if (abs(diff
) < descr
->width
)
1199 ScrollWindowEx( hwnd
, diff
, 0, NULL
, NULL
, 0, NULL
,
1200 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1202 InvalidateRect( hwnd
, NULL
, TRUE
);
1206 /***********************************************************************
1207 * LISTBOX_SetHorizontalExtent
1209 static LRESULT
LISTBOX_SetHorizontalExtent( HWND hwnd
, LB_DESCR
*descr
,
1212 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1214 if (extent
<= 0) extent
= 1;
1215 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1216 TRACE("[%p]: new horz extent = %d\n", hwnd
, extent
);
1217 descr
->horz_extent
= extent
;
1218 if (descr
->horz_pos
> extent
- descr
->width
)
1219 LISTBOX_SetHorizontalPos( hwnd
, descr
, extent
- descr
->width
);
1221 LISTBOX_UpdateScroll( hwnd
, descr
);
1226 /***********************************************************************
1227 * LISTBOX_SetColumnWidth
1229 static LRESULT
LISTBOX_SetColumnWidth( HWND hwnd
, LB_DESCR
*descr
, INT width
)
1231 if (width
== descr
->column_width
) return LB_OKAY
;
1232 TRACE("[%p]: new column width = %d\n", hwnd
, width
);
1233 descr
->column_width
= width
;
1234 LISTBOX_UpdatePage( hwnd
, descr
);
1239 /***********************************************************************
1242 * Returns the item height.
1244 static INT
LISTBOX_SetFont( HWND hwnd
, LB_DESCR
*descr
, HFONT font
)
1252 if (!(hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
)))
1254 ERR("unable to get DC.\n" );
1257 if (font
) oldFont
= SelectObject( hdc
, font
);
1258 GetTextMetricsW( hdc
, &tm
);
1259 if (oldFont
) SelectObject( hdc
, oldFont
);
1260 ReleaseDC( hwnd
, hdc
);
1261 if (!IS_OWNERDRAW(descr
))
1262 LISTBOX_SetItemHeight( hwnd
, descr
, 0, tm
.tmHeight
, FALSE
);
1263 return tm
.tmHeight
;
1267 /***********************************************************************
1268 * LISTBOX_MakeItemVisible
1270 * Make sure that a given item is partially or fully visible.
1272 static void LISTBOX_MakeItemVisible( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1277 if (index
<= descr
->top_item
) top
= index
;
1278 else if (descr
->style
& LBS_MULTICOLUMN
)
1280 INT cols
= descr
->width
;
1281 if (!fully
) cols
+= descr
->column_width
- 1;
1282 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1284 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1285 top
= index
- descr
->page_size
* (cols
- 1);
1287 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1289 INT height
= fully
? descr
->items
[index
].height
: 1;
1290 for (top
= index
; top
> descr
->top_item
; top
--)
1291 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1295 if (index
< descr
->top_item
+ descr
->page_size
) return;
1296 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1297 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1298 top
= index
- descr
->page_size
+ 1;
1300 LISTBOX_SetTopItem( hwnd
, descr
, top
, TRUE
);
1303 /***********************************************************************
1304 * LISTBOX_SetCaretIndex
1307 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1310 static LRESULT
LISTBOX_SetCaretIndex( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1311 BOOL fully_visible
)
1313 INT oldfocus
= descr
->focus_item
;
1315 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1316 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1317 if (index
== oldfocus
) return LB_OKAY
;
1318 descr
->focus_item
= index
;
1319 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1320 LISTBOX_RepaintItem( hwnd
, descr
, oldfocus
, ODA_FOCUS
);
1322 LISTBOX_MakeItemVisible( hwnd
, descr
, index
, fully_visible
);
1323 if (descr
->caret_on
&& (descr
->in_focus
))
1324 LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_FOCUS
);
1330 /***********************************************************************
1331 * LISTBOX_SelectItemRange
1333 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1335 static LRESULT
LISTBOX_SelectItemRange( HWND hwnd
, LB_DESCR
*descr
, INT first
,
1340 /* A few sanity checks */
1342 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1343 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1344 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1345 if (last
== -1) last
= descr
->nb_items
- 1;
1346 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1347 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1348 /* selected_item reflects last selected/unselected item on multiple sel */
1349 descr
->selected_item
= last
;
1351 if (on
) /* Turn selection on */
1353 for (i
= first
; i
<= last
; i
++)
1355 if (descr
->items
[i
].selected
) continue;
1356 descr
->items
[i
].selected
= TRUE
;
1357 LISTBOX_RepaintItem( hwnd
, descr
, i
, ODA_SELECT
);
1359 LISTBOX_SetCaretIndex( hwnd
, descr
, last
, TRUE
);
1361 else /* Turn selection off */
1363 for (i
= first
; i
<= last
; i
++)
1365 if (!descr
->items
[i
].selected
) continue;
1366 descr
->items
[i
].selected
= FALSE
;
1367 LISTBOX_RepaintItem( hwnd
, descr
, i
, ODA_SELECT
);
1373 /***********************************************************************
1374 * LISTBOX_SetSelection
1376 static LRESULT
LISTBOX_SetSelection( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1377 BOOL on
, BOOL send_notify
)
1379 TRACE( "index=%d notify=%s\n", index
, send_notify
? "YES" : "NO" );
1381 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1382 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1383 if (descr
->style
& LBS_MULTIPLESEL
)
1385 if (index
== -1) /* Select all items */
1386 return LISTBOX_SelectItemRange( hwnd
, descr
, 0, -1, on
);
1387 else /* Only one item */
1388 return LISTBOX_SelectItemRange( hwnd
, descr
, index
, index
, on
);
1392 INT oldsel
= descr
->selected_item
;
1393 if (index
== oldsel
) return LB_OKAY
;
1394 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1395 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1396 descr
->selected_item
= index
;
1397 if (oldsel
!= -1) LISTBOX_RepaintItem( hwnd
, descr
, oldsel
, ODA_SELECT
);
1398 if (index
!= -1) LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_SELECT
);
1399 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( hwnd
, descr
,
1400 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1402 if( descr
->lphc
) /* set selection change flag for parent combo */
1403 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1409 /***********************************************************************
1412 * Change the caret position and extend the selection to the new caret.
1414 static void LISTBOX_MoveCaret( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1415 BOOL fully_visible
)
1417 INT oldfocus
= descr
->focus_item
;
1419 if ((index
< 0) || (index
>= descr
->nb_items
))
1422 /* Important, repaint needs to be done in this order if
1423 you want to mimic Windows behavior:
1424 1. Remove the focus and paint the item
1425 2. Remove the selection and paint the item(s)
1426 3. Set the selection and repaint the item(s)
1427 4. Set the focus to 'index' and repaint the item */
1429 /* 1. remove the focus and repaint the item */
1430 descr
->focus_item
= -1;
1431 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1432 LISTBOX_RepaintItem( hwnd
, descr
, oldfocus
, ODA_FOCUS
);
1434 /* 2. then turn off the previous selection */
1435 /* 3. repaint the new selected item */
1436 if (descr
->style
& LBS_EXTENDEDSEL
)
1438 if (descr
->anchor_item
!= -1)
1440 INT first
= min( index
, descr
->anchor_item
);
1441 INT last
= max( index
, descr
->anchor_item
);
1443 LISTBOX_SelectItemRange( hwnd
, descr
, 0, first
- 1, FALSE
);
1444 LISTBOX_SelectItemRange( hwnd
, descr
, last
+ 1, -1, FALSE
);
1445 LISTBOX_SelectItemRange( hwnd
, descr
, first
, last
, TRUE
);
1448 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1450 /* Set selection to new caret item */
1451 LISTBOX_SetSelection( hwnd
, descr
, index
, TRUE
, FALSE
);
1454 /* 4. repaint the new item with the focus */
1455 descr
->focus_item
= index
;
1456 LISTBOX_MakeItemVisible( hwnd
, descr
, index
, fully_visible
);
1457 if (descr
->caret_on
&& (descr
->in_focus
))
1458 LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_FOCUS
);
1462 /***********************************************************************
1463 * LISTBOX_InsertItem
1465 static LRESULT
LISTBOX_InsertItem( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1466 LPWSTR str
, DWORD data
)
1470 INT oldfocus
= descr
->focus_item
;
1472 if (index
== -1) index
= descr
->nb_items
;
1473 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1474 if (!descr
->items
) max_items
= 0;
1475 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1476 if (descr
->nb_items
== max_items
)
1478 /* We need to grow the array */
1479 max_items
+= LB_ARRAY_GRANULARITY
;
1480 if (!(item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1481 max_items
* sizeof(LB_ITEMDATA
) )))
1483 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
1486 descr
->items
= item
;
1489 /* Insert the item structure */
1491 item
= &descr
->items
[index
];
1492 if (index
< descr
->nb_items
)
1493 RtlMoveMemory( item
+ 1, item
,
1494 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1498 item
->selected
= FALSE
;
1501 /* Get item height */
1503 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1505 MEASUREITEMSTRUCT mis
;
1506 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
1508 mis
.CtlType
= ODT_LISTBOX
;
1511 mis
.itemData
= descr
->items
[index
].data
;
1512 mis
.itemHeight
= descr
->item_height
;
1513 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1514 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1515 TRACE("[%p]: measure item %d (%s) = %d\n",
1516 hwnd
, index
, str
? debugstr_w(str
) : "", item
->height
);
1519 /* Repaint the items */
1521 LISTBOX_UpdateScroll( hwnd
, descr
);
1522 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1524 /* Move selection and focused item */
1525 /* If listbox was empty, set focus to the first item */
1526 if (descr
->nb_items
== 1)
1527 LISTBOX_SetCaretIndex( hwnd
, descr
, 0, FALSE
);
1528 /* single select don't change selection index in win31 */
1529 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1531 descr
->selected_item
++;
1532 LISTBOX_SetSelection( hwnd
, descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1536 if (index
<= descr
->selected_item
)
1538 descr
->selected_item
++;
1539 descr
->focus_item
= oldfocus
; /* focus not changed */
1546 /***********************************************************************
1547 * LISTBOX_InsertString
1549 static LRESULT
LISTBOX_InsertString( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1552 LPWSTR new_str
= NULL
;
1556 if (HAS_STRINGS(descr
))
1558 static const WCHAR empty_stringW
[] = { 0 };
1559 if (!str
) str
= empty_stringW
;
1560 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1562 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
1565 strcpyW(new_str
, str
);
1567 else data
= (DWORD
)str
;
1569 if (index
== -1) index
= descr
->nb_items
;
1570 if ((ret
= LISTBOX_InsertItem( hwnd
, descr
, index
, new_str
, data
)) != 0)
1572 if (new_str
) HeapFree( GetProcessHeap(), 0, new_str
);
1576 TRACE("[%p]: added item %d %s\n",
1577 hwnd
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1582 /***********************************************************************
1583 * LISTBOX_DeleteItem
1585 * Delete the content of an item. 'index' must be a valid index.
1587 static void LISTBOX_DeleteItem( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1589 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1590 * while Win95 sends it for all items with user data.
1591 * It's probably better to send it too often than not
1592 * often enough, so this is what we do here.
1594 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1596 DELETEITEMSTRUCT dis
;
1597 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
1599 dis
.CtlType
= ODT_LISTBOX
;
1602 dis
.hwndItem
= hwnd
;
1603 dis
.itemData
= descr
->items
[index
].data
;
1604 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1606 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1607 HeapFree( GetProcessHeap(), 0, descr
->items
[index
].str
);
1611 /***********************************************************************
1612 * LISTBOX_RemoveItem
1614 * Remove an item from the listbox and delete its content.
1616 static LRESULT
LISTBOX_RemoveItem( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1621 if ((index
== -1) && (descr
->nb_items
> 0)) index
= descr
->nb_items
- 1;
1622 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1624 /* We need to invalidate the original rect instead of the updated one. */
1625 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1627 LISTBOX_DeleteItem( hwnd
, descr
, index
);
1629 /* Remove the item */
1631 item
= &descr
->items
[index
];
1632 if (index
< descr
->nb_items
-1)
1633 RtlMoveMemory( item
, item
+ 1,
1634 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1636 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1638 /* Shrink the item array if possible */
1640 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1641 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1643 max_items
-= LB_ARRAY_GRANULARITY
;
1644 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1645 max_items
* sizeof(LB_ITEMDATA
) );
1646 if (item
) descr
->items
= item
;
1648 /* Repaint the items */
1650 LISTBOX_UpdateScroll( hwnd
, descr
);
1651 /* if we removed the scrollbar, reset the top of the list
1652 (correct for owner-drawn ???) */
1653 if (descr
->nb_items
== descr
->page_size
)
1654 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1656 /* Move selection and focused item */
1657 if (!IS_MULTISELECT(descr
))
1659 if (index
== descr
->selected_item
)
1660 descr
->selected_item
= -1;
1661 else if (index
< descr
->selected_item
)
1663 descr
->selected_item
--;
1664 if (ISWIN31
) /* win 31 do not change the selected item number */
1665 LISTBOX_SetSelection( hwnd
, descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1669 if (descr
->focus_item
>= descr
->nb_items
)
1671 descr
->focus_item
= descr
->nb_items
- 1;
1672 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1678 /***********************************************************************
1679 * LISTBOX_ResetContent
1681 static void LISTBOX_ResetContent( HWND hwnd
, LB_DESCR
*descr
)
1685 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( hwnd
, descr
, i
);
1686 if (descr
->items
) HeapFree( GetProcessHeap(), 0, descr
->items
);
1687 descr
->nb_items
= 0;
1688 descr
->top_item
= 0;
1689 descr
->selected_item
= -1;
1690 descr
->focus_item
= 0;
1691 descr
->anchor_item
= -1;
1692 descr
->items
= NULL
;
1696 /***********************************************************************
1699 static LRESULT
LISTBOX_SetCount( HWND hwnd
, LB_DESCR
*descr
, INT count
)
1703 if (HAS_STRINGS(descr
)) return LB_ERR
;
1704 /* FIXME: this is far from optimal... */
1705 if (count
> descr
->nb_items
)
1707 while (count
> descr
->nb_items
)
1708 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, -1, 0 )) < 0)
1711 else if (count
< descr
->nb_items
)
1713 while (count
< descr
->nb_items
)
1714 if ((ret
= LISTBOX_RemoveItem( hwnd
, descr
, -1 )) < 0)
1721 /***********************************************************************
1724 static LRESULT
LISTBOX_Directory( HWND hwnd
, LB_DESCR
*descr
, UINT attrib
,
1725 LPCWSTR filespec
, BOOL long_names
)
1728 LRESULT ret
= LB_OKAY
;
1729 WIN32_FIND_DATAW entry
;
1732 /* don't scan directory if we just want drives exclusively */
1733 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1734 /* scan directory */
1735 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1737 int le
= GetLastError();
1738 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1745 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1747 static const WCHAR bracketW
[] = { ']',0 };
1748 static const WCHAR dotW
[] = { '.',0 };
1749 if (!(attrib
& DDL_DIRECTORY
) ||
1750 !strcmpW( entry
.cAlternateFileName
, dotW
)) continue;
1752 if (long_names
) strcpyW( buffer
+ 1, entry
.cFileName
);
1753 else strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1754 strcatW(buffer
, bracketW
);
1756 else /* not a directory */
1758 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1759 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1761 if ((attrib
& DDL_EXCLUSIVE
) &&
1762 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1765 if (long_names
) strcpyW( buffer
, entry
.cFileName
);
1766 else strcpyW( buffer
, entry
.cAlternateFileName
);
1768 if (!long_names
) CharLowerW( buffer
);
1769 pos
= LISTBOX_FindFileStrPos( hwnd
, descr
, buffer
);
1770 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, pos
, buffer
)) < 0)
1772 } while (FindNextFileW( handle
, &entry
));
1773 FindClose( handle
);
1778 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1780 WCHAR buffer
[] = {'[','-','a','-',']',0};
1781 WCHAR root
[] = {'A',':','\\',0};
1783 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1785 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1786 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, -1, buffer
)) < 0)
1794 /***********************************************************************
1795 * LISTBOX_HandleVScroll
1797 static LRESULT
LISTBOX_HandleVScroll( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1801 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1802 switch(LOWORD(wParam
))
1805 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
- 1, TRUE
);
1808 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+ 1, TRUE
);
1811 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
-
1812 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1815 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+
1816 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1818 case SB_THUMBPOSITION
:
1819 LISTBOX_SetTopItem( hwnd
, descr
, HIWORD(wParam
), TRUE
);
1822 info
.cbSize
= sizeof(info
);
1823 info
.fMask
= SIF_TRACKPOS
;
1824 GetScrollInfo( hwnd
, SB_VERT
, &info
);
1825 LISTBOX_SetTopItem( hwnd
, descr
, info
.nTrackPos
, TRUE
);
1828 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1831 LISTBOX_SetTopItem( hwnd
, descr
, descr
->nb_items
, TRUE
);
1838 /***********************************************************************
1839 * LISTBOX_HandleHScroll
1841 static LRESULT
LISTBOX_HandleHScroll( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1846 if (descr
->style
& LBS_MULTICOLUMN
)
1848 switch(LOWORD(wParam
))
1851 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
-descr
->page_size
,
1855 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+descr
->page_size
,
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
);
1865 page
= descr
->width
/ descr
->column_width
;
1866 if (page
< 1) page
= 1;
1867 LISTBOX_SetTopItem( hwnd
, descr
,
1868 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1870 case SB_THUMBPOSITION
:
1871 LISTBOX_SetTopItem( hwnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1875 info
.cbSize
= sizeof(info
);
1876 info
.fMask
= SIF_TRACKPOS
;
1877 GetScrollInfo( hwnd
, SB_VERT
, &info
);
1878 LISTBOX_SetTopItem( hwnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1882 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1885 LISTBOX_SetTopItem( hwnd
, descr
, descr
->nb_items
, TRUE
);
1889 else if (descr
->horz_extent
)
1891 switch(LOWORD(wParam
))
1894 LISTBOX_SetHorizontalPos( hwnd
, descr
, descr
->horz_pos
- 1 );
1897 LISTBOX_SetHorizontalPos( hwnd
, descr
, descr
->horz_pos
+ 1 );
1900 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1901 descr
->horz_pos
- descr
->width
);
1904 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1905 descr
->horz_pos
+ descr
->width
);
1907 case SB_THUMBPOSITION
:
1908 LISTBOX_SetHorizontalPos( hwnd
, descr
, HIWORD(wParam
) );
1911 info
.cbSize
= sizeof(info
);
1912 info
.fMask
= SIF_TRACKPOS
;
1913 GetScrollInfo( hwnd
, SB_HORZ
, &info
);
1914 LISTBOX_SetHorizontalPos( hwnd
, descr
, info
.nTrackPos
);
1917 LISTBOX_SetHorizontalPos( hwnd
, descr
, 0 );
1920 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1921 descr
->horz_extent
- descr
->width
);
1928 static LRESULT
LISTBOX_HandleMouseWheel(HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1930 short gcWheelDelta
= 0;
1931 UINT pulScrollLines
= 3;
1933 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1935 gcWheelDelta
-= (short) HIWORD(wParam
);
1937 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
1939 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
1940 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
1941 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+ cLineScroll
, TRUE
);
1946 /***********************************************************************
1947 * LISTBOX_HandleLButtonDown
1949 static LRESULT
LISTBOX_HandleLButtonDown( HWND hwnd
, LB_DESCR
*descr
,
1950 WPARAM wParam
, INT x
, INT y
)
1952 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
1953 TRACE("[%p]: lbuttondown %d,%d item %d\n", hwnd
, x
, y
, index
);
1954 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
1956 if (!descr
->in_focus
)
1958 if( !descr
->lphc
) SetFocus( hwnd
);
1959 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
1962 if (index
== -1) return 0;
1964 if (descr
->style
& LBS_EXTENDEDSEL
)
1966 /* we should perhaps make sure that all items are deselected
1967 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1968 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1969 LISTBOX_SetSelection( hwnd, descr, -1, FALSE, FALSE);
1972 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1973 if (wParam
& MK_CONTROL
)
1975 LISTBOX_SetCaretIndex( hwnd
, descr
, index
, FALSE
);
1976 LISTBOX_SetSelection( hwnd
, descr
, index
,
1977 !descr
->items
[index
].selected
,
1978 (descr
->style
& LBS_NOTIFY
) != 0);
1980 else LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
1984 LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
1985 LISTBOX_SetSelection( hwnd
, descr
, index
,
1986 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1987 !descr
->items
[index
].selected
),
1988 (descr
->style
& LBS_NOTIFY
) != 0 );
1991 descr
->captured
= TRUE
;
1996 if (descr
->style
& LBS_NOTIFY
)
1997 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
1998 MAKELPARAM( x
, y
) );
1999 if (GetWindowLongA( hwnd
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2006 if (DragDetect( hwnd
, pt
))
2007 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2014 /*************************************************************************
2015 * LISTBOX_HandleLButtonDownCombo [Internal]
2017 * Process LButtonDown message for the ComboListBox
2020 * pWnd [I] The windows internal structure
2021 * pDescr [I] The ListBox internal structure
2022 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2023 * x [I] X Mouse Coordinate
2024 * y [I] Y Mouse Coordinate
2027 * 0 since we are processing the WM_LBUTTONDOWN Message
2030 * This function is only to be used when a ListBox is a ComboListBox
2033 static LRESULT
LISTBOX_HandleLButtonDownCombo( HWND hwnd
, LB_DESCR
*pDescr
,
2034 UINT msg
, WPARAM wParam
, INT x
, INT y
)
2036 RECT clientRect
, screenRect
;
2042 GetClientRect(hwnd
, &clientRect
);
2044 if(PtInRect(&clientRect
, mousePos
))
2046 /* MousePos is in client, resume normal processing */
2047 if (msg
== WM_LBUTTONDOWN
)
2049 pDescr
->lphc
->droppedIndex
= pDescr
->nb_items
? pDescr
->selected_item
: -1;
2050 return LISTBOX_HandleLButtonDown( hwnd
, pDescr
, wParam
, x
, y
);
2052 else if (pDescr
->style
& LBS_NOTIFY
)
2053 SEND_NOTIFICATION( hwnd
, pDescr
, LBN_DBLCLK
);
2058 POINT screenMousePos
;
2059 HWND hWndOldCapture
;
2061 /* Check the Non-Client Area */
2062 screenMousePos
= mousePos
;
2063 hWndOldCapture
= GetCapture();
2065 GetWindowRect(hwnd
, &screenRect
);
2066 ClientToScreen(hwnd
, &screenMousePos
);
2068 if(!PtInRect(&screenRect
, screenMousePos
))
2070 LISTBOX_SetCaretIndex( hwnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
);
2071 LISTBOX_SetSelection( hwnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2072 COMBO_FlipListbox( pDescr
->lphc
, FALSE
, FALSE
);
2077 /* Check to see the NC is a scrollbar */
2079 LONG style
= GetWindowLongA( hwnd
, GWL_STYLE
);
2080 /* Check Vertical scroll bar */
2081 if (style
& WS_VSCROLL
)
2083 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2084 if (PtInRect( &clientRect
, mousePos
))
2086 nHitTestType
= HTVSCROLL
;
2089 /* Check horizontal scroll bar */
2090 if (style
& WS_HSCROLL
)
2092 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2093 if (PtInRect( &clientRect
, mousePos
))
2095 nHitTestType
= HTHSCROLL
;
2098 /* Windows sends this message when a scrollbar is clicked
2101 if(nHitTestType
!= 0)
2103 SendMessageW(hwnd
, WM_NCLBUTTONDOWN
, nHitTestType
,
2104 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2106 /* Resume the Capture after scrolling is complete
2108 if(hWndOldCapture
!= 0)
2110 SetCapture(hWndOldCapture
);
2117 /***********************************************************************
2118 * LISTBOX_HandleLButtonUp
2120 static LRESULT
LISTBOX_HandleLButtonUp( HWND hwnd
, LB_DESCR
*descr
)
2122 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2123 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2124 LISTBOX_Timer
= LB_TIMER_NONE
;
2125 if (descr
->captured
)
2127 descr
->captured
= FALSE
;
2128 if (GetCapture() == hwnd
) ReleaseCapture();
2129 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2130 SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2136 /***********************************************************************
2137 * LISTBOX_HandleTimer
2139 * Handle scrolling upon a timer event.
2140 * Return TRUE if scrolling should continue.
2142 static LRESULT
LISTBOX_HandleTimer( HWND hwnd
, LB_DESCR
*descr
,
2143 INT index
, TIMER_DIRECTION dir
)
2148 if (descr
->top_item
) index
= descr
->top_item
- 1;
2152 if (descr
->top_item
) index
-= descr
->page_size
;
2155 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2156 if (index
== descr
->focus_item
) index
++;
2157 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2159 case LB_TIMER_RIGHT
:
2160 if (index
+ descr
->page_size
< descr
->nb_items
)
2161 index
+= descr
->page_size
;
2166 if (index
== descr
->focus_item
) return FALSE
;
2167 LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
2172 /***********************************************************************
2173 * LISTBOX_HandleSystemTimer
2175 * WM_SYSTIMER handler.
2177 static LRESULT
LISTBOX_HandleSystemTimer( HWND hwnd
, LB_DESCR
*descr
)
2179 if (!LISTBOX_HandleTimer( hwnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
2181 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2182 LISTBOX_Timer
= LB_TIMER_NONE
;
2188 /***********************************************************************
2189 * LISTBOX_HandleMouseMove
2191 * WM_MOUSEMOVE handler.
2193 static void LISTBOX_HandleMouseMove( HWND hwnd
, LB_DESCR
*descr
,
2197 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2199 if (!descr
->captured
) return;
2201 if (descr
->style
& LBS_MULTICOLUMN
)
2204 else if (y
>= descr
->item_height
* descr
->page_size
)
2205 y
= descr
->item_height
* descr
->page_size
- 1;
2209 dir
= LB_TIMER_LEFT
;
2212 else if (x
>= descr
->width
)
2214 dir
= LB_TIMER_RIGHT
;
2215 x
= descr
->width
- 1;
2220 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2221 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2224 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2225 if (index
== -1) index
= descr
->focus_item
;
2226 if (!LISTBOX_HandleTimer( hwnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2228 /* Start/stop the system timer */
2230 if (dir
!= LB_TIMER_NONE
)
2231 SetSystemTimer( hwnd
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2232 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2233 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2234 LISTBOX_Timer
= dir
;
2238 /***********************************************************************
2239 * LISTBOX_HandleKeyDown
2241 static LRESULT
LISTBOX_HandleKeyDown( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
2244 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2245 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2246 bForceSelection
= FALSE
; /* only for single select list */
2248 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2250 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2251 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2253 if (caret
== -2) return 0;
2255 if (caret
== -1) switch(wParam
)
2258 if (descr
->style
& LBS_MULTICOLUMN
)
2260 bForceSelection
= FALSE
;
2261 if (descr
->focus_item
>= descr
->page_size
)
2262 caret
= descr
->focus_item
- descr
->page_size
;
2267 caret
= descr
->focus_item
- 1;
2268 if (caret
< 0) caret
= 0;
2271 if (descr
->style
& LBS_MULTICOLUMN
)
2273 bForceSelection
= FALSE
;
2274 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2275 caret
= descr
->focus_item
+ descr
->page_size
;
2280 caret
= descr
->focus_item
+ 1;
2281 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2285 if (descr
->style
& LBS_MULTICOLUMN
)
2287 INT page
= descr
->width
/ descr
->column_width
;
2288 if (page
< 1) page
= 1;
2289 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2291 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2292 if (caret
< 0) caret
= 0;
2295 if (descr
->style
& LBS_MULTICOLUMN
)
2297 INT page
= descr
->width
/ descr
->column_width
;
2298 if (page
< 1) page
= 1;
2299 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2301 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2302 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2308 caret
= descr
->nb_items
- 1;
2311 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2312 else if (descr
->style
& LBS_MULTIPLESEL
)
2314 LISTBOX_SetSelection( hwnd
, descr
, descr
->focus_item
,
2315 !descr
->items
[descr
->focus_item
].selected
,
2316 (descr
->style
& LBS_NOTIFY
) != 0 );
2320 bForceSelection
= FALSE
;
2322 if (bForceSelection
) /* focused item is used instead of key */
2323 caret
= descr
->focus_item
;
2326 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
2327 !(GetKeyState( VK_SHIFT
) & 0x8000))
2328 descr
->anchor_item
= caret
;
2329 LISTBOX_MoveCaret( hwnd
, descr
, caret
, TRUE
);
2330 LISTBOX_SetSelection( hwnd
, descr
, caret
, TRUE
, FALSE
);
2331 if (descr
->style
& LBS_NOTIFY
)
2335 /* make sure that combo parent doesn't hide us */
2336 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2338 if (descr
->nb_items
) SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2345 /***********************************************************************
2346 * LISTBOX_HandleChar
2348 static LRESULT
LISTBOX_HandleChar( HWND hwnd
, LB_DESCR
*descr
, WCHAR charW
)
2356 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2358 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2359 MAKEWPARAM(charW
, descr
->focus_item
),
2361 if (caret
== -2) return 0;
2364 caret
= LISTBOX_FindString( hwnd
, descr
, descr
->focus_item
, str
, FALSE
);
2367 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2368 LISTBOX_SetSelection( hwnd
, descr
, caret
, TRUE
, FALSE
);
2369 LISTBOX_MoveCaret( hwnd
, descr
, caret
, TRUE
);
2370 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2371 SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2377 /***********************************************************************
2380 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2383 MEASUREITEMSTRUCT mis
;
2386 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2389 GetClientRect( hwnd
, &rect
);
2390 descr
->owner
= GetParent( hwnd
);
2391 descr
->style
= GetWindowLongA( hwnd
, GWL_STYLE
);
2392 descr
->width
= rect
.right
- rect
.left
;
2393 descr
->height
= rect
.bottom
- rect
.top
;
2394 descr
->items
= NULL
;
2395 descr
->nb_items
= 0;
2396 descr
->top_item
= 0;
2397 descr
->selected_item
= -1;
2398 descr
->focus_item
= 0;
2399 descr
->anchor_item
= -1;
2400 descr
->item_height
= 1;
2401 descr
->page_size
= 1;
2402 descr
->column_width
= 150;
2403 descr
->horz_extent
= (descr
->style
& WS_HSCROLL
) ? 1 : 0;
2404 descr
->horz_pos
= 0;
2407 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2408 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2409 descr
->in_focus
= FALSE
;
2410 descr
->captured
= FALSE
;
2412 descr
->locale
= 0; /* FIXME */
2415 if (is_old_app(hwnd
) && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2417 /* Win95 document "List Box Differences" from MSDN:
2418 If a list box in a version 3.x application has either the
2419 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2420 horizontal and vertical scroll bars.
2422 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2427 TRACE_(combo
)("[%p]: resetting owner %p -> %p\n", hwnd
, descr
->owner
, lphc
->self
);
2428 descr
->owner
= lphc
->self
;
2431 SetWindowLongA( hwnd
, 0, (LONG
)descr
);
2433 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2435 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2436 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2437 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2438 descr
->item_height
= LISTBOX_SetFont( hwnd
, descr
, 0 );
2440 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2442 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2444 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2445 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2449 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
2450 mis
.CtlType
= ODT_LISTBOX
;
2455 mis
.itemHeight
= descr
->item_height
;
2456 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2457 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2461 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2466 /***********************************************************************
2469 static BOOL
LISTBOX_Destroy( HWND hwnd
, LB_DESCR
*descr
)
2471 LISTBOX_ResetContent( hwnd
, descr
);
2472 SetWindowLongA( hwnd
, 0, 0 );
2473 HeapFree( GetProcessHeap(), 0, descr
);
2478 /***********************************************************************
2479 * ListBoxWndProc_common
2481 static LRESULT WINAPI
ListBoxWndProc_common( HWND hwnd
, UINT msg
,
2482 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2487 if (!(descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 )))
2489 if (msg
== WM_CREATE
)
2491 if (!LISTBOX_Create( hwnd
, NULL
))
2493 TRACE("creating wnd=%p descr=%lx\n", hwnd
, GetWindowLongA( hwnd
, 0 ) );
2496 /* Ignore all other messages before we get a WM_CREATE */
2497 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2498 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2501 TRACE("[%p]: msg %s wp %08x lp %08lx\n",
2502 hwnd
, SPY_GetMsgName(msg
, hwnd
), wParam
, lParam
);
2505 case LB_RESETCONTENT16
:
2506 case LB_RESETCONTENT
:
2507 LISTBOX_ResetContent( hwnd
, descr
);
2508 LISTBOX_UpdateScroll( hwnd
, descr
);
2509 InvalidateRect( hwnd
, NULL
, TRUE
);
2512 case LB_ADDSTRING16
:
2513 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2519 if(unicode
|| !HAS_STRINGS(descr
))
2520 textW
= (LPWSTR
)lParam
;
2523 LPSTR textA
= (LPSTR
)lParam
;
2524 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2525 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2526 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2528 wParam
= LISTBOX_FindStringPos( hwnd
, descr
, textW
, FALSE
);
2529 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2530 if (!unicode
&& HAS_STRINGS(descr
))
2531 HeapFree(GetProcessHeap(), 0, textW
);
2535 case LB_INSERTSTRING16
:
2536 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2537 wParam
= (INT
)(INT16
)wParam
;
2539 case LB_INSERTSTRING
:
2543 if(unicode
|| !HAS_STRINGS(descr
))
2544 textW
= (LPWSTR
)lParam
;
2547 LPSTR textA
= (LPSTR
)lParam
;
2548 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2549 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2550 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2552 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2553 if(!unicode
&& HAS_STRINGS(descr
))
2554 HeapFree(GetProcessHeap(), 0, textW
);
2559 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2565 if(unicode
|| !HAS_STRINGS(descr
))
2566 textW
= (LPWSTR
)lParam
;
2569 LPSTR textA
= (LPSTR
)lParam
;
2570 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2571 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2572 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2574 wParam
= LISTBOX_FindFileStrPos( hwnd
, descr
, textW
);
2575 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2576 if(!unicode
&& HAS_STRINGS(descr
))
2577 HeapFree(GetProcessHeap(), 0, textW
);
2581 case LB_DELETESTRING16
:
2582 case LB_DELETESTRING
:
2583 if (LISTBOX_RemoveItem( hwnd
, descr
, wParam
) != LB_ERR
)
2584 return descr
->nb_items
;
2588 case LB_GETITEMDATA16
:
2589 case LB_GETITEMDATA
:
2590 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2592 return descr
->items
[wParam
].data
;
2594 case LB_SETITEMDATA16
:
2595 case LB_SETITEMDATA
:
2596 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2598 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2603 return descr
->nb_items
;
2606 lParam
= (LPARAM
)MapSL(lParam
);
2609 return LISTBOX_GetText( descr
, wParam
, lParam
, unicode
);
2611 case LB_GETTEXTLEN16
:
2614 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2616 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2617 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2618 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2619 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2621 case LB_GETCURSEL16
:
2623 if (descr
->nb_items
==0)
2625 if (!IS_MULTISELECT(descr
))
2626 return descr
->selected_item
;
2628 if (descr
->selected_item
!=-1)
2629 return descr
->selected_item
;
2631 return descr
->focus_item
;
2632 /* otherwise, if the user tries to move the selection with the */
2633 /* arrow keys, we will give the application something to choke on */
2634 case LB_GETTOPINDEX16
:
2635 case LB_GETTOPINDEX
:
2636 return descr
->top_item
;
2638 case LB_GETITEMHEIGHT16
:
2639 case LB_GETITEMHEIGHT
:
2640 return LISTBOX_GetItemHeight( descr
, wParam
);
2642 case LB_SETITEMHEIGHT16
:
2643 lParam
= LOWORD(lParam
);
2645 case LB_SETITEMHEIGHT
:
2646 return LISTBOX_SetItemHeight( hwnd
, descr
, wParam
, lParam
, TRUE
);
2648 case LB_ITEMFROMPOINT
:
2653 pt
.x
= LOWORD(lParam
);
2654 pt
.y
= HIWORD(lParam
);
2657 rect
.right
= descr
->width
;
2658 rect
.bottom
= descr
->height
;
2660 return MAKELONG( LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
),
2661 !PtInRect( &rect
, pt
) );
2664 case LB_SETCARETINDEX16
:
2665 case LB_SETCARETINDEX
:
2666 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2667 if (LISTBOX_SetCaretIndex( hwnd
, descr
, wParam
, !lParam
) == LB_ERR
)
2674 case LB_GETCARETINDEX16
:
2675 case LB_GETCARETINDEX
:
2676 return descr
->focus_item
;
2678 case LB_SETTOPINDEX16
:
2679 case LB_SETTOPINDEX
:
2680 return LISTBOX_SetTopItem( hwnd
, descr
, wParam
, TRUE
);
2682 case LB_SETCOLUMNWIDTH16
:
2683 case LB_SETCOLUMNWIDTH
:
2684 return LISTBOX_SetColumnWidth( hwnd
, descr
, wParam
);
2686 case LB_GETITEMRECT16
:
2689 ret
= LISTBOX_GetItemRect( descr
, (INT16
)wParam
, &rect
);
2690 CONV_RECT32TO16( &rect
, MapSL(lParam
) );
2694 case LB_GETITEMRECT
:
2695 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2697 case LB_FINDSTRING16
:
2698 wParam
= (INT
)(INT16
)wParam
;
2699 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2705 if(unicode
|| !HAS_STRINGS(descr
))
2706 textW
= (LPWSTR
)lParam
;
2709 LPSTR textA
= (LPSTR
)lParam
;
2710 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2711 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2712 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2714 ret
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, FALSE
);
2715 if(!unicode
&& HAS_STRINGS(descr
))
2716 HeapFree(GetProcessHeap(), 0, textW
);
2720 case LB_FINDSTRINGEXACT16
:
2721 wParam
= (INT
)(INT16
)wParam
;
2722 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2724 case LB_FINDSTRINGEXACT
:
2728 if(unicode
|| !HAS_STRINGS(descr
))
2729 textW
= (LPWSTR
)lParam
;
2732 LPSTR textA
= (LPSTR
)lParam
;
2733 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2734 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2735 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2737 ret
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, TRUE
);
2738 if(!unicode
&& HAS_STRINGS(descr
))
2739 HeapFree(GetProcessHeap(), 0, textW
);
2743 case LB_SELECTSTRING16
:
2744 wParam
= (INT
)(INT16
)wParam
;
2745 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2747 case LB_SELECTSTRING
:
2752 if(HAS_STRINGS(descr
))
2753 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2754 debugstr_a((LPSTR
)lParam
));
2755 if(unicode
|| !HAS_STRINGS(descr
))
2756 textW
= (LPWSTR
)lParam
;
2759 LPSTR textA
= (LPSTR
)lParam
;
2760 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2761 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2762 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2764 index
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, FALSE
);
2765 if(!unicode
&& HAS_STRINGS(descr
))
2766 HeapFree(GetProcessHeap(), 0, textW
);
2767 if (index
!= LB_ERR
)
2769 LISTBOX_SetCaretIndex( hwnd
, descr
, index
, TRUE
);
2770 LISTBOX_SetSelection( hwnd
, descr
, index
, TRUE
, FALSE
);
2776 wParam
= (INT
)(INT16
)wParam
;
2779 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2781 return descr
->items
[wParam
].selected
;
2784 lParam
= (INT
)(INT16
)lParam
;
2787 return LISTBOX_SetSelection( hwnd
, descr
, lParam
, wParam
, FALSE
);
2789 case LB_SETCURSEL16
:
2790 wParam
= (INT
)(INT16
)wParam
;
2793 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2794 LISTBOX_SetCaretIndex( hwnd
, descr
, wParam
, TRUE
);
2795 return LISTBOX_SetSelection( hwnd
, descr
, wParam
, TRUE
, FALSE
);
2797 case LB_GETSELCOUNT16
:
2798 case LB_GETSELCOUNT
:
2799 return LISTBOX_GetSelCount( descr
);
2801 case LB_GETSELITEMS16
:
2802 return LISTBOX_GetSelItems16( descr
, wParam
, (LPINT16
)MapSL(lParam
) );
2804 case LB_GETSELITEMS
:
2805 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2807 case LB_SELITEMRANGE16
:
2808 case LB_SELITEMRANGE
:
2809 if (LOWORD(lParam
) <= HIWORD(lParam
))
2810 return LISTBOX_SelectItemRange( hwnd
, descr
, LOWORD(lParam
),
2811 HIWORD(lParam
), wParam
);
2813 return LISTBOX_SelectItemRange( hwnd
, descr
, HIWORD(lParam
),
2814 LOWORD(lParam
), wParam
);
2816 case LB_SELITEMRANGEEX16
:
2817 case LB_SELITEMRANGEEX
:
2818 if ((INT
)lParam
>= (INT
)wParam
)
2819 return LISTBOX_SelectItemRange( hwnd
, descr
, wParam
, lParam
, TRUE
);
2821 return LISTBOX_SelectItemRange( hwnd
, descr
, lParam
, wParam
, FALSE
);
2823 case LB_GETHORIZONTALEXTENT16
:
2824 case LB_GETHORIZONTALEXTENT
:
2825 return descr
->horz_extent
;
2827 case LB_SETHORIZONTALEXTENT16
:
2828 case LB_SETHORIZONTALEXTENT
:
2829 return LISTBOX_SetHorizontalExtent( hwnd
, descr
, wParam
);
2831 case LB_GETANCHORINDEX16
:
2832 case LB_GETANCHORINDEX
:
2833 return descr
->anchor_item
;
2835 case LB_SETANCHORINDEX16
:
2836 wParam
= (INT
)(INT16
)wParam
;
2838 case LB_SETANCHORINDEX
:
2839 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2841 descr
->anchor_item
= (INT
)wParam
;
2845 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2846 * be set automatically (this is different in Win32) */
2847 if (wParam
& DDL_DRIVES
) wParam
|= DDL_EXCLUSIVE
;
2848 lParam
= (LPARAM
)MapSL(lParam
);
2855 textW
= (LPWSTR
)lParam
;
2858 LPSTR textA
= (LPSTR
)lParam
;
2859 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2860 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2861 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2863 ret
= LISTBOX_Directory( hwnd
, descr
, wParam
, textW
, msg
== LB_DIR
);
2865 HeapFree(GetProcessHeap(), 0, textW
);
2870 return descr
->locale
;
2873 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2876 case LB_INITSTORAGE
:
2877 return LISTBOX_InitStorage( hwnd
, descr
, wParam
);
2880 return LISTBOX_SetCount( hwnd
, descr
, (INT
)wParam
);
2882 case LB_SETTABSTOPS16
:
2883 return LISTBOX_SetTabStops( hwnd
, descr
, (INT
)(INT16
)wParam
, MapSL(lParam
), TRUE
);
2885 case LB_SETTABSTOPS
:
2886 return LISTBOX_SetTabStops( hwnd
, descr
, wParam
, (LPINT
)lParam
, FALSE
);
2890 if (descr
->caret_on
)
2892 descr
->caret_on
= TRUE
;
2893 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2894 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2899 if (!descr
->caret_on
)
2901 descr
->caret_on
= FALSE
;
2902 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2903 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2907 return LISTBOX_Destroy( hwnd
, descr
);
2910 InvalidateRect( hwnd
, NULL
, TRUE
);
2914 LISTBOX_SetRedraw( hwnd
, descr
, wParam
!= 0 );
2918 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2923 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( hwnd
, &ps
);
2924 ret
= LISTBOX_Paint( hwnd
, descr
, hdc
);
2925 if( !wParam
) EndPaint( hwnd
, &ps
);
2929 LISTBOX_UpdateSize( hwnd
, descr
);
2932 return (LRESULT
)descr
->font
;
2934 LISTBOX_SetFont( hwnd
, descr
, (HFONT
)wParam
);
2935 if (lParam
) InvalidateRect( hwnd
, 0, TRUE
);
2938 descr
->in_focus
= TRUE
;
2939 descr
->caret_on
= TRUE
;
2940 if (descr
->focus_item
!= -1)
2941 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2942 SEND_NOTIFICATION( hwnd
, descr
, LBN_SETFOCUS
);
2945 descr
->in_focus
= FALSE
;
2946 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2947 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2948 SEND_NOTIFICATION( hwnd
, descr
, LBN_KILLFOCUS
);
2951 return LISTBOX_HandleHScroll( hwnd
, descr
, wParam
);
2953 return LISTBOX_HandleVScroll( hwnd
, descr
, wParam
);
2955 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2956 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
2957 return LISTBOX_HandleMouseWheel( hwnd
, descr
, wParam
);
2958 case WM_LBUTTONDOWN
:
2959 return LISTBOX_HandleLButtonDown( hwnd
, descr
, wParam
,
2960 (INT16
)LOWORD(lParam
),
2961 (INT16
)HIWORD(lParam
) );
2962 case WM_LBUTTONDBLCLK
:
2963 if (descr
->style
& LBS_NOTIFY
)
2964 SEND_NOTIFICATION( hwnd
, descr
, LBN_DBLCLK
);
2967 if (GetCapture() == hwnd
)
2968 LISTBOX_HandleMouseMove( hwnd
, descr
, (INT16
)LOWORD(lParam
),
2969 (INT16
)HIWORD(lParam
) );
2972 return LISTBOX_HandleLButtonUp( hwnd
, descr
);
2974 return LISTBOX_HandleKeyDown( hwnd
, descr
, wParam
);
2979 charW
= (WCHAR
)wParam
;
2982 CHAR charA
= (CHAR
)wParam
;
2983 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
2985 return LISTBOX_HandleChar( hwnd
, descr
, charW
);
2988 return LISTBOX_HandleSystemTimer( hwnd
, descr
);
2990 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
2993 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
2994 wParam
, (LPARAM
)hwnd
);
2995 TRACE("hbrush = %p\n", hbrush
);
2997 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3000 GetClientRect(hwnd
, &rect
);
3001 FillRect((HDC
)wParam
, &rect
, hbrush
);
3007 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3008 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3012 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3013 WARN("[%p]: unknown msg %04x wp %08x lp %08lx\n",
3014 hwnd
, msg
, wParam
, lParam
);
3015 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3016 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3021 /***********************************************************************
3024 * This is just a wrapper for the real wndproc, it only does window locking
3027 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3029 if (!IsWindow(hwnd
)) return 0;
3030 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3033 /***********************************************************************
3036 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3038 if (!IsWindow(hwnd
)) return 0;
3039 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);
3042 /***********************************************************************
3043 * ComboLBWndProc_common
3045 * The real combo listbox wndproc
3047 static LRESULT WINAPI
ComboLBWndProc_common( HWND hwnd
, UINT msg
,
3048 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
3054 if (!(descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 )))
3056 if (msg
== WM_CREATE
)
3058 CREATESTRUCTA
*lpcs
= (CREATESTRUCTA
*)lParam
;
3059 TRACE_(combo
)("\tpassed parent handle = %p\n",lpcs
->lpCreateParams
);
3060 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
3061 return LISTBOX_Create( hwnd
, lphc
);
3063 /* Ignore all other messages before we get a WM_CREATE */
3064 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3065 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3068 TRACE_(combo
)("[%p]: msg %s wp %08x lp %08lx\n",
3069 hwnd
, SPY_GetMsgName(msg
, hwnd
), wParam
, lParam
);
3071 if ((lphc
= descr
->lphc
) != NULL
)
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
);
3112 /* else we are in Win3.1 look, go with the default behavior. */
3116 if (TWEAK_WineLook
> WIN31_LOOK
)
3122 * If the mouse button "up" is not in the listbox,
3123 * we make sure there is no selection by re-selecting the
3124 * item that was selected when the listbox was made visible.
3126 mousePos
.x
= (INT16
)LOWORD(lParam
);
3127 mousePos
.y
= (INT16
)HIWORD(lParam
);
3129 GetClientRect(hwnd
, &clientRect
);
3132 * When the user clicks outside the combobox and the focus
3133 * is lost, the owning combobox will send a fake buttonup with
3134 * 0xFFFFFFF as the mouse location, we must also revert the
3135 * selection to the original selection.
3137 if ( (lParam
== (LPARAM
)-1) ||
3138 (!PtInRect( &clientRect
, mousePos
)) )
3140 LISTBOX_MoveCaret( hwnd
, descr
, lphc
->droppedIndex
, FALSE
);
3143 return LISTBOX_HandleLButtonUp( hwnd
, descr
);
3144 case WM_LBUTTONDBLCLK
:
3145 case WM_LBUTTONDOWN
:
3146 return LISTBOX_HandleLButtonDownCombo(hwnd
, descr
, msg
, wParam
,
3147 (INT16
)LOWORD(lParam
),
3148 (INT16
)HIWORD(lParam
) );
3152 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3154 /* for some reason(?) Windows makes it possible to
3155 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3157 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3158 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3159 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3161 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3165 return LISTBOX_HandleKeyDown( hwnd
, descr
, wParam
);
3167 case LB_SETCURSEL16
:
3169 lRet
= unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3170 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3171 lRet
=(lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
3174 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3180 /* default handling: call listbox wnd proc */
3181 lRet
= unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3182 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3184 TRACE_(combo
)("\t default on msg [%04x]\n", (UINT16
)msg
);
3189 /***********************************************************************
3192 * NOTE: in Windows, winproc address of the ComboLBox is the same
3193 * as that of the Listbox.
3195 LRESULT WINAPI
ComboLBWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3197 if (!IsWindow(hwnd
)) return 0;
3198 return ComboLBWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3201 /***********************************************************************
3204 LRESULT WINAPI
ComboLBWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3206 if (!IsWindow(hwnd
)) return 0;
3207 return ComboLBWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);