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 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
720 nb_items
* sizeof(LB_ITEMDATA
));
723 item
= HeapAlloc( GetProcessHeap(), 0,
724 nb_items
* sizeof(LB_ITEMDATA
));
729 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
737 /***********************************************************************
738 * LISTBOX_SetTabStops
740 static BOOL
LISTBOX_SetTabStops( HWND hwnd
, LB_DESCR
*descr
, INT count
,
741 LPINT tabs
, BOOL short_ints
)
743 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
744 if (descr
->tabs
) HeapFree( GetProcessHeap(), 0, descr
->tabs
);
745 if (!(descr
->nb_tabs
= count
))
750 /* FIXME: count = 1 */
751 if (!(descr
->tabs
= (INT
*)HeapAlloc( GetProcessHeap(), 0,
752 descr
->nb_tabs
* sizeof(INT
) )))
757 LPINT16 p
= (LPINT16
)tabs
;
759 TRACE("[%p]: settabstops ", hwnd
);
760 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
761 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
762 if (TRACE_ON(listbox
)) TRACE("%hd ", descr
->tabs
[i
]);
764 if (TRACE_ON(listbox
)) TRACE("\n");
766 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
767 /* FIXME: repaint the window? */
772 /***********************************************************************
775 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPARAM lParam
, BOOL unicode
)
777 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
778 if (HAS_STRINGS(descr
))
781 return strlenW(descr
->items
[index
].str
);
783 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
787 LPWSTR buffer
= (LPWSTR
)lParam
;
788 strcpyW( buffer
, descr
->items
[index
].str
);
789 return strlenW(buffer
);
793 LPSTR buffer
= (LPSTR
)lParam
;
794 return WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1, buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
798 *((LPDWORD
)lParam
)=*(LPDWORD
)(&descr
->items
[index
].data
);
799 return sizeof(DWORD
);
804 /***********************************************************************
805 * LISTBOX_FindStringPos
807 * Find the nearest string located before a given string in sort order.
808 * If 'exact' is TRUE, return an error if we don't get an exact match.
810 static INT
LISTBOX_FindStringPos( HWND hwnd
, LB_DESCR
*descr
, LPCWSTR str
,
813 INT index
, min
, max
, res
= -1;
815 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
817 max
= descr
->nb_items
;
820 index
= (min
+ max
) / 2;
821 if (HAS_STRINGS(descr
))
822 res
= lstrcmpiW( str
, descr
->items
[index
].str
);
825 COMPAREITEMSTRUCT cis
;
826 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
828 cis
.CtlType
= ODT_LISTBOX
;
831 /* note that some application (MetaStock) expects the second item
832 * to be in the listbox */
834 cis
.itemData1
= (DWORD
)str
;
836 cis
.itemData2
= descr
->items
[index
].data
;
837 cis
.dwLocaleId
= descr
->locale
;
838 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
840 if (!res
) return index
;
841 if (res
< 0) max
= index
;
842 else min
= index
+ 1;
844 return exact
? -1 : max
;
848 /***********************************************************************
849 * LISTBOX_FindFileStrPos
851 * Find the nearest string located before a given string in directory
852 * sort order (i.e. first files, then directories, then drives).
854 static INT
LISTBOX_FindFileStrPos( HWND hwnd
, LB_DESCR
*descr
, LPCWSTR str
)
856 INT min
, max
, res
= -1;
858 if (!HAS_STRINGS(descr
))
859 return LISTBOX_FindStringPos( hwnd
, descr
, str
, FALSE
);
861 max
= descr
->nb_items
;
864 INT index
= (min
+ max
) / 2;
865 LPCWSTR p
= descr
->items
[index
].str
;
866 if (*p
== '[') /* drive or directory */
868 if (*str
!= '[') res
= -1;
869 else if (p
[1] == '-') /* drive */
871 if (str
[1] == '-') res
= str
[2] - p
[2];
876 if (str
[1] == '-') res
= 1;
877 else res
= lstrcmpiW( str
, p
);
882 if (*str
== '[') res
= 1;
883 else res
= lstrcmpiW( str
, p
);
885 if (!res
) return index
;
886 if (res
< 0) max
= index
;
887 else min
= index
+ 1;
893 /***********************************************************************
896 * Find the item beginning with a given string.
898 static INT
LISTBOX_FindString( HWND hwnd
, LB_DESCR
*descr
, INT start
,
899 LPCWSTR str
, BOOL exact
)
904 if (start
>= descr
->nb_items
) start
= -1;
905 item
= descr
->items
+ start
+ 1;
906 if (HAS_STRINGS(descr
))
908 if (!str
|| ! str
[0] ) return LB_ERR
;
911 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
912 if (!lstrcmpiW( str
, item
->str
)) return i
;
913 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
914 if (!lstrcmpiW( str
, item
->str
)) return i
;
918 /* Special case for drives and directories: ignore prefix */
919 #define CHECK_DRIVE(item) \
920 if ((item)->str[0] == '[') \
922 if (!strncmpiW( str, (item)->str+1, len )) return i; \
923 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
927 INT len
= strlenW(str
);
928 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
930 if (!strncmpiW( str
, item
->str
, len
)) return i
;
933 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
935 if (!strncmpiW( str
, item
->str
, len
)) return i
;
943 if (exact
&& (descr
->style
& LBS_SORT
))
944 /* If sorted, use a WM_COMPAREITEM binary search */
945 return LISTBOX_FindStringPos( hwnd
, descr
, str
, TRUE
);
947 /* Otherwise use a linear search */
948 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
949 if (item
->data
== (DWORD
)str
) return i
;
950 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
951 if (item
->data
== (DWORD
)str
) return i
;
957 /***********************************************************************
958 * LISTBOX_GetSelCount
960 static LRESULT
LISTBOX_GetSelCount( LB_DESCR
*descr
)
963 LB_ITEMDATA
*item
= descr
->items
;
965 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
966 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
967 if (item
->selected
) count
++;
972 /***********************************************************************
973 * LISTBOX_GetSelItems16
975 static LRESULT
LISTBOX_GetSelItems16( LB_DESCR
*descr
, INT16 max
, LPINT16 array
)
978 LB_ITEMDATA
*item
= descr
->items
;
980 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
981 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
982 if (item
->selected
) array
[count
++] = (INT16
)i
;
987 /***********************************************************************
988 * LISTBOX_GetSelItems
990 static LRESULT
LISTBOX_GetSelItems( LB_DESCR
*descr
, INT max
, LPINT array
)
993 LB_ITEMDATA
*item
= descr
->items
;
995 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
996 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
997 if (item
->selected
) array
[count
++] = i
;
1002 /***********************************************************************
1005 static LRESULT
LISTBOX_Paint( HWND hwnd
, LB_DESCR
*descr
, HDC hdc
)
1007 INT i
, col_pos
= descr
->page_size
- 1;
1009 RECT focusRect
= {-1, -1, -1, -1};
1011 HBRUSH hbrush
, oldBrush
= 0;
1013 if (descr
->style
& LBS_NOREDRAW
) return 0;
1015 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1016 if (descr
->style
& LBS_MULTICOLUMN
)
1017 rect
.right
= rect
.left
+ descr
->column_width
;
1018 else if (descr
->horz_pos
)
1020 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1021 rect
.right
+= descr
->horz_pos
;
1024 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1025 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
1026 (WPARAM
)hdc
, (LPARAM
)hwnd
);
1027 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1028 if (!IsWindowEnabled(hwnd
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1030 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1033 /* Special case for empty listbox: paint focus rect */
1034 rect
.bottom
= rect
.top
+ descr
->item_height
;
1035 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, descr
->focus_item
,
1037 rect
.top
= rect
.bottom
;
1040 /* Paint all the item, regarding the selection
1041 Focus state will be painted after */
1043 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1045 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1046 rect
.bottom
= rect
.top
+ descr
->item_height
;
1048 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1050 if (i
== descr
->focus_item
)
1052 /* keep the focus rect, to paint the focus item after */
1053 focusRect
.left
= rect
.left
;
1054 focusRect
.right
= rect
.right
;
1055 focusRect
.top
= rect
.top
;
1056 focusRect
.bottom
= rect
.bottom
;
1058 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1059 rect
.top
= rect
.bottom
;
1061 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1063 if (!IS_OWNERDRAW(descr
))
1065 /* Clear the bottom of the column */
1066 if (rect
.top
< descr
->height
)
1068 rect
.bottom
= descr
->height
;
1069 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1070 &rect
, NULL
, 0, NULL
);
1074 /* Go to the next column */
1075 rect
.left
+= descr
->column_width
;
1076 rect
.right
+= descr
->column_width
;
1078 col_pos
= descr
->page_size
- 1;
1083 if (rect
.top
>= descr
->height
) break;
1087 /* Paint the focus item now */
1088 if (focusRect
.top
!= focusRect
.bottom
&& descr
->caret_on
)
1089 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1091 if (!IS_OWNERDRAW(descr
))
1093 /* Clear the remainder of the client area */
1094 if (rect
.top
< descr
->height
)
1096 rect
.bottom
= descr
->height
;
1097 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1098 &rect
, NULL
, 0, NULL
);
1100 if (rect
.right
< descr
->width
)
1102 rect
.left
= rect
.right
;
1103 rect
.right
= descr
->width
;
1105 rect
.bottom
= descr
->height
;
1106 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1107 &rect
, NULL
, 0, NULL
);
1110 if (oldFont
) SelectObject( hdc
, oldFont
);
1111 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1116 /***********************************************************************
1117 * LISTBOX_InvalidateItems
1119 * Invalidate all items from a given item. If the specified item is not
1120 * visible, nothing happens.
1122 static void LISTBOX_InvalidateItems( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1126 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1128 if (descr
->style
& LBS_NOREDRAW
)
1130 descr
->style
|= LBS_DISPLAYCHANGED
;
1133 rect
.bottom
= descr
->height
;
1134 InvalidateRect( hwnd
, &rect
, TRUE
);
1135 if (descr
->style
& LBS_MULTICOLUMN
)
1137 /* Repaint the other columns */
1138 rect
.left
= rect
.right
;
1139 rect
.right
= descr
->width
;
1141 InvalidateRect( hwnd
, &rect
, TRUE
);
1147 /***********************************************************************
1148 * LISTBOX_GetItemHeight
1150 static LRESULT
LISTBOX_GetItemHeight( LB_DESCR
*descr
, INT index
)
1152 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1154 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1155 return descr
->items
[index
].height
;
1157 else return descr
->item_height
;
1161 /***********************************************************************
1162 * LISTBOX_SetItemHeight
1164 static LRESULT
LISTBOX_SetItemHeight( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1165 INT height
, BOOL repaint
)
1167 if (!height
) height
= 1;
1169 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1171 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1172 TRACE("[%p]: item %d height = %d\n", hwnd
, index
, height
);
1173 descr
->items
[index
].height
= height
;
1174 LISTBOX_UpdateScroll( hwnd
, descr
);
1176 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1178 else if (height
!= descr
->item_height
)
1180 TRACE("[%p]: new height = %d\n", hwnd
, height
);
1181 descr
->item_height
= height
;
1182 LISTBOX_UpdatePage( hwnd
, descr
);
1183 LISTBOX_UpdateScroll( hwnd
, descr
);
1185 InvalidateRect( hwnd
, 0, TRUE
);
1191 /***********************************************************************
1192 * LISTBOX_SetHorizontalPos
1194 static void LISTBOX_SetHorizontalPos( HWND hwnd
, LB_DESCR
*descr
, INT pos
)
1198 if (pos
> descr
->horz_extent
- descr
->width
)
1199 pos
= descr
->horz_extent
- descr
->width
;
1200 if (pos
< 0) pos
= 0;
1201 if (!(diff
= descr
->horz_pos
- pos
)) return;
1202 TRACE("[%p]: new horz pos = %d\n", hwnd
, pos
);
1203 descr
->horz_pos
= pos
;
1204 LISTBOX_UpdateScroll( hwnd
, descr
);
1205 if (abs(diff
) < descr
->width
)
1206 ScrollWindowEx( hwnd
, diff
, 0, NULL
, NULL
, 0, NULL
,
1207 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1209 InvalidateRect( hwnd
, NULL
, TRUE
);
1213 /***********************************************************************
1214 * LISTBOX_SetHorizontalExtent
1216 static LRESULT
LISTBOX_SetHorizontalExtent( HWND hwnd
, LB_DESCR
*descr
,
1219 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1221 if (extent
<= 0) extent
= 1;
1222 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1223 TRACE("[%p]: new horz extent = %d\n", hwnd
, extent
);
1224 descr
->horz_extent
= extent
;
1225 if (descr
->horz_pos
> extent
- descr
->width
)
1226 LISTBOX_SetHorizontalPos( hwnd
, descr
, extent
- descr
->width
);
1228 LISTBOX_UpdateScroll( hwnd
, descr
);
1233 /***********************************************************************
1234 * LISTBOX_SetColumnWidth
1236 static LRESULT
LISTBOX_SetColumnWidth( HWND hwnd
, LB_DESCR
*descr
, INT width
)
1238 if (width
== descr
->column_width
) return LB_OKAY
;
1239 TRACE("[%p]: new column width = %d\n", hwnd
, width
);
1240 descr
->column_width
= width
;
1241 LISTBOX_UpdatePage( hwnd
, descr
);
1246 /***********************************************************************
1249 * Returns the item height.
1251 static INT
LISTBOX_SetFont( HWND hwnd
, LB_DESCR
*descr
, HFONT font
)
1259 if (!(hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
)))
1261 ERR("unable to get DC.\n" );
1264 if (font
) oldFont
= SelectObject( hdc
, font
);
1265 GetTextMetricsW( hdc
, &tm
);
1266 if (oldFont
) SelectObject( hdc
, oldFont
);
1267 ReleaseDC( hwnd
, hdc
);
1268 if (!IS_OWNERDRAW(descr
))
1269 LISTBOX_SetItemHeight( hwnd
, descr
, 0, tm
.tmHeight
, FALSE
);
1270 return tm
.tmHeight
;
1274 /***********************************************************************
1275 * LISTBOX_MakeItemVisible
1277 * Make sure that a given item is partially or fully visible.
1279 static void LISTBOX_MakeItemVisible( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1284 if (index
<= descr
->top_item
) top
= index
;
1285 else if (descr
->style
& LBS_MULTICOLUMN
)
1287 INT cols
= descr
->width
;
1288 if (!fully
) cols
+= descr
->column_width
- 1;
1289 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1291 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1292 top
= index
- descr
->page_size
* (cols
- 1);
1294 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1296 INT height
= fully
? descr
->items
[index
].height
: 1;
1297 for (top
= index
; top
> descr
->top_item
; top
--)
1298 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1302 if (index
< descr
->top_item
+ descr
->page_size
) return;
1303 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1304 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1305 top
= index
- descr
->page_size
+ 1;
1307 LISTBOX_SetTopItem( hwnd
, descr
, top
, TRUE
);
1310 /***********************************************************************
1311 * LISTBOX_SetCaretIndex
1314 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1317 static LRESULT
LISTBOX_SetCaretIndex( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1318 BOOL fully_visible
)
1320 INT oldfocus
= descr
->focus_item
;
1322 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1323 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1324 if (index
== oldfocus
) return LB_OKAY
;
1325 descr
->focus_item
= index
;
1326 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1327 LISTBOX_RepaintItem( hwnd
, descr
, oldfocus
, ODA_FOCUS
);
1329 LISTBOX_MakeItemVisible( hwnd
, descr
, index
, fully_visible
);
1330 if (descr
->caret_on
&& (descr
->in_focus
))
1331 LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_FOCUS
);
1337 /***********************************************************************
1338 * LISTBOX_SelectItemRange
1340 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1342 static LRESULT
LISTBOX_SelectItemRange( HWND hwnd
, LB_DESCR
*descr
, INT first
,
1347 /* A few sanity checks */
1349 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1350 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1351 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1352 if (last
== -1) last
= descr
->nb_items
- 1;
1353 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1354 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1355 /* selected_item reflects last selected/unselected item on multiple sel */
1356 descr
->selected_item
= last
;
1358 if (on
) /* Turn selection on */
1360 for (i
= first
; i
<= last
; i
++)
1362 if (descr
->items
[i
].selected
) continue;
1363 descr
->items
[i
].selected
= TRUE
;
1364 LISTBOX_RepaintItem( hwnd
, descr
, i
, ODA_SELECT
);
1366 LISTBOX_SetCaretIndex( hwnd
, descr
, last
, TRUE
);
1368 else /* Turn selection off */
1370 for (i
= first
; i
<= last
; i
++)
1372 if (!descr
->items
[i
].selected
) continue;
1373 descr
->items
[i
].selected
= FALSE
;
1374 LISTBOX_RepaintItem( hwnd
, descr
, i
, ODA_SELECT
);
1380 /***********************************************************************
1381 * LISTBOX_SetSelection
1383 static LRESULT
LISTBOX_SetSelection( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1384 BOOL on
, BOOL send_notify
)
1386 TRACE( "index=%d notify=%s\n", index
, send_notify
? "YES" : "NO" );
1388 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1389 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1390 if (descr
->style
& LBS_MULTIPLESEL
)
1392 if (index
== -1) /* Select all items */
1393 return LISTBOX_SelectItemRange( hwnd
, descr
, 0, -1, on
);
1394 else /* Only one item */
1395 return LISTBOX_SelectItemRange( hwnd
, descr
, index
, index
, on
);
1399 INT oldsel
= descr
->selected_item
;
1400 if (index
== oldsel
) return LB_OKAY
;
1401 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1402 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1403 descr
->selected_item
= index
;
1404 if (oldsel
!= -1) LISTBOX_RepaintItem( hwnd
, descr
, oldsel
, ODA_SELECT
);
1405 if (index
!= -1) LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_SELECT
);
1406 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( hwnd
, descr
,
1407 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1409 if( descr
->lphc
) /* set selection change flag for parent combo */
1410 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1416 /***********************************************************************
1419 * Change the caret position and extend the selection to the new caret.
1421 static void LISTBOX_MoveCaret( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1422 BOOL fully_visible
)
1424 INT oldfocus
= descr
->focus_item
;
1426 if ((index
< 0) || (index
>= descr
->nb_items
))
1429 /* Important, repaint needs to be done in this order if
1430 you want to mimic Windows behavior:
1431 1. Remove the focus and paint the item
1432 2. Remove the selection and paint the item(s)
1433 3. Set the selection and repaint the item(s)
1434 4. Set the focus to 'index' and repaint the item */
1436 /* 1. remove the focus and repaint the item */
1437 descr
->focus_item
= -1;
1438 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1439 LISTBOX_RepaintItem( hwnd
, descr
, oldfocus
, ODA_FOCUS
);
1441 /* 2. then turn off the previous selection */
1442 /* 3. repaint the new selected item */
1443 if (descr
->style
& LBS_EXTENDEDSEL
)
1445 if (descr
->anchor_item
!= -1)
1447 INT first
= min( index
, descr
->anchor_item
);
1448 INT last
= max( index
, descr
->anchor_item
);
1450 LISTBOX_SelectItemRange( hwnd
, descr
, 0, first
- 1, FALSE
);
1451 LISTBOX_SelectItemRange( hwnd
, descr
, last
+ 1, -1, FALSE
);
1452 LISTBOX_SelectItemRange( hwnd
, descr
, first
, last
, TRUE
);
1455 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1457 /* Set selection to new caret item */
1458 LISTBOX_SetSelection( hwnd
, descr
, index
, TRUE
, FALSE
);
1461 /* 4. repaint the new item with the focus */
1462 descr
->focus_item
= index
;
1463 LISTBOX_MakeItemVisible( hwnd
, descr
, index
, fully_visible
);
1464 if (descr
->caret_on
&& (descr
->in_focus
))
1465 LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_FOCUS
);
1469 /***********************************************************************
1470 * LISTBOX_InsertItem
1472 static LRESULT
LISTBOX_InsertItem( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1473 LPWSTR str
, DWORD data
)
1477 INT oldfocus
= descr
->focus_item
;
1479 if (index
== -1) index
= descr
->nb_items
;
1480 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1481 if (!descr
->items
) max_items
= 0;
1482 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1483 if (descr
->nb_items
== max_items
)
1485 /* We need to grow the array */
1486 max_items
+= LB_ARRAY_GRANULARITY
;
1488 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1489 max_items
* sizeof(LB_ITEMDATA
) );
1491 item
= HeapAlloc( GetProcessHeap(), 0,
1492 max_items
* sizeof(LB_ITEMDATA
) );
1495 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
1498 descr
->items
= item
;
1501 /* Insert the item structure */
1503 item
= &descr
->items
[index
];
1504 if (index
< descr
->nb_items
)
1505 RtlMoveMemory( item
+ 1, item
,
1506 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1510 item
->selected
= FALSE
;
1513 /* Get item height */
1515 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1517 MEASUREITEMSTRUCT mis
;
1518 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
1520 mis
.CtlType
= ODT_LISTBOX
;
1523 mis
.itemData
= descr
->items
[index
].data
;
1524 mis
.itemHeight
= descr
->item_height
;
1525 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1526 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1527 TRACE("[%p]: measure item %d (%s) = %d\n",
1528 hwnd
, index
, str
? debugstr_w(str
) : "", item
->height
);
1531 /* Repaint the items */
1533 LISTBOX_UpdateScroll( hwnd
, descr
);
1534 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1536 /* Move selection and focused item */
1537 /* If listbox was empty, set focus to the first item */
1538 if (descr
->nb_items
== 1)
1539 LISTBOX_SetCaretIndex( hwnd
, descr
, 0, FALSE
);
1540 /* single select don't change selection index in win31 */
1541 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1543 descr
->selected_item
++;
1544 LISTBOX_SetSelection( hwnd
, descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1548 if (index
<= descr
->selected_item
)
1550 descr
->selected_item
++;
1551 descr
->focus_item
= oldfocus
; /* focus not changed */
1558 /***********************************************************************
1559 * LISTBOX_InsertString
1561 static LRESULT
LISTBOX_InsertString( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1564 LPWSTR new_str
= NULL
;
1568 if (HAS_STRINGS(descr
))
1570 static const WCHAR empty_stringW
[] = { 0 };
1571 if (!str
) str
= empty_stringW
;
1572 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1574 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
1577 strcpyW(new_str
, str
);
1579 else data
= (DWORD
)str
;
1581 if (index
== -1) index
= descr
->nb_items
;
1582 if ((ret
= LISTBOX_InsertItem( hwnd
, descr
, index
, new_str
, data
)) != 0)
1584 if (new_str
) HeapFree( GetProcessHeap(), 0, new_str
);
1588 TRACE("[%p]: added item %d %s\n",
1589 hwnd
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1594 /***********************************************************************
1595 * LISTBOX_DeleteItem
1597 * Delete the content of an item. 'index' must be a valid index.
1599 static void LISTBOX_DeleteItem( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1601 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1602 * while Win95 sends it for all items with user data.
1603 * It's probably better to send it too often than not
1604 * often enough, so this is what we do here.
1606 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1608 DELETEITEMSTRUCT dis
;
1609 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
1611 dis
.CtlType
= ODT_LISTBOX
;
1614 dis
.hwndItem
= hwnd
;
1615 dis
.itemData
= descr
->items
[index
].data
;
1616 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1618 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1619 HeapFree( GetProcessHeap(), 0, descr
->items
[index
].str
);
1623 /***********************************************************************
1624 * LISTBOX_RemoveItem
1626 * Remove an item from the listbox and delete its content.
1628 static LRESULT
LISTBOX_RemoveItem( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1633 if ((index
== -1) && (descr
->nb_items
> 0)) index
= descr
->nb_items
- 1;
1634 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1636 /* We need to invalidate the original rect instead of the updated one. */
1637 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1639 LISTBOX_DeleteItem( hwnd
, descr
, index
);
1641 /* Remove the item */
1643 item
= &descr
->items
[index
];
1644 if (index
< descr
->nb_items
-1)
1645 RtlMoveMemory( item
, item
+ 1,
1646 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1648 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1650 /* Shrink the item array if possible */
1652 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1653 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1655 max_items
-= LB_ARRAY_GRANULARITY
;
1656 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1657 max_items
* sizeof(LB_ITEMDATA
) );
1658 if (item
) descr
->items
= item
;
1660 /* Repaint the items */
1662 LISTBOX_UpdateScroll( hwnd
, descr
);
1663 /* if we removed the scrollbar, reset the top of the list
1664 (correct for owner-drawn ???) */
1665 if (descr
->nb_items
== descr
->page_size
)
1666 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1668 /* Move selection and focused item */
1669 if (!IS_MULTISELECT(descr
))
1671 if (index
== descr
->selected_item
)
1672 descr
->selected_item
= -1;
1673 else if (index
< descr
->selected_item
)
1675 descr
->selected_item
--;
1676 if (ISWIN31
) /* win 31 do not change the selected item number */
1677 LISTBOX_SetSelection( hwnd
, descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1681 if (descr
->focus_item
>= descr
->nb_items
)
1683 descr
->focus_item
= descr
->nb_items
- 1;
1684 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1690 /***********************************************************************
1691 * LISTBOX_ResetContent
1693 static void LISTBOX_ResetContent( HWND hwnd
, LB_DESCR
*descr
)
1697 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( hwnd
, descr
, i
);
1698 if (descr
->items
) HeapFree( GetProcessHeap(), 0, descr
->items
);
1699 descr
->nb_items
= 0;
1700 descr
->top_item
= 0;
1701 descr
->selected_item
= -1;
1702 descr
->focus_item
= 0;
1703 descr
->anchor_item
= -1;
1704 descr
->items
= NULL
;
1708 /***********************************************************************
1711 static LRESULT
LISTBOX_SetCount( HWND hwnd
, LB_DESCR
*descr
, INT count
)
1715 if (HAS_STRINGS(descr
)) return LB_ERR
;
1716 /* FIXME: this is far from optimal... */
1717 if (count
> descr
->nb_items
)
1719 while (count
> descr
->nb_items
)
1720 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, -1, 0 )) < 0)
1723 else if (count
< descr
->nb_items
)
1725 while (count
< descr
->nb_items
)
1726 if ((ret
= LISTBOX_RemoveItem( hwnd
, descr
, -1 )) < 0)
1733 /***********************************************************************
1736 static LRESULT
LISTBOX_Directory( HWND hwnd
, LB_DESCR
*descr
, UINT attrib
,
1737 LPCWSTR filespec
, BOOL long_names
)
1740 LRESULT ret
= LB_OKAY
;
1741 WIN32_FIND_DATAW entry
;
1744 /* don't scan directory if we just want drives exclusively */
1745 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1746 /* scan directory */
1747 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1749 int le
= GetLastError();
1750 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1757 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1759 static const WCHAR bracketW
[] = { ']',0 };
1760 static const WCHAR dotW
[] = { '.',0 };
1761 if (!(attrib
& DDL_DIRECTORY
) ||
1762 !strcmpW( entry
.cAlternateFileName
, dotW
)) continue;
1764 if (long_names
) strcpyW( buffer
+ 1, entry
.cFileName
);
1765 else strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1766 strcatW(buffer
, bracketW
);
1768 else /* not a directory */
1770 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1771 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1773 if ((attrib
& DDL_EXCLUSIVE
) &&
1774 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1777 if (long_names
) strcpyW( buffer
, entry
.cFileName
);
1778 else strcpyW( buffer
, entry
.cAlternateFileName
);
1780 if (!long_names
) CharLowerW( buffer
);
1781 pos
= LISTBOX_FindFileStrPos( hwnd
, descr
, buffer
);
1782 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, pos
, buffer
)) < 0)
1784 } while (FindNextFileW( handle
, &entry
));
1785 FindClose( handle
);
1790 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1792 WCHAR buffer
[] = {'[','-','a','-',']',0};
1793 WCHAR root
[] = {'A',':','\\',0};
1795 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1797 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1798 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, -1, buffer
)) < 0)
1806 /***********************************************************************
1807 * LISTBOX_HandleVScroll
1809 static LRESULT
LISTBOX_HandleVScroll( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1813 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1814 switch(LOWORD(wParam
))
1817 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
- 1, TRUE
);
1820 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+ 1, TRUE
);
1823 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
-
1824 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1827 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+
1828 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1830 case SB_THUMBPOSITION
:
1831 LISTBOX_SetTopItem( hwnd
, descr
, HIWORD(wParam
), TRUE
);
1834 info
.cbSize
= sizeof(info
);
1835 info
.fMask
= SIF_TRACKPOS
;
1836 GetScrollInfo( hwnd
, SB_VERT
, &info
);
1837 LISTBOX_SetTopItem( hwnd
, descr
, info
.nTrackPos
, TRUE
);
1840 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1843 LISTBOX_SetTopItem( hwnd
, descr
, descr
->nb_items
, TRUE
);
1850 /***********************************************************************
1851 * LISTBOX_HandleHScroll
1853 static LRESULT
LISTBOX_HandleHScroll( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1858 if (descr
->style
& LBS_MULTICOLUMN
)
1860 switch(LOWORD(wParam
))
1863 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
-descr
->page_size
,
1867 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+descr
->page_size
,
1871 page
= descr
->width
/ descr
->column_width
;
1872 if (page
< 1) page
= 1;
1873 LISTBOX_SetTopItem( hwnd
, descr
,
1874 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1877 page
= descr
->width
/ descr
->column_width
;
1878 if (page
< 1) page
= 1;
1879 LISTBOX_SetTopItem( hwnd
, descr
,
1880 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1882 case SB_THUMBPOSITION
:
1883 LISTBOX_SetTopItem( hwnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1887 info
.cbSize
= sizeof(info
);
1888 info
.fMask
= SIF_TRACKPOS
;
1889 GetScrollInfo( hwnd
, SB_VERT
, &info
);
1890 LISTBOX_SetTopItem( hwnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1894 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1897 LISTBOX_SetTopItem( hwnd
, descr
, descr
->nb_items
, TRUE
);
1901 else if (descr
->horz_extent
)
1903 switch(LOWORD(wParam
))
1906 LISTBOX_SetHorizontalPos( hwnd
, descr
, descr
->horz_pos
- 1 );
1909 LISTBOX_SetHorizontalPos( hwnd
, descr
, descr
->horz_pos
+ 1 );
1912 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1913 descr
->horz_pos
- descr
->width
);
1916 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1917 descr
->horz_pos
+ descr
->width
);
1919 case SB_THUMBPOSITION
:
1920 LISTBOX_SetHorizontalPos( hwnd
, descr
, HIWORD(wParam
) );
1923 info
.cbSize
= sizeof(info
);
1924 info
.fMask
= SIF_TRACKPOS
;
1925 GetScrollInfo( hwnd
, SB_HORZ
, &info
);
1926 LISTBOX_SetHorizontalPos( hwnd
, descr
, info
.nTrackPos
);
1929 LISTBOX_SetHorizontalPos( hwnd
, descr
, 0 );
1932 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1933 descr
->horz_extent
- descr
->width
);
1940 static LRESULT
LISTBOX_HandleMouseWheel(HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1942 short gcWheelDelta
= 0;
1943 UINT pulScrollLines
= 3;
1945 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1947 gcWheelDelta
-= (short) HIWORD(wParam
);
1949 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
1951 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
1952 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
1953 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+ cLineScroll
, TRUE
);
1958 /***********************************************************************
1959 * LISTBOX_HandleLButtonDown
1961 static LRESULT
LISTBOX_HandleLButtonDown( HWND hwnd
, LB_DESCR
*descr
,
1962 WPARAM wParam
, INT x
, INT y
)
1964 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
1965 TRACE("[%p]: lbuttondown %d,%d item %d\n", hwnd
, x
, y
, index
);
1966 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
1968 if (!descr
->in_focus
)
1970 if( !descr
->lphc
) SetFocus( hwnd
);
1971 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
1974 if (index
== -1) return 0;
1976 if (descr
->style
& LBS_EXTENDEDSEL
)
1978 /* we should perhaps make sure that all items are deselected
1979 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1980 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1981 LISTBOX_SetSelection( hwnd, descr, -1, FALSE, FALSE);
1984 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1985 if (wParam
& MK_CONTROL
)
1987 LISTBOX_SetCaretIndex( hwnd
, descr
, index
, FALSE
);
1988 LISTBOX_SetSelection( hwnd
, descr
, index
,
1989 !descr
->items
[index
].selected
,
1990 (descr
->style
& LBS_NOTIFY
) != 0);
1992 else LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
1996 LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
1997 LISTBOX_SetSelection( hwnd
, descr
, index
,
1998 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1999 !descr
->items
[index
].selected
),
2000 (descr
->style
& LBS_NOTIFY
) != 0 );
2003 descr
->captured
= TRUE
;
2008 if (descr
->style
& LBS_NOTIFY
)
2009 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2010 MAKELPARAM( x
, y
) );
2011 if (GetWindowLongA( hwnd
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2018 if (DragDetect( hwnd
, pt
))
2019 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2026 /*************************************************************************
2027 * LISTBOX_HandleLButtonDownCombo [Internal]
2029 * Process LButtonDown message for the ComboListBox
2032 * pWnd [I] The windows internal structure
2033 * pDescr [I] The ListBox internal structure
2034 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2035 * x [I] X Mouse Coordinate
2036 * y [I] Y Mouse Coordinate
2039 * 0 since we are processing the WM_LBUTTONDOWN Message
2042 * This function is only to be used when a ListBox is a ComboListBox
2045 static LRESULT
LISTBOX_HandleLButtonDownCombo( HWND hwnd
, LB_DESCR
*pDescr
,
2046 UINT msg
, WPARAM wParam
, INT x
, INT y
)
2048 RECT clientRect
, screenRect
;
2054 GetClientRect(hwnd
, &clientRect
);
2056 if(PtInRect(&clientRect
, mousePos
))
2058 /* MousePos is in client, resume normal processing */
2059 if (msg
== WM_LBUTTONDOWN
)
2061 pDescr
->lphc
->droppedIndex
= pDescr
->nb_items
? pDescr
->selected_item
: -1;
2062 return LISTBOX_HandleLButtonDown( hwnd
, pDescr
, wParam
, x
, y
);
2064 else if (pDescr
->style
& LBS_NOTIFY
)
2065 SEND_NOTIFICATION( hwnd
, pDescr
, LBN_DBLCLK
);
2070 POINT screenMousePos
;
2071 HWND hWndOldCapture
;
2073 /* Check the Non-Client Area */
2074 screenMousePos
= mousePos
;
2075 hWndOldCapture
= GetCapture();
2077 GetWindowRect(hwnd
, &screenRect
);
2078 ClientToScreen(hwnd
, &screenMousePos
);
2080 if(!PtInRect(&screenRect
, screenMousePos
))
2082 LISTBOX_SetCaretIndex( hwnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
);
2083 LISTBOX_SetSelection( hwnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2084 COMBO_FlipListbox( pDescr
->lphc
, FALSE
, FALSE
);
2089 /* Check to see the NC is a scrollbar */
2091 LONG style
= GetWindowLongA( hwnd
, GWL_STYLE
);
2092 /* Check Vertical scroll bar */
2093 if (style
& WS_VSCROLL
)
2095 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2096 if (PtInRect( &clientRect
, mousePos
))
2098 nHitTestType
= HTVSCROLL
;
2101 /* Check horizontal scroll bar */
2102 if (style
& WS_HSCROLL
)
2104 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2105 if (PtInRect( &clientRect
, mousePos
))
2107 nHitTestType
= HTHSCROLL
;
2110 /* Windows sends this message when a scrollbar is clicked
2113 if(nHitTestType
!= 0)
2115 SendMessageW(hwnd
, WM_NCLBUTTONDOWN
, nHitTestType
,
2116 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2118 /* Resume the Capture after scrolling is complete
2120 if(hWndOldCapture
!= 0)
2122 SetCapture(hWndOldCapture
);
2129 /***********************************************************************
2130 * LISTBOX_HandleLButtonUp
2132 static LRESULT
LISTBOX_HandleLButtonUp( HWND hwnd
, LB_DESCR
*descr
)
2134 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2135 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2136 LISTBOX_Timer
= LB_TIMER_NONE
;
2137 if (descr
->captured
)
2139 descr
->captured
= FALSE
;
2140 if (GetCapture() == hwnd
) ReleaseCapture();
2141 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2142 SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2148 /***********************************************************************
2149 * LISTBOX_HandleTimer
2151 * Handle scrolling upon a timer event.
2152 * Return TRUE if scrolling should continue.
2154 static LRESULT
LISTBOX_HandleTimer( HWND hwnd
, LB_DESCR
*descr
,
2155 INT index
, TIMER_DIRECTION dir
)
2160 if (descr
->top_item
) index
= descr
->top_item
- 1;
2164 if (descr
->top_item
) index
-= descr
->page_size
;
2167 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2168 if (index
== descr
->focus_item
) index
++;
2169 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2171 case LB_TIMER_RIGHT
:
2172 if (index
+ descr
->page_size
< descr
->nb_items
)
2173 index
+= descr
->page_size
;
2178 if (index
== descr
->focus_item
) return FALSE
;
2179 LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
2184 /***********************************************************************
2185 * LISTBOX_HandleSystemTimer
2187 * WM_SYSTIMER handler.
2189 static LRESULT
LISTBOX_HandleSystemTimer( HWND hwnd
, LB_DESCR
*descr
)
2191 if (!LISTBOX_HandleTimer( hwnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
2193 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2194 LISTBOX_Timer
= LB_TIMER_NONE
;
2200 /***********************************************************************
2201 * LISTBOX_HandleMouseMove
2203 * WM_MOUSEMOVE handler.
2205 static void LISTBOX_HandleMouseMove( HWND hwnd
, LB_DESCR
*descr
,
2209 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2211 if (!descr
->captured
) return;
2213 if (descr
->style
& LBS_MULTICOLUMN
)
2216 else if (y
>= descr
->item_height
* descr
->page_size
)
2217 y
= descr
->item_height
* descr
->page_size
- 1;
2221 dir
= LB_TIMER_LEFT
;
2224 else if (x
>= descr
->width
)
2226 dir
= LB_TIMER_RIGHT
;
2227 x
= descr
->width
- 1;
2232 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2233 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2236 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2237 if (index
== -1) index
= descr
->focus_item
;
2238 if (!LISTBOX_HandleTimer( hwnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2240 /* Start/stop the system timer */
2242 if (dir
!= LB_TIMER_NONE
)
2243 SetSystemTimer( hwnd
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2244 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2245 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2246 LISTBOX_Timer
= dir
;
2250 /***********************************************************************
2251 * LISTBOX_HandleKeyDown
2253 static LRESULT
LISTBOX_HandleKeyDown( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
2256 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2257 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2258 bForceSelection
= FALSE
; /* only for single select list */
2260 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2262 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2263 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2265 if (caret
== -2) return 0;
2267 if (caret
== -1) switch(wParam
)
2270 if (descr
->style
& LBS_MULTICOLUMN
)
2272 bForceSelection
= FALSE
;
2273 if (descr
->focus_item
>= descr
->page_size
)
2274 caret
= descr
->focus_item
- descr
->page_size
;
2279 caret
= descr
->focus_item
- 1;
2280 if (caret
< 0) caret
= 0;
2283 if (descr
->style
& LBS_MULTICOLUMN
)
2285 bForceSelection
= FALSE
;
2286 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2287 caret
= descr
->focus_item
+ descr
->page_size
;
2292 caret
= descr
->focus_item
+ 1;
2293 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2297 if (descr
->style
& LBS_MULTICOLUMN
)
2299 INT page
= descr
->width
/ descr
->column_width
;
2300 if (page
< 1) page
= 1;
2301 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2303 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2304 if (caret
< 0) caret
= 0;
2307 if (descr
->style
& LBS_MULTICOLUMN
)
2309 INT page
= descr
->width
/ descr
->column_width
;
2310 if (page
< 1) page
= 1;
2311 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2313 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2314 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2320 caret
= descr
->nb_items
- 1;
2323 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2324 else if (descr
->style
& LBS_MULTIPLESEL
)
2326 LISTBOX_SetSelection( hwnd
, descr
, descr
->focus_item
,
2327 !descr
->items
[descr
->focus_item
].selected
,
2328 (descr
->style
& LBS_NOTIFY
) != 0 );
2332 bForceSelection
= FALSE
;
2334 if (bForceSelection
) /* focused item is used instead of key */
2335 caret
= descr
->focus_item
;
2338 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
2339 !(GetKeyState( VK_SHIFT
) & 0x8000))
2340 descr
->anchor_item
= caret
;
2341 LISTBOX_MoveCaret( hwnd
, descr
, caret
, TRUE
);
2342 LISTBOX_SetSelection( hwnd
, descr
, caret
, TRUE
, FALSE
);
2343 if (descr
->style
& LBS_NOTIFY
)
2347 /* make sure that combo parent doesn't hide us */
2348 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2350 if (descr
->nb_items
) SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2357 /***********************************************************************
2358 * LISTBOX_HandleChar
2360 static LRESULT
LISTBOX_HandleChar( HWND hwnd
, LB_DESCR
*descr
, WCHAR charW
)
2368 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2370 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2371 MAKEWPARAM(charW
, descr
->focus_item
),
2373 if (caret
== -2) return 0;
2376 caret
= LISTBOX_FindString( hwnd
, descr
, descr
->focus_item
, str
, FALSE
);
2379 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2380 LISTBOX_SetSelection( hwnd
, descr
, caret
, TRUE
, FALSE
);
2381 LISTBOX_MoveCaret( hwnd
, descr
, caret
, TRUE
);
2382 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2383 SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2389 /***********************************************************************
2392 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2395 MEASUREITEMSTRUCT mis
;
2398 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2401 GetClientRect( hwnd
, &rect
);
2402 descr
->owner
= GetParent( hwnd
);
2403 descr
->style
= GetWindowLongA( hwnd
, GWL_STYLE
);
2404 descr
->width
= rect
.right
- rect
.left
;
2405 descr
->height
= rect
.bottom
- rect
.top
;
2406 descr
->items
= NULL
;
2407 descr
->nb_items
= 0;
2408 descr
->top_item
= 0;
2409 descr
->selected_item
= -1;
2410 descr
->focus_item
= 0;
2411 descr
->anchor_item
= -1;
2412 descr
->item_height
= 1;
2413 descr
->page_size
= 1;
2414 descr
->column_width
= 150;
2415 descr
->horz_extent
= (descr
->style
& WS_HSCROLL
) ? 1 : 0;
2416 descr
->horz_pos
= 0;
2419 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2420 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2421 descr
->in_focus
= FALSE
;
2422 descr
->captured
= FALSE
;
2424 descr
->locale
= 0; /* FIXME */
2427 if (is_old_app(hwnd
) && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2429 /* Win95 document "List Box Differences" from MSDN:
2430 If a list box in a version 3.x application has either the
2431 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2432 horizontal and vertical scroll bars.
2434 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2439 TRACE_(combo
)("[%p]: resetting owner %p -> %p\n", hwnd
, descr
->owner
, lphc
->self
);
2440 descr
->owner
= lphc
->self
;
2443 SetWindowLongA( hwnd
, 0, (LONG
)descr
);
2445 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2447 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2448 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2449 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2450 descr
->item_height
= LISTBOX_SetFont( hwnd
, descr
, 0 );
2452 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2454 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2456 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2457 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2461 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
2462 mis
.CtlType
= ODT_LISTBOX
;
2467 mis
.itemHeight
= descr
->item_height
;
2468 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2469 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2473 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2478 /***********************************************************************
2481 static BOOL
LISTBOX_Destroy( HWND hwnd
, LB_DESCR
*descr
)
2483 LISTBOX_ResetContent( hwnd
, descr
);
2484 SetWindowLongA( hwnd
, 0, 0 );
2485 HeapFree( GetProcessHeap(), 0, descr
);
2490 /***********************************************************************
2491 * ListBoxWndProc_common
2493 static LRESULT WINAPI
ListBoxWndProc_common( HWND hwnd
, UINT msg
,
2494 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2499 if (!(descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 )))
2501 if (msg
== WM_CREATE
)
2503 if (!LISTBOX_Create( hwnd
, NULL
))
2505 TRACE("creating wnd=%p descr=%lx\n", hwnd
, GetWindowLongA( hwnd
, 0 ) );
2508 /* Ignore all other messages before we get a WM_CREATE */
2509 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2510 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2513 TRACE("[%p]: msg %s wp %08x lp %08lx\n",
2514 hwnd
, SPY_GetMsgName(msg
, hwnd
), wParam
, lParam
);
2517 case LB_RESETCONTENT16
:
2518 case LB_RESETCONTENT
:
2519 LISTBOX_ResetContent( hwnd
, descr
);
2520 LISTBOX_UpdateScroll( hwnd
, descr
);
2521 InvalidateRect( hwnd
, NULL
, TRUE
);
2524 case LB_ADDSTRING16
:
2525 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2531 if(unicode
|| !HAS_STRINGS(descr
))
2532 textW
= (LPWSTR
)lParam
;
2535 LPSTR textA
= (LPSTR
)lParam
;
2536 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2537 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2538 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2540 wParam
= LISTBOX_FindStringPos( hwnd
, descr
, textW
, FALSE
);
2541 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2542 if (!unicode
&& HAS_STRINGS(descr
))
2543 HeapFree(GetProcessHeap(), 0, textW
);
2547 case LB_INSERTSTRING16
:
2548 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2549 wParam
= (INT
)(INT16
)wParam
;
2551 case LB_INSERTSTRING
:
2555 if(unicode
|| !HAS_STRINGS(descr
))
2556 textW
= (LPWSTR
)lParam
;
2559 LPSTR textA
= (LPSTR
)lParam
;
2560 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2561 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2562 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2564 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2565 if(!unicode
&& HAS_STRINGS(descr
))
2566 HeapFree(GetProcessHeap(), 0, textW
);
2571 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2577 if(unicode
|| !HAS_STRINGS(descr
))
2578 textW
= (LPWSTR
)lParam
;
2581 LPSTR textA
= (LPSTR
)lParam
;
2582 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2583 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2584 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2586 wParam
= LISTBOX_FindFileStrPos( hwnd
, descr
, textW
);
2587 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2588 if(!unicode
&& HAS_STRINGS(descr
))
2589 HeapFree(GetProcessHeap(), 0, textW
);
2593 case LB_DELETESTRING16
:
2594 case LB_DELETESTRING
:
2595 if (LISTBOX_RemoveItem( hwnd
, descr
, wParam
) != LB_ERR
)
2596 return descr
->nb_items
;
2600 case LB_GETITEMDATA16
:
2601 case LB_GETITEMDATA
:
2602 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2604 return descr
->items
[wParam
].data
;
2606 case LB_SETITEMDATA16
:
2607 case LB_SETITEMDATA
:
2608 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2610 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2615 return descr
->nb_items
;
2618 lParam
= (LPARAM
)MapSL(lParam
);
2621 return LISTBOX_GetText( descr
, wParam
, lParam
, unicode
);
2623 case LB_GETTEXTLEN16
:
2626 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2628 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2629 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2630 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2631 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2633 case LB_GETCURSEL16
:
2635 if (descr
->nb_items
==0)
2637 if (!IS_MULTISELECT(descr
))
2638 return descr
->selected_item
;
2640 if (descr
->selected_item
!=-1)
2641 return descr
->selected_item
;
2643 return descr
->focus_item
;
2644 /* otherwise, if the user tries to move the selection with the */
2645 /* arrow keys, we will give the application something to choke on */
2646 case LB_GETTOPINDEX16
:
2647 case LB_GETTOPINDEX
:
2648 return descr
->top_item
;
2650 case LB_GETITEMHEIGHT16
:
2651 case LB_GETITEMHEIGHT
:
2652 return LISTBOX_GetItemHeight( descr
, wParam
);
2654 case LB_SETITEMHEIGHT16
:
2655 lParam
= LOWORD(lParam
);
2657 case LB_SETITEMHEIGHT
:
2658 return LISTBOX_SetItemHeight( hwnd
, descr
, wParam
, lParam
, TRUE
);
2660 case LB_ITEMFROMPOINT
:
2665 pt
.x
= LOWORD(lParam
);
2666 pt
.y
= HIWORD(lParam
);
2669 rect
.right
= descr
->width
;
2670 rect
.bottom
= descr
->height
;
2672 return MAKELONG( LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
),
2673 !PtInRect( &rect
, pt
) );
2676 case LB_SETCARETINDEX16
:
2677 case LB_SETCARETINDEX
:
2678 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2679 if (LISTBOX_SetCaretIndex( hwnd
, descr
, wParam
, !lParam
) == LB_ERR
)
2686 case LB_GETCARETINDEX16
:
2687 case LB_GETCARETINDEX
:
2688 return descr
->focus_item
;
2690 case LB_SETTOPINDEX16
:
2691 case LB_SETTOPINDEX
:
2692 return LISTBOX_SetTopItem( hwnd
, descr
, wParam
, TRUE
);
2694 case LB_SETCOLUMNWIDTH16
:
2695 case LB_SETCOLUMNWIDTH
:
2696 return LISTBOX_SetColumnWidth( hwnd
, descr
, wParam
);
2698 case LB_GETITEMRECT16
:
2701 ret
= LISTBOX_GetItemRect( descr
, (INT16
)wParam
, &rect
);
2702 CONV_RECT32TO16( &rect
, MapSL(lParam
) );
2706 case LB_GETITEMRECT
:
2707 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2709 case LB_FINDSTRING16
:
2710 wParam
= (INT
)(INT16
)wParam
;
2711 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2717 if(unicode
|| !HAS_STRINGS(descr
))
2718 textW
= (LPWSTR
)lParam
;
2721 LPSTR textA
= (LPSTR
)lParam
;
2722 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2723 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2724 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2726 ret
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, FALSE
);
2727 if(!unicode
&& HAS_STRINGS(descr
))
2728 HeapFree(GetProcessHeap(), 0, textW
);
2732 case LB_FINDSTRINGEXACT16
:
2733 wParam
= (INT
)(INT16
)wParam
;
2734 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2736 case LB_FINDSTRINGEXACT
:
2740 if(unicode
|| !HAS_STRINGS(descr
))
2741 textW
= (LPWSTR
)lParam
;
2744 LPSTR textA
= (LPSTR
)lParam
;
2745 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2746 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2747 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2749 ret
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, TRUE
);
2750 if(!unicode
&& HAS_STRINGS(descr
))
2751 HeapFree(GetProcessHeap(), 0, textW
);
2755 case LB_SELECTSTRING16
:
2756 wParam
= (INT
)(INT16
)wParam
;
2757 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2759 case LB_SELECTSTRING
:
2764 if(HAS_STRINGS(descr
))
2765 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2766 debugstr_a((LPSTR
)lParam
));
2767 if(unicode
|| !HAS_STRINGS(descr
))
2768 textW
= (LPWSTR
)lParam
;
2771 LPSTR textA
= (LPSTR
)lParam
;
2772 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2773 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2774 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2776 index
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, FALSE
);
2777 if(!unicode
&& HAS_STRINGS(descr
))
2778 HeapFree(GetProcessHeap(), 0, textW
);
2779 if (index
!= LB_ERR
)
2781 LISTBOX_SetCaretIndex( hwnd
, descr
, index
, TRUE
);
2782 LISTBOX_SetSelection( hwnd
, descr
, index
, TRUE
, FALSE
);
2788 wParam
= (INT
)(INT16
)wParam
;
2791 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2793 return descr
->items
[wParam
].selected
;
2796 lParam
= (INT
)(INT16
)lParam
;
2799 return LISTBOX_SetSelection( hwnd
, descr
, lParam
, wParam
, FALSE
);
2801 case LB_SETCURSEL16
:
2802 wParam
= (INT
)(INT16
)wParam
;
2805 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2806 LISTBOX_SetCaretIndex( hwnd
, descr
, wParam
, TRUE
);
2807 return LISTBOX_SetSelection( hwnd
, descr
, wParam
, TRUE
, FALSE
);
2809 case LB_GETSELCOUNT16
:
2810 case LB_GETSELCOUNT
:
2811 return LISTBOX_GetSelCount( descr
);
2813 case LB_GETSELITEMS16
:
2814 return LISTBOX_GetSelItems16( descr
, wParam
, (LPINT16
)MapSL(lParam
) );
2816 case LB_GETSELITEMS
:
2817 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2819 case LB_SELITEMRANGE16
:
2820 case LB_SELITEMRANGE
:
2821 if (LOWORD(lParam
) <= HIWORD(lParam
))
2822 return LISTBOX_SelectItemRange( hwnd
, descr
, LOWORD(lParam
),
2823 HIWORD(lParam
), wParam
);
2825 return LISTBOX_SelectItemRange( hwnd
, descr
, HIWORD(lParam
),
2826 LOWORD(lParam
), wParam
);
2828 case LB_SELITEMRANGEEX16
:
2829 case LB_SELITEMRANGEEX
:
2830 if ((INT
)lParam
>= (INT
)wParam
)
2831 return LISTBOX_SelectItemRange( hwnd
, descr
, wParam
, lParam
, TRUE
);
2833 return LISTBOX_SelectItemRange( hwnd
, descr
, lParam
, wParam
, FALSE
);
2835 case LB_GETHORIZONTALEXTENT16
:
2836 case LB_GETHORIZONTALEXTENT
:
2837 return descr
->horz_extent
;
2839 case LB_SETHORIZONTALEXTENT16
:
2840 case LB_SETHORIZONTALEXTENT
:
2841 return LISTBOX_SetHorizontalExtent( hwnd
, descr
, wParam
);
2843 case LB_GETANCHORINDEX16
:
2844 case LB_GETANCHORINDEX
:
2845 return descr
->anchor_item
;
2847 case LB_SETANCHORINDEX16
:
2848 wParam
= (INT
)(INT16
)wParam
;
2850 case LB_SETANCHORINDEX
:
2851 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2853 descr
->anchor_item
= (INT
)wParam
;
2857 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2858 * be set automatically (this is different in Win32) */
2859 if (wParam
& DDL_DRIVES
) wParam
|= DDL_EXCLUSIVE
;
2860 lParam
= (LPARAM
)MapSL(lParam
);
2867 textW
= (LPWSTR
)lParam
;
2870 LPSTR textA
= (LPSTR
)lParam
;
2871 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2872 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2873 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2875 ret
= LISTBOX_Directory( hwnd
, descr
, wParam
, textW
, msg
== LB_DIR
);
2877 HeapFree(GetProcessHeap(), 0, textW
);
2882 return descr
->locale
;
2885 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2888 case LB_INITSTORAGE
:
2889 return LISTBOX_InitStorage( hwnd
, descr
, wParam
);
2892 return LISTBOX_SetCount( hwnd
, descr
, (INT
)wParam
);
2894 case LB_SETTABSTOPS16
:
2895 return LISTBOX_SetTabStops( hwnd
, descr
, (INT
)(INT16
)wParam
, MapSL(lParam
), TRUE
);
2897 case LB_SETTABSTOPS
:
2898 return LISTBOX_SetTabStops( hwnd
, descr
, wParam
, (LPINT
)lParam
, FALSE
);
2902 if (descr
->caret_on
)
2904 descr
->caret_on
= TRUE
;
2905 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2906 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2911 if (!descr
->caret_on
)
2913 descr
->caret_on
= FALSE
;
2914 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2915 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2919 return LISTBOX_Destroy( hwnd
, descr
);
2922 InvalidateRect( hwnd
, NULL
, TRUE
);
2926 LISTBOX_SetRedraw( hwnd
, descr
, wParam
!= 0 );
2930 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2935 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( hwnd
, &ps
);
2936 ret
= LISTBOX_Paint( hwnd
, descr
, hdc
);
2937 if( !wParam
) EndPaint( hwnd
, &ps
);
2941 LISTBOX_UpdateSize( hwnd
, descr
);
2944 return (LRESULT
)descr
->font
;
2946 LISTBOX_SetFont( hwnd
, descr
, (HFONT
)wParam
);
2947 if (lParam
) InvalidateRect( hwnd
, 0, TRUE
);
2950 descr
->in_focus
= TRUE
;
2951 descr
->caret_on
= TRUE
;
2952 if (descr
->focus_item
!= -1)
2953 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2954 SEND_NOTIFICATION( hwnd
, descr
, LBN_SETFOCUS
);
2957 descr
->in_focus
= FALSE
;
2958 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2959 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2960 SEND_NOTIFICATION( hwnd
, descr
, LBN_KILLFOCUS
);
2963 return LISTBOX_HandleHScroll( hwnd
, descr
, wParam
);
2965 return LISTBOX_HandleVScroll( hwnd
, descr
, wParam
);
2967 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2968 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
2969 return LISTBOX_HandleMouseWheel( hwnd
, descr
, wParam
);
2970 case WM_LBUTTONDOWN
:
2971 return LISTBOX_HandleLButtonDown( hwnd
, descr
, wParam
,
2972 (INT16
)LOWORD(lParam
),
2973 (INT16
)HIWORD(lParam
) );
2974 case WM_LBUTTONDBLCLK
:
2975 if (descr
->style
& LBS_NOTIFY
)
2976 SEND_NOTIFICATION( hwnd
, descr
, LBN_DBLCLK
);
2979 if (GetCapture() == hwnd
)
2980 LISTBOX_HandleMouseMove( hwnd
, descr
, (INT16
)LOWORD(lParam
),
2981 (INT16
)HIWORD(lParam
) );
2984 return LISTBOX_HandleLButtonUp( hwnd
, descr
);
2986 return LISTBOX_HandleKeyDown( hwnd
, descr
, wParam
);
2991 charW
= (WCHAR
)wParam
;
2994 CHAR charA
= (CHAR
)wParam
;
2995 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
2997 return LISTBOX_HandleChar( hwnd
, descr
, charW
);
3000 return LISTBOX_HandleSystemTimer( hwnd
, descr
);
3002 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3005 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3006 wParam
, (LPARAM
)hwnd
);
3007 TRACE("hbrush = %p\n", hbrush
);
3009 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3012 GetClientRect(hwnd
, &rect
);
3013 FillRect((HDC
)wParam
, &rect
, hbrush
);
3019 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3020 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3024 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3025 WARN("[%p]: unknown msg %04x wp %08x lp %08lx\n",
3026 hwnd
, msg
, wParam
, lParam
);
3027 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3028 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3033 /***********************************************************************
3036 * This is just a wrapper for the real wndproc, it only does window locking
3039 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3041 if (!IsWindow(hwnd
)) return 0;
3042 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3045 /***********************************************************************
3048 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3050 if (!IsWindow(hwnd
)) return 0;
3051 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);
3054 /***********************************************************************
3055 * ComboLBWndProc_common
3057 * The real combo listbox wndproc
3059 static LRESULT WINAPI
ComboLBWndProc_common( HWND hwnd
, UINT msg
,
3060 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
3066 if (!(descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 )))
3068 if (msg
== WM_CREATE
)
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
);
3075 /* Ignore all other messages before we get a WM_CREATE */
3076 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3077 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3080 TRACE_(combo
)("[%p]: msg %s wp %08x lp %08lx\n",
3081 hwnd
, SPY_GetMsgName(msg
, hwnd
), wParam
, lParam
);
3083 if ((lphc
= descr
->lphc
) != NULL
)
3088 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
3089 (CB_GETTYPE(lphc
) != CBS_SIMPLE
) )
3095 mousePos
.x
= (INT16
)LOWORD(lParam
);
3096 mousePos
.y
= (INT16
)HIWORD(lParam
);
3099 * If we are in a dropdown combobox, we simulate that
3100 * the mouse is captured to show the tracking of the item.
3102 GetClientRect(hwnd
, &clientRect
);
3104 if (PtInRect( &clientRect
, mousePos
))
3106 captured
= descr
->captured
;
3107 descr
->captured
= TRUE
;
3109 LISTBOX_HandleMouseMove( hwnd
, descr
,
3110 mousePos
.x
, mousePos
.y
);
3112 descr
->captured
= captured
;
3117 LISTBOX_HandleMouseMove( hwnd
, descr
,
3118 mousePos
.x
, mousePos
.y
);
3124 /* else we are in Win3.1 look, go with the default behavior. */
3128 if (TWEAK_WineLook
> WIN31_LOOK
)
3134 * If the mouse button "up" is not in the listbox,
3135 * we make sure there is no selection by re-selecting the
3136 * item that was selected when the listbox was made visible.
3138 mousePos
.x
= (INT16
)LOWORD(lParam
);
3139 mousePos
.y
= (INT16
)HIWORD(lParam
);
3141 GetClientRect(hwnd
, &clientRect
);
3144 * When the user clicks outside the combobox and the focus
3145 * is lost, the owning combobox will send a fake buttonup with
3146 * 0xFFFFFFF as the mouse location, we must also revert the
3147 * selection to the original selection.
3149 if ( (lParam
== (LPARAM
)-1) ||
3150 (!PtInRect( &clientRect
, mousePos
)) )
3152 LISTBOX_MoveCaret( hwnd
, descr
, lphc
->droppedIndex
, FALSE
);
3155 return LISTBOX_HandleLButtonUp( hwnd
, descr
);
3156 case WM_LBUTTONDBLCLK
:
3157 case WM_LBUTTONDOWN
:
3158 return LISTBOX_HandleLButtonDownCombo(hwnd
, descr
, msg
, wParam
,
3159 (INT16
)LOWORD(lParam
),
3160 (INT16
)HIWORD(lParam
) );
3164 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3166 /* for some reason(?) Windows makes it possible to
3167 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3169 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3170 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3171 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3173 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3177 return LISTBOX_HandleKeyDown( hwnd
, descr
, wParam
);
3179 case LB_SETCURSEL16
:
3181 lRet
= unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3182 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3183 lRet
=(lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
3186 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3192 /* default handling: call listbox wnd proc */
3193 lRet
= unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3194 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3196 TRACE_(combo
)("\t default on msg [%04x]\n", (UINT16
)msg
);
3201 /***********************************************************************
3204 * NOTE: in Windows, winproc address of the ComboLBox is the same
3205 * as that of the Listbox.
3207 LRESULT WINAPI
ComboLBWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3209 if (!IsWindow(hwnd
)) return 0;
3210 return ComboLBWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3213 /***********************************************************************
3216 LRESULT WINAPI
ComboLBWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3218 if (!IsWindow(hwnd
)) return 0;
3219 return ComboLBWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);