4 * Copyright 1996 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "wine/winuser16.h"
27 #include "wine/winbase16.h"
29 #include "wine/unicode.h"
35 #include "wine/debug.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(listbox
);
39 WINE_DECLARE_DEBUG_CHANNEL(combo
);
45 * Probably needs improvement:
49 /* Items array granularity */
50 #define LB_ARRAY_GRANULARITY 16
52 /* Scrolling timeout in ms */
53 #define LB_SCROLL_TIMEOUT 50
55 /* Listbox system timer id */
58 /* flag listbox changed while setredraw false - internal style */
59 #define LBS_DISPLAYCHANGED 0x80000000
64 LPWSTR str
; /* Item text */
65 BOOL selected
; /* Is item selected? */
66 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
67 DWORD data
; /* User data */
70 /* Listbox structure */
73 HWND owner
; /* Owner window to send notifications to */
74 UINT style
; /* Window style */
75 INT width
; /* Window width */
76 INT height
; /* Window height */
77 LB_ITEMDATA
*items
; /* Array of items */
78 INT nb_items
; /* Number of items */
79 INT top_item
; /* Top visible item */
80 INT selected_item
; /* Selected item */
81 INT focus_item
; /* Item that has the focus */
82 INT anchor_item
; /* Anchor item for extended selection */
83 INT item_height
; /* Default item height */
84 INT page_size
; /* Items per listbox page */
85 INT column_width
; /* Column width for multi-column listboxes */
86 INT horz_extent
; /* Horizontal extent (0 if no hscroll) */
87 INT horz_pos
; /* Horizontal position */
88 INT nb_tabs
; /* Number of tabs in array */
89 INT
*tabs
; /* Array of tabs */
90 BOOL caret_on
; /* Is caret on? */
91 BOOL captured
; /* Is mouse captured? */
93 HFONT font
; /* Current font */
94 LCID locale
; /* Current locale for string comparisons */
95 LPHEADCOMBO lphc
; /* ComboLBox */
99 #define IS_OWNERDRAW(descr) \
100 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
102 #define HAS_STRINGS(descr) \
103 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
106 #define IS_MULTISELECT(descr) \
107 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
109 #define SEND_NOTIFICATION(hwnd,descr,code) \
110 (SendMessageW( (descr)->owner, WM_COMMAND, \
111 MAKEWPARAM( GetWindowLongA((hwnd),GWL_ID), (code)), (LPARAM)(hwnd) ))
113 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
115 /* Current timer status */
125 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
127 static LRESULT WINAPI
ComboLBWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
128 static LRESULT WINAPI
ComboLBWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
129 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
130 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
132 static LRESULT
LISTBOX_GetItemRect( LB_DESCR
*descr
, INT index
, RECT
*rect
);
134 /*********************************************************************
135 * listbox class descriptor
137 const struct builtin_class_descr LISTBOX_builtin_class
=
139 "ListBox", /* name */
140 CS_GLOBALCLASS
| CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
141 ListBoxWndProcA
, /* procA */
142 ListBoxWndProcW
, /* procW */
143 sizeof(LB_DESCR
*), /* extra */
144 IDC_ARROWA
, /* cursor */
149 /*********************************************************************
150 * combolbox class descriptor
152 const struct builtin_class_descr COMBOLBOX_builtin_class
=
154 "ComboLBox", /* name */
155 CS_GLOBALCLASS
| CS_DBLCLKS
| CS_SAVEBITS
, /* style */
156 ComboLBWndProcA
, /* procA */
157 ComboLBWndProcW
, /* procW */
158 sizeof(LB_DESCR
*), /* extra */
159 IDC_ARROWA
, /* cursor */
164 /* check whether app is a Win 3.1 app */
165 inline static BOOL
is_old_app( HWND hwnd
)
167 return (GetExpWinVer16( GetWindowLongA(hwnd
,GWL_HINSTANCE
) ) & 0xFF00 ) == 0x0300;
171 /***********************************************************************
174 void LISTBOX_Dump( HWND hwnd
)
178 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 );
180 TRACE( "Listbox:\n" );
181 TRACE( "hwnd=%p descr=%08x items=%d top=%d\n",
182 hwnd
, (UINT
)descr
, descr
->nb_items
, descr
->top_item
);
183 for (i
= 0, item
= descr
->items
; i
< descr
->nb_items
; i
++, item
++)
185 TRACE( "%4d: %-40s %d %08lx %3d\n",
186 i
, debugstr_w(item
->str
), item
->selected
, item
->data
, item
->height
);
191 /***********************************************************************
192 * LISTBOX_GetCurrentPageSize
194 * Return the current page size
196 static INT
LISTBOX_GetCurrentPageSize( LB_DESCR
*descr
)
199 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
200 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
202 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
204 if (i
== descr
->top_item
) return 1;
205 else return i
- descr
->top_item
;
209 /***********************************************************************
210 * LISTBOX_GetMaxTopIndex
212 * Return the maximum possible index for the top of the listbox.
214 static INT
LISTBOX_GetMaxTopIndex( LB_DESCR
*descr
)
218 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
220 page
= descr
->height
;
221 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
222 if ((page
-= descr
->items
[max
].height
) < 0) break;
223 if (max
< descr
->nb_items
- 1) max
++;
225 else if (descr
->style
& LBS_MULTICOLUMN
)
227 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
228 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
229 max
= (max
- page
) * descr
->page_size
;
233 max
= descr
->nb_items
- descr
->page_size
;
235 if (max
< 0) max
= 0;
240 /***********************************************************************
241 * LISTBOX_UpdateScroll
243 * Update the scrollbars. Should be called whenever the content
244 * of the listbox changes.
246 static void LISTBOX_UpdateScroll( HWND hwnd
, LB_DESCR
*descr
)
250 /* Check the listbox scroll bar flags individually before we call
251 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
252 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
253 scroll bar when we do not need one.
254 if (!(descr->style & WS_VSCROLL)) return;
257 /* It is important that we check descr->style, and not wnd->dwStyle,
258 for WS_VSCROLL, as the former is exactly the one passed in
259 argument to CreateWindow.
260 In Windows (and from now on in Wine :) a listbox created
261 with such a style (no WS_SCROLL) does not update
262 the scrollbar with listbox-related data, thus letting
263 the programmer use it for his/her own purposes. */
265 if (descr
->style
& LBS_NOREDRAW
) return;
266 info
.cbSize
= sizeof(info
);
268 if (descr
->style
& LBS_MULTICOLUMN
)
271 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
272 info
.nPos
= descr
->top_item
/ descr
->page_size
;
273 info
.nPage
= descr
->width
/ descr
->column_width
;
274 if (info
.nPage
< 1) info
.nPage
= 1;
275 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
276 if (descr
->style
& LBS_DISABLENOSCROLL
)
277 info
.fMask
|= SIF_DISABLENOSCROLL
;
278 if (descr
->style
& WS_HSCROLL
)
279 SetScrollInfo( hwnd
, SB_HORZ
, &info
, TRUE
);
281 info
.fMask
= SIF_RANGE
;
282 if (descr
->style
& WS_VSCROLL
)
283 SetScrollInfo( hwnd
, SB_VERT
, &info
, TRUE
);
288 info
.nMax
= descr
->nb_items
- 1;
289 info
.nPos
= descr
->top_item
;
290 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
291 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
292 if (descr
->style
& LBS_DISABLENOSCROLL
)
293 info
.fMask
|= SIF_DISABLENOSCROLL
;
294 if (descr
->style
& WS_VSCROLL
)
295 SetScrollInfo( hwnd
, SB_VERT
, &info
, TRUE
);
297 if (descr
->horz_extent
)
300 info
.nMax
= descr
->horz_extent
- 1;
301 info
.nPos
= descr
->horz_pos
;
302 info
.nPage
= descr
->width
;
303 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
304 if (descr
->style
& LBS_DISABLENOSCROLL
)
305 info
.fMask
|= SIF_DISABLENOSCROLL
;
306 if (descr
->style
& WS_HSCROLL
)
307 SetScrollInfo( hwnd
, SB_HORZ
, &info
, TRUE
);
313 /***********************************************************************
316 * Set the top item of the listbox, scrolling up or down if necessary.
318 static LRESULT
LISTBOX_SetTopItem( HWND hwnd
, LB_DESCR
*descr
, INT index
,
321 INT max
= LISTBOX_GetMaxTopIndex( descr
);
322 if (index
> max
) index
= max
;
323 if (index
< 0) index
= 0;
324 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
325 if (descr
->top_item
== index
) return LB_OKAY
;
326 if (descr
->style
& LBS_MULTICOLUMN
)
328 INT diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
329 if (scroll
&& (abs(diff
) < descr
->width
))
330 ScrollWindowEx( hwnd
, diff
, 0, NULL
, NULL
, 0, NULL
,
331 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
339 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
343 if (index
> descr
->top_item
)
345 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
346 diff
-= descr
->items
[i
].height
;
350 for (i
= index
; i
< descr
->top_item
; i
++)
351 diff
+= descr
->items
[i
].height
;
355 diff
= (descr
->top_item
- index
) * descr
->item_height
;
357 if (abs(diff
) < descr
->height
)
358 ScrollWindowEx( hwnd
, 0, diff
, NULL
, NULL
, 0, NULL
,
359 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
363 if (!scroll
) InvalidateRect( hwnd
, NULL
, TRUE
);
364 descr
->top_item
= index
;
365 LISTBOX_UpdateScroll( hwnd
, descr
);
370 /***********************************************************************
373 * Update the page size. Should be called when the size of
374 * the client area or the item height changes.
376 static void LISTBOX_UpdatePage( HWND hwnd
, LB_DESCR
*descr
)
380 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
382 if (page_size
== descr
->page_size
) return;
383 descr
->page_size
= page_size
;
384 if (descr
->style
& LBS_MULTICOLUMN
)
385 InvalidateRect( hwnd
, NULL
, TRUE
);
386 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
, FALSE
);
390 /***********************************************************************
393 * Update the size of the listbox. Should be called when the size of
394 * the client area changes.
396 static void LISTBOX_UpdateSize( HWND hwnd
, LB_DESCR
*descr
)
400 GetClientRect( hwnd
, &rect
);
401 descr
->width
= rect
.right
- rect
.left
;
402 descr
->height
= rect
.bottom
- rect
.top
;
403 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
408 GetWindowRect( hwnd
, &rect
);
409 if(descr
->item_height
!= 0)
410 remaining
= descr
->height
% descr
->item_height
;
413 if ((descr
->height
> descr
->item_height
) && remaining
)
415 if (is_old_app(hwnd
))
416 { /* give a margin for error to 16 bits programs - if we need
417 less than the height of the nonclient area, round to the
418 *next* number of items */
419 int ncheight
= rect
.bottom
- rect
.top
- descr
->height
;
420 if ((descr
->item_height
- remaining
) <= ncheight
)
421 remaining
= remaining
- descr
->item_height
;
423 TRACE("[%p]: changing height %d -> %d\n",
424 hwnd
, descr
->height
, descr
->height
- remaining
);
425 SetWindowPos( hwnd
, 0, 0, 0, rect
.right
- rect
.left
,
426 rect
.bottom
- rect
.top
- remaining
,
427 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
431 TRACE("[%p]: new size = %d,%d\n", hwnd
, descr
->width
, descr
->height
);
432 LISTBOX_UpdatePage( hwnd
, descr
);
433 LISTBOX_UpdateScroll( hwnd
, descr
);
435 /* Invalidate the focused item so it will be repainted correctly */
436 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
438 InvalidateRect( hwnd
, &rect
, FALSE
);
443 /***********************************************************************
444 * LISTBOX_GetItemRect
446 * Get the rectangle enclosing an item, in listbox client coordinates.
447 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
449 static LRESULT
LISTBOX_GetItemRect( LB_DESCR
*descr
, INT index
, RECT
*rect
)
451 /* Index <= 0 is legal even on empty listboxes */
452 if (index
&& (index
>= descr
->nb_items
)) return -1;
453 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
454 if (descr
->style
& LBS_MULTICOLUMN
)
456 INT col
= (index
/ descr
->page_size
) -
457 (descr
->top_item
/ descr
->page_size
);
458 rect
->left
+= col
* descr
->column_width
;
459 rect
->right
= rect
->left
+ descr
->column_width
;
460 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
461 rect
->bottom
= rect
->top
+ descr
->item_height
;
463 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
466 rect
->right
+= descr
->horz_pos
;
467 if ((index
>= 0) && (index
< descr
->nb_items
))
469 if (index
< descr
->top_item
)
471 for (i
= descr
->top_item
-1; i
>= index
; i
--)
472 rect
->top
-= descr
->items
[i
].height
;
476 for (i
= descr
->top_item
; i
< index
; i
++)
477 rect
->top
+= descr
->items
[i
].height
;
479 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
485 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
486 rect
->bottom
= rect
->top
+ descr
->item_height
;
487 rect
->right
+= descr
->horz_pos
;
490 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
491 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
495 /***********************************************************************
496 * LISTBOX_GetItemFromPoint
498 * Return the item nearest from point (x,y) (in client coordinates).
500 static INT
LISTBOX_GetItemFromPoint( LB_DESCR
*descr
, INT x
, INT y
)
502 INT index
= descr
->top_item
;
504 if (!descr
->nb_items
) return -1; /* No items */
505 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
510 while (index
< descr
->nb_items
)
512 if ((pos
+= descr
->items
[index
].height
) > y
) break;
521 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
525 else if (descr
->style
& LBS_MULTICOLUMN
)
527 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
528 if (y
>= 0) index
+= y
/ descr
->item_height
;
529 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
530 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
534 index
+= (y
/ descr
->item_height
);
536 if (index
< 0) return 0;
537 if (index
>= descr
->nb_items
) return -1;
542 /***********************************************************************
547 static void LISTBOX_PaintItem( HWND hwnd
, LB_DESCR
*descr
, HDC hdc
,
548 const RECT
*rect
, INT index
, UINT action
, BOOL ignoreFocus
)
550 LB_ITEMDATA
*item
= NULL
;
551 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
553 if (IS_OWNERDRAW(descr
))
558 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
562 if (action
== ODA_FOCUS
)
563 DrawFocusRect( hdc
, rect
);
565 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
569 /* some programs mess with the clipping region when
570 drawing the item, *and* restore the previous region
571 after they are done, so a region has better to exist
572 else everything ends clipped */
573 GetClientRect(hwnd
, &r
);
574 hrgn
= CreateRectRgnIndirect(&r
);
575 SelectClipRgn( hdc
, hrgn
);
576 DeleteObject( hrgn
);
578 dis
.CtlType
= ODT_LISTBOX
;
581 dis
.itemAction
= action
;
585 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
586 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
588 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
589 if (!IsWindowEnabled(hwnd
)) dis
.itemState
|= ODS_DISABLED
;
590 dis
.itemData
= item
? item
->data
: 0;
592 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%ld,%ld-%ld,%ld\n",
593 hwnd
, index
, item
? debugstr_w(item
->str
) : "", action
,
594 dis
.itemState
, rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
595 SendMessageW(descr
->owner
, WM_DRAWITEM
, id
, (LPARAM
)&dis
);
599 COLORREF oldText
= 0, oldBk
= 0;
601 if (action
== ODA_FOCUS
)
603 DrawFocusRect( hdc
, rect
);
606 if (item
&& item
->selected
)
608 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
609 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
612 TRACE("[%p]: painting %d (%s) action=%02x rect=%ld,%ld-%ld,%ld\n",
613 hwnd
, index
, item
? debugstr_w(item
->str
) : "", action
,
614 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
616 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
617 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
618 else if (!(descr
->style
& LBS_USETABSTOPS
))
619 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
620 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
621 strlenW(item
->str
), NULL
);
624 /* Output empty string to paint background in the full width. */
625 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
626 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
627 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
628 item
->str
, strlenW(item
->str
),
629 descr
->nb_tabs
, descr
->tabs
, 0);
631 if (item
&& item
->selected
)
633 SetBkColor( hdc
, oldBk
);
634 SetTextColor( hdc
, oldText
);
636 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
638 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
643 /***********************************************************************
646 * Change the redraw flag.
648 static void LISTBOX_SetRedraw( HWND hwnd
, LB_DESCR
*descr
, BOOL on
)
652 if (!(descr
->style
& LBS_NOREDRAW
)) return;
653 descr
->style
&= ~LBS_NOREDRAW
;
654 if (descr
->style
& LBS_DISPLAYCHANGED
)
655 { /* page was changed while setredraw false, refresh automatically */
656 InvalidateRect(hwnd
, NULL
, TRUE
);
657 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
658 { /* reset top of page if less than number of items/page */
659 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
660 if (descr
->top_item
< 0) descr
->top_item
= 0;
662 descr
->style
&= ~LBS_DISPLAYCHANGED
;
664 LISTBOX_UpdateScroll( hwnd
, descr
);
666 else descr
->style
|= LBS_NOREDRAW
;
670 /***********************************************************************
671 * LISTBOX_RepaintItem
673 * Repaint a single item synchronously.
675 static void LISTBOX_RepaintItem( HWND hwnd
, LB_DESCR
*descr
, INT index
,
681 HBRUSH hbrush
, oldBrush
= 0;
683 /* Do not repaint the item if the item is not visible */
684 if (!IsWindowVisible(hwnd
)) return;
685 if (descr
->style
& LBS_NOREDRAW
)
687 descr
->style
|= LBS_DISPLAYCHANGED
;
690 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
691 if (!(hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
))) return;
692 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
693 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
694 (WPARAM
)hdc
, (LPARAM
)hwnd
);
695 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
696 if (!IsWindowEnabled(hwnd
))
697 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
698 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
699 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, index
, action
, FALSE
);
700 if (oldFont
) SelectObject( hdc
, oldFont
);
701 if (oldBrush
) SelectObject( hdc
, oldBrush
);
702 ReleaseDC( hwnd
, hdc
);
706 /***********************************************************************
707 * LISTBOX_InitStorage
709 static LRESULT
LISTBOX_InitStorage( HWND hwnd
, LB_DESCR
*descr
, INT nb_items
)
713 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
714 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
716 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
717 if (!(item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
718 nb_items
* sizeof(LB_ITEMDATA
) )))
720 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
728 /***********************************************************************
729 * LISTBOX_SetTabStops
731 static BOOL
LISTBOX_SetTabStops( HWND hwnd
, LB_DESCR
*descr
, INT count
,
732 LPINT tabs
, BOOL short_ints
)
734 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
735 if (descr
->tabs
) HeapFree( GetProcessHeap(), 0, descr
->tabs
);
736 if (!(descr
->nb_tabs
= count
))
741 /* FIXME: count = 1 */
742 if (!(descr
->tabs
= (INT
*)HeapAlloc( GetProcessHeap(), 0,
743 descr
->nb_tabs
* sizeof(INT
) )))
748 LPINT16 p
= (LPINT16
)tabs
;
750 TRACE("[%p]: settabstops ", hwnd
);
751 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
752 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
753 if (TRACE_ON(listbox
)) TRACE("%hd ", descr
->tabs
[i
]);
755 if (TRACE_ON(listbox
)) TRACE("\n");
757 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
758 /* FIXME: repaint the window? */
763 /***********************************************************************
766 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPARAM lParam
, BOOL unicode
)
768 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
769 if (HAS_STRINGS(descr
))
772 return strlenW(descr
->items
[index
].str
);
774 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
778 LPWSTR buffer
= (LPWSTR
)lParam
;
779 strcpyW( buffer
, descr
->items
[index
].str
);
780 return strlenW(buffer
);
784 LPSTR buffer
= (LPSTR
)lParam
;
785 return WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1, buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
789 *((LPDWORD
)lParam
)=*(LPDWORD
)(&descr
->items
[index
].data
);
790 return sizeof(DWORD
);
795 /***********************************************************************
796 * LISTBOX_FindStringPos
798 * Find the nearest string located before a given string in sort order.
799 * If 'exact' is TRUE, return an error if we don't get an exact match.
801 static INT
LISTBOX_FindStringPos( HWND hwnd
, LB_DESCR
*descr
, LPCWSTR str
,
804 INT index
, min
, max
, res
= -1;
806 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
808 max
= descr
->nb_items
;
811 index
= (min
+ max
) / 2;
812 if (HAS_STRINGS(descr
))
813 res
= lstrcmpiW( str
, descr
->items
[index
].str
);
816 COMPAREITEMSTRUCT cis
;
817 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
819 cis
.CtlType
= ODT_LISTBOX
;
822 /* note that some application (MetaStock) expects the second item
823 * to be in the listbox */
825 cis
.itemData1
= (DWORD
)str
;
827 cis
.itemData2
= descr
->items
[index
].data
;
828 cis
.dwLocaleId
= descr
->locale
;
829 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
831 if (!res
) return index
;
832 if (res
< 0) max
= index
;
833 else min
= index
+ 1;
835 return exact
? -1 : max
;
839 /***********************************************************************
840 * LISTBOX_FindFileStrPos
842 * Find the nearest string located before a given string in directory
843 * sort order (i.e. first files, then directories, then drives).
845 static INT
LISTBOX_FindFileStrPos( HWND hwnd
, LB_DESCR
*descr
, LPCWSTR str
)
847 INT min
, max
, res
= -1;
849 if (!HAS_STRINGS(descr
))
850 return LISTBOX_FindStringPos( hwnd
, descr
, str
, FALSE
);
852 max
= descr
->nb_items
;
855 INT index
= (min
+ max
) / 2;
856 LPCWSTR p
= descr
->items
[index
].str
;
857 if (*p
== '[') /* drive or directory */
859 if (*str
!= '[') res
= -1;
860 else if (p
[1] == '-') /* drive */
862 if (str
[1] == '-') res
= str
[2] - p
[2];
867 if (str
[1] == '-') res
= 1;
868 else res
= lstrcmpiW( str
, p
);
873 if (*str
== '[') res
= 1;
874 else res
= lstrcmpiW( str
, p
);
876 if (!res
) return index
;
877 if (res
< 0) max
= index
;
878 else min
= index
+ 1;
884 /***********************************************************************
887 * Find the item beginning with a given string.
889 static INT
LISTBOX_FindString( HWND hwnd
, LB_DESCR
*descr
, INT start
,
890 LPCWSTR str
, BOOL exact
)
895 if (start
>= descr
->nb_items
) start
= -1;
896 item
= descr
->items
+ start
+ 1;
897 if (HAS_STRINGS(descr
))
899 if (!str
|| ! str
[0] ) return LB_ERR
;
902 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
903 if (!lstrcmpiW( str
, item
->str
)) return i
;
904 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
905 if (!lstrcmpiW( str
, item
->str
)) return i
;
909 /* Special case for drives and directories: ignore prefix */
910 #define CHECK_DRIVE(item) \
911 if ((item)->str[0] == '[') \
913 if (!strncmpiW( str, (item)->str+1, len )) return i; \
914 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
918 INT len
= strlenW(str
);
919 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
921 if (!strncmpiW( str
, item
->str
, len
)) return i
;
924 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
926 if (!strncmpiW( str
, item
->str
, len
)) return i
;
934 if (exact
&& (descr
->style
& LBS_SORT
))
935 /* If sorted, use a WM_COMPAREITEM binary search */
936 return LISTBOX_FindStringPos( hwnd
, descr
, str
, TRUE
);
938 /* Otherwise use a linear search */
939 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
940 if (item
->data
== (DWORD
)str
) return i
;
941 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
942 if (item
->data
== (DWORD
)str
) return i
;
948 /***********************************************************************
949 * LISTBOX_GetSelCount
951 static LRESULT
LISTBOX_GetSelCount( LB_DESCR
*descr
)
954 LB_ITEMDATA
*item
= descr
->items
;
956 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
957 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
958 if (item
->selected
) count
++;
963 /***********************************************************************
964 * LISTBOX_GetSelItems16
966 static LRESULT
LISTBOX_GetSelItems16( LB_DESCR
*descr
, INT16 max
, LPINT16 array
)
969 LB_ITEMDATA
*item
= descr
->items
;
971 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
972 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
973 if (item
->selected
) array
[count
++] = (INT16
)i
;
978 /***********************************************************************
979 * LISTBOX_GetSelItems
981 static LRESULT
LISTBOX_GetSelItems( LB_DESCR
*descr
, INT max
, LPINT array
)
984 LB_ITEMDATA
*item
= descr
->items
;
986 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
987 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
988 if (item
->selected
) array
[count
++] = i
;
993 /***********************************************************************
996 static LRESULT
LISTBOX_Paint( HWND hwnd
, LB_DESCR
*descr
, HDC hdc
)
998 INT i
, col_pos
= descr
->page_size
- 1;
1000 RECT focusRect
= {-1, -1, -1, -1};
1002 HBRUSH hbrush
, oldBrush
= 0;
1004 if (descr
->style
& LBS_NOREDRAW
) return 0;
1006 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1007 if (descr
->style
& LBS_MULTICOLUMN
)
1008 rect
.right
= rect
.left
+ descr
->column_width
;
1009 else if (descr
->horz_pos
)
1011 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1012 rect
.right
+= descr
->horz_pos
;
1015 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1016 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
1017 (WPARAM
)hdc
, (LPARAM
)hwnd
);
1018 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1019 if (!IsWindowEnabled(hwnd
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1021 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1024 /* Special case for empty listbox: paint focus rect */
1025 rect
.bottom
= rect
.top
+ descr
->item_height
;
1026 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, descr
->focus_item
,
1028 rect
.top
= rect
.bottom
;
1031 /* Paint all the item, regarding the selection
1032 Focus state will be painted after */
1034 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1036 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1037 rect
.bottom
= rect
.top
+ descr
->item_height
;
1039 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1041 if (i
== descr
->focus_item
)
1043 /* keep the focus rect, to paint the focus item after */
1044 focusRect
.left
= rect
.left
;
1045 focusRect
.right
= rect
.right
;
1046 focusRect
.top
= rect
.top
;
1047 focusRect
.bottom
= rect
.bottom
;
1049 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1050 rect
.top
= rect
.bottom
;
1052 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1054 if (!IS_OWNERDRAW(descr
))
1056 /* Clear the bottom of the column */
1057 if (rect
.top
< descr
->height
)
1059 rect
.bottom
= descr
->height
;
1060 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1061 &rect
, NULL
, 0, NULL
);
1065 /* Go to the next column */
1066 rect
.left
+= descr
->column_width
;
1067 rect
.right
+= descr
->column_width
;
1069 col_pos
= descr
->page_size
- 1;
1074 if (rect
.top
>= descr
->height
) break;
1078 /* Paint the focus item now */
1079 if (focusRect
.top
!= focusRect
.bottom
&& descr
->caret_on
)
1080 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1082 if (!IS_OWNERDRAW(descr
))
1084 /* Clear the remainder of the client area */
1085 if (rect
.top
< descr
->height
)
1087 rect
.bottom
= descr
->height
;
1088 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1089 &rect
, NULL
, 0, NULL
);
1091 if (rect
.right
< descr
->width
)
1093 rect
.left
= rect
.right
;
1094 rect
.right
= descr
->width
;
1096 rect
.bottom
= descr
->height
;
1097 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1098 &rect
, NULL
, 0, NULL
);
1101 if (oldFont
) SelectObject( hdc
, oldFont
);
1102 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1107 /***********************************************************************
1108 * LISTBOX_InvalidateItems
1110 * Invalidate all items from a given item. If the specified item is not
1111 * visible, nothing happens.
1113 static void LISTBOX_InvalidateItems( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1117 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1119 if (descr
->style
& LBS_NOREDRAW
)
1121 descr
->style
|= LBS_DISPLAYCHANGED
;
1124 rect
.bottom
= descr
->height
;
1125 InvalidateRect( hwnd
, &rect
, TRUE
);
1126 if (descr
->style
& LBS_MULTICOLUMN
)
1128 /* Repaint the other columns */
1129 rect
.left
= rect
.right
;
1130 rect
.right
= descr
->width
;
1132 InvalidateRect( hwnd
, &rect
, TRUE
);
1138 /***********************************************************************
1139 * LISTBOX_GetItemHeight
1141 static LRESULT
LISTBOX_GetItemHeight( LB_DESCR
*descr
, INT index
)
1143 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1145 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1146 return descr
->items
[index
].height
;
1148 else return descr
->item_height
;
1152 /***********************************************************************
1153 * LISTBOX_SetItemHeight
1155 static LRESULT
LISTBOX_SetItemHeight( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1156 INT height
, BOOL repaint
)
1158 if (!height
) height
= 1;
1160 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1162 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1163 TRACE("[%p]: item %d height = %d\n", hwnd
, index
, height
);
1164 descr
->items
[index
].height
= height
;
1165 LISTBOX_UpdateScroll( hwnd
, descr
);
1167 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1169 else if (height
!= descr
->item_height
)
1171 TRACE("[%p]: new height = %d\n", hwnd
, height
);
1172 descr
->item_height
= height
;
1173 LISTBOX_UpdatePage( hwnd
, descr
);
1174 LISTBOX_UpdateScroll( hwnd
, descr
);
1176 InvalidateRect( hwnd
, 0, TRUE
);
1182 /***********************************************************************
1183 * LISTBOX_SetHorizontalPos
1185 static void LISTBOX_SetHorizontalPos( HWND hwnd
, LB_DESCR
*descr
, INT pos
)
1189 if (pos
> descr
->horz_extent
- descr
->width
)
1190 pos
= descr
->horz_extent
- descr
->width
;
1191 if (pos
< 0) pos
= 0;
1192 if (!(diff
= descr
->horz_pos
- pos
)) return;
1193 TRACE("[%p]: new horz pos = %d\n", hwnd
, pos
);
1194 descr
->horz_pos
= pos
;
1195 LISTBOX_UpdateScroll( hwnd
, descr
);
1196 if (abs(diff
) < descr
->width
)
1197 ScrollWindowEx( hwnd
, diff
, 0, NULL
, NULL
, 0, NULL
,
1198 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1200 InvalidateRect( hwnd
, NULL
, TRUE
);
1204 /***********************************************************************
1205 * LISTBOX_SetHorizontalExtent
1207 static LRESULT
LISTBOX_SetHorizontalExtent( HWND hwnd
, LB_DESCR
*descr
,
1210 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1212 if (extent
<= 0) extent
= 1;
1213 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1214 TRACE("[%p]: new horz extent = %d\n", hwnd
, extent
);
1215 descr
->horz_extent
= extent
;
1216 if (descr
->horz_pos
> extent
- descr
->width
)
1217 LISTBOX_SetHorizontalPos( hwnd
, descr
, extent
- descr
->width
);
1219 LISTBOX_UpdateScroll( hwnd
, descr
);
1224 /***********************************************************************
1225 * LISTBOX_SetColumnWidth
1227 static LRESULT
LISTBOX_SetColumnWidth( HWND hwnd
, LB_DESCR
*descr
, INT width
)
1229 if (width
== descr
->column_width
) return LB_OKAY
;
1230 TRACE("[%p]: new column width = %d\n", hwnd
, width
);
1231 descr
->column_width
= width
;
1232 LISTBOX_UpdatePage( hwnd
, descr
);
1237 /***********************************************************************
1240 * Returns the item height.
1242 static INT
LISTBOX_SetFont( HWND hwnd
, LB_DESCR
*descr
, HFONT font
)
1250 if (!(hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
)))
1252 ERR("unable to get DC.\n" );
1255 if (font
) oldFont
= SelectObject( hdc
, font
);
1256 GetTextMetricsW( hdc
, &tm
);
1257 if (oldFont
) SelectObject( hdc
, oldFont
);
1258 ReleaseDC( hwnd
, hdc
);
1259 if (!IS_OWNERDRAW(descr
))
1260 LISTBOX_SetItemHeight( hwnd
, descr
, 0, tm
.tmHeight
, FALSE
);
1261 return tm
.tmHeight
;
1265 /***********************************************************************
1266 * LISTBOX_MakeItemVisible
1268 * Make sure that a given item is partially or fully visible.
1270 static void LISTBOX_MakeItemVisible( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1275 if (index
<= descr
->top_item
) top
= index
;
1276 else if (descr
->style
& LBS_MULTICOLUMN
)
1278 INT cols
= descr
->width
;
1279 if (!fully
) cols
+= descr
->column_width
- 1;
1280 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1282 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1283 top
= index
- descr
->page_size
* (cols
- 1);
1285 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1287 INT height
= fully
? descr
->items
[index
].height
: 1;
1288 for (top
= index
; top
> descr
->top_item
; top
--)
1289 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1293 if (index
< descr
->top_item
+ descr
->page_size
) return;
1294 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1295 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1296 top
= index
- descr
->page_size
+ 1;
1298 LISTBOX_SetTopItem( hwnd
, descr
, top
, TRUE
);
1301 /***********************************************************************
1302 * LISTBOX_SetCaretIndex
1305 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1308 static LRESULT
LISTBOX_SetCaretIndex( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1309 BOOL fully_visible
)
1311 INT oldfocus
= descr
->focus_item
;
1313 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1314 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1315 if (index
== oldfocus
) return LB_OKAY
;
1316 descr
->focus_item
= index
;
1317 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1318 LISTBOX_RepaintItem( hwnd
, descr
, oldfocus
, ODA_FOCUS
);
1320 LISTBOX_MakeItemVisible( hwnd
, descr
, index
, fully_visible
);
1321 if (descr
->caret_on
&& (descr
->in_focus
))
1322 LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_FOCUS
);
1328 /***********************************************************************
1329 * LISTBOX_SelectItemRange
1331 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1333 static LRESULT
LISTBOX_SelectItemRange( HWND hwnd
, LB_DESCR
*descr
, INT first
,
1338 /* A few sanity checks */
1340 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1341 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1342 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1343 if (last
== -1) last
= descr
->nb_items
- 1;
1344 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1345 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1346 /* selected_item reflects last selected/unselected item on multiple sel */
1347 descr
->selected_item
= last
;
1349 if (on
) /* Turn selection on */
1351 for (i
= first
; i
<= last
; i
++)
1353 if (descr
->items
[i
].selected
) continue;
1354 descr
->items
[i
].selected
= TRUE
;
1355 LISTBOX_RepaintItem( hwnd
, descr
, i
, ODA_SELECT
);
1357 LISTBOX_SetCaretIndex( hwnd
, descr
, last
, TRUE
);
1359 else /* Turn selection off */
1361 for (i
= first
; i
<= last
; i
++)
1363 if (!descr
->items
[i
].selected
) continue;
1364 descr
->items
[i
].selected
= FALSE
;
1365 LISTBOX_RepaintItem( hwnd
, descr
, i
, ODA_SELECT
);
1371 /***********************************************************************
1372 * LISTBOX_SetSelection
1374 static LRESULT
LISTBOX_SetSelection( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1375 BOOL on
, BOOL send_notify
)
1377 TRACE( "index=%d notify=%s\n", index
, send_notify
? "YES" : "NO" );
1379 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1380 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1381 if (descr
->style
& LBS_MULTIPLESEL
)
1383 if (index
== -1) /* Select all items */
1384 return LISTBOX_SelectItemRange( hwnd
, descr
, 0, -1, on
);
1385 else /* Only one item */
1386 return LISTBOX_SelectItemRange( hwnd
, descr
, index
, index
, on
);
1390 INT oldsel
= descr
->selected_item
;
1391 if (index
== oldsel
) return LB_OKAY
;
1392 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1393 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1394 descr
->selected_item
= index
;
1395 if (oldsel
!= -1) LISTBOX_RepaintItem( hwnd
, descr
, oldsel
, ODA_SELECT
);
1396 if (index
!= -1) LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_SELECT
);
1397 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( hwnd
, descr
,
1398 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1400 if( descr
->lphc
) /* set selection change flag for parent combo */
1401 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1407 /***********************************************************************
1410 * Change the caret position and extend the selection to the new caret.
1412 static void LISTBOX_MoveCaret( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1413 BOOL fully_visible
)
1415 INT oldfocus
= descr
->focus_item
;
1417 if ((index
< 0) || (index
>= descr
->nb_items
))
1420 /* Important, repaint needs to be done in this order if
1421 you want to mimic Windows behavior:
1422 1. Remove the focus and paint the item
1423 2. Remove the selection and paint the item(s)
1424 3. Set the selection and repaint the item(s)
1425 4. Set the focus to 'index' and repaint the item */
1427 /* 1. remove the focus and repaint the item */
1428 descr
->focus_item
= -1;
1429 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1430 LISTBOX_RepaintItem( hwnd
, descr
, oldfocus
, ODA_FOCUS
);
1432 /* 2. then turn off the previous selection */
1433 /* 3. repaint the new selected item */
1434 if (descr
->style
& LBS_EXTENDEDSEL
)
1436 if (descr
->anchor_item
!= -1)
1438 INT first
= min( index
, descr
->anchor_item
);
1439 INT last
= max( index
, descr
->anchor_item
);
1441 LISTBOX_SelectItemRange( hwnd
, descr
, 0, first
- 1, FALSE
);
1442 LISTBOX_SelectItemRange( hwnd
, descr
, last
+ 1, -1, FALSE
);
1443 LISTBOX_SelectItemRange( hwnd
, descr
, first
, last
, TRUE
);
1446 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1448 /* Set selection to new caret item */
1449 LISTBOX_SetSelection( hwnd
, descr
, index
, TRUE
, FALSE
);
1452 /* 4. repaint the new item with the focus */
1453 descr
->focus_item
= index
;
1454 LISTBOX_MakeItemVisible( hwnd
, descr
, index
, fully_visible
);
1455 if (descr
->caret_on
&& (descr
->in_focus
))
1456 LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_FOCUS
);
1460 /***********************************************************************
1461 * LISTBOX_InsertItem
1463 static LRESULT
LISTBOX_InsertItem( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1464 LPWSTR str
, DWORD data
)
1468 INT oldfocus
= descr
->focus_item
;
1470 if (index
== -1) index
= descr
->nb_items
;
1471 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1472 if (!descr
->items
) max_items
= 0;
1473 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1474 if (descr
->nb_items
== max_items
)
1476 /* We need to grow the array */
1477 max_items
+= LB_ARRAY_GRANULARITY
;
1478 if (!(item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1479 max_items
* sizeof(LB_ITEMDATA
) )))
1481 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
1484 descr
->items
= item
;
1487 /* Insert the item structure */
1489 item
= &descr
->items
[index
];
1490 if (index
< descr
->nb_items
)
1491 RtlMoveMemory( item
+ 1, item
,
1492 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1496 item
->selected
= FALSE
;
1499 /* Get item height */
1501 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1503 MEASUREITEMSTRUCT mis
;
1504 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
1506 mis
.CtlType
= ODT_LISTBOX
;
1509 mis
.itemData
= descr
->items
[index
].data
;
1510 mis
.itemHeight
= descr
->item_height
;
1511 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1512 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1513 TRACE("[%p]: measure item %d (%s) = %d\n",
1514 hwnd
, index
, str
? debugstr_w(str
) : "", item
->height
);
1517 /* Repaint the items */
1519 LISTBOX_UpdateScroll( hwnd
, descr
);
1520 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1522 /* Move selection and focused item */
1523 /* If listbox was empty, set focus to the first item */
1524 if (descr
->nb_items
== 1)
1525 LISTBOX_SetCaretIndex( hwnd
, descr
, 0, FALSE
);
1526 /* single select don't change selection index in win31 */
1527 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1529 descr
->selected_item
++;
1530 LISTBOX_SetSelection( hwnd
, descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1534 if (index
<= descr
->selected_item
)
1536 descr
->selected_item
++;
1537 descr
->focus_item
= oldfocus
; /* focus not changed */
1544 /***********************************************************************
1545 * LISTBOX_InsertString
1547 static LRESULT
LISTBOX_InsertString( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1550 LPWSTR new_str
= NULL
;
1554 if (HAS_STRINGS(descr
))
1556 static const WCHAR empty_stringW
[] = { 0 };
1557 if (!str
) str
= empty_stringW
;
1558 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1560 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
1563 strcpyW(new_str
, str
);
1565 else data
= (DWORD
)str
;
1567 if (index
== -1) index
= descr
->nb_items
;
1568 if ((ret
= LISTBOX_InsertItem( hwnd
, descr
, index
, new_str
, data
)) != 0)
1570 if (new_str
) HeapFree( GetProcessHeap(), 0, new_str
);
1574 TRACE("[%p]: added item %d %s\n",
1575 hwnd
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1580 /***********************************************************************
1581 * LISTBOX_DeleteItem
1583 * Delete the content of an item. 'index' must be a valid index.
1585 static void LISTBOX_DeleteItem( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1587 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1588 * while Win95 sends it for all items with user data.
1589 * It's probably better to send it too often than not
1590 * often enough, so this is what we do here.
1592 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1594 DELETEITEMSTRUCT dis
;
1595 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
1597 dis
.CtlType
= ODT_LISTBOX
;
1600 dis
.hwndItem
= hwnd
;
1601 dis
.itemData
= descr
->items
[index
].data
;
1602 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1604 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1605 HeapFree( GetProcessHeap(), 0, descr
->items
[index
].str
);
1609 /***********************************************************************
1610 * LISTBOX_RemoveItem
1612 * Remove an item from the listbox and delete its content.
1614 static LRESULT
LISTBOX_RemoveItem( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1619 if ((index
== -1) && (descr
->nb_items
> 0)) index
= descr
->nb_items
- 1;
1620 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1622 /* We need to invalidate the original rect instead of the updated one. */
1623 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1625 LISTBOX_DeleteItem( hwnd
, descr
, index
);
1627 /* Remove the item */
1629 item
= &descr
->items
[index
];
1630 if (index
< descr
->nb_items
-1)
1631 RtlMoveMemory( item
, item
+ 1,
1632 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1634 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1636 /* Shrink the item array if possible */
1638 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1639 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1641 max_items
-= LB_ARRAY_GRANULARITY
;
1642 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1643 max_items
* sizeof(LB_ITEMDATA
) );
1644 if (item
) descr
->items
= item
;
1646 /* Repaint the items */
1648 LISTBOX_UpdateScroll( hwnd
, descr
);
1649 /* if we removed the scrollbar, reset the top of the list
1650 (correct for owner-drawn ???) */
1651 if (descr
->nb_items
== descr
->page_size
)
1652 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1654 /* Move selection and focused item */
1655 if (!IS_MULTISELECT(descr
))
1657 if (index
== descr
->selected_item
)
1658 descr
->selected_item
= -1;
1659 else if (index
< descr
->selected_item
)
1661 descr
->selected_item
--;
1662 if (ISWIN31
) /* win 31 do not change the selected item number */
1663 LISTBOX_SetSelection( hwnd
, descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1667 if (descr
->focus_item
>= descr
->nb_items
)
1669 descr
->focus_item
= descr
->nb_items
- 1;
1670 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1676 /***********************************************************************
1677 * LISTBOX_ResetContent
1679 static void LISTBOX_ResetContent( HWND hwnd
, LB_DESCR
*descr
)
1683 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( hwnd
, descr
, i
);
1684 if (descr
->items
) HeapFree( GetProcessHeap(), 0, descr
->items
);
1685 descr
->nb_items
= 0;
1686 descr
->top_item
= 0;
1687 descr
->selected_item
= -1;
1688 descr
->focus_item
= 0;
1689 descr
->anchor_item
= -1;
1690 descr
->items
= NULL
;
1694 /***********************************************************************
1697 static LRESULT
LISTBOX_SetCount( HWND hwnd
, LB_DESCR
*descr
, INT count
)
1701 if (HAS_STRINGS(descr
)) return LB_ERR
;
1702 /* FIXME: this is far from optimal... */
1703 if (count
> descr
->nb_items
)
1705 while (count
> descr
->nb_items
)
1706 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, -1, 0 )) < 0)
1709 else if (count
< descr
->nb_items
)
1711 while (count
< descr
->nb_items
)
1712 if ((ret
= LISTBOX_RemoveItem( hwnd
, descr
, -1 )) < 0)
1719 /***********************************************************************
1722 static LRESULT
LISTBOX_Directory( HWND hwnd
, LB_DESCR
*descr
, UINT attrib
,
1723 LPCWSTR filespec
, BOOL long_names
)
1726 LRESULT ret
= LB_OKAY
;
1727 WIN32_FIND_DATAW entry
;
1730 /* don't scan directory if we just want drives exclusively */
1731 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1732 /* scan directory */
1733 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1735 if (GetLastError() != ERROR_NO_MORE_FILES
) return LB_ERR
;
1742 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1744 static const WCHAR bracketW
[] = { ']',0 };
1745 static const WCHAR dotW
[] = { '.',0 };
1746 if (!(attrib
& DDL_DIRECTORY
) ||
1747 !strcmpW( entry
.cAlternateFileName
, dotW
)) continue;
1749 if (long_names
) strcpyW( buffer
+ 1, entry
.cFileName
);
1750 else strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1751 strcatW(buffer
, bracketW
);
1753 else /* not a directory */
1755 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1756 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1758 if ((attrib
& DDL_EXCLUSIVE
) &&
1759 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1762 if (long_names
) strcpyW( buffer
, entry
.cFileName
);
1763 else strcpyW( buffer
, entry
.cAlternateFileName
);
1765 if (!long_names
) CharLowerW( buffer
);
1766 pos
= LISTBOX_FindFileStrPos( hwnd
, descr
, buffer
);
1767 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, pos
, buffer
)) < 0)
1769 } while (FindNextFileW( handle
, &entry
));
1770 FindClose( handle
);
1775 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1777 WCHAR buffer
[] = {'[','-','a','-',']',0};
1778 WCHAR root
[] = {'A',':','\\',0};
1780 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1782 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1783 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, -1, buffer
)) < 0)
1791 /***********************************************************************
1792 * LISTBOX_HandleVScroll
1794 static LRESULT
LISTBOX_HandleVScroll( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1798 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1799 switch(LOWORD(wParam
))
1802 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
- 1, TRUE
);
1805 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+ 1, TRUE
);
1808 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
-
1809 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1812 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+
1813 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1815 case SB_THUMBPOSITION
:
1816 LISTBOX_SetTopItem( hwnd
, descr
, HIWORD(wParam
), TRUE
);
1819 info
.cbSize
= sizeof(info
);
1820 info
.fMask
= SIF_TRACKPOS
;
1821 GetScrollInfo( hwnd
, SB_VERT
, &info
);
1822 LISTBOX_SetTopItem( hwnd
, descr
, info
.nTrackPos
, TRUE
);
1825 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1828 LISTBOX_SetTopItem( hwnd
, descr
, descr
->nb_items
, TRUE
);
1835 /***********************************************************************
1836 * LISTBOX_HandleHScroll
1838 static LRESULT
LISTBOX_HandleHScroll( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1843 if (descr
->style
& LBS_MULTICOLUMN
)
1845 switch(LOWORD(wParam
))
1848 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
-descr
->page_size
,
1852 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+descr
->page_size
,
1856 page
= descr
->width
/ descr
->column_width
;
1857 if (page
< 1) page
= 1;
1858 LISTBOX_SetTopItem( hwnd
, descr
,
1859 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1862 page
= descr
->width
/ descr
->column_width
;
1863 if (page
< 1) page
= 1;
1864 LISTBOX_SetTopItem( hwnd
, descr
,
1865 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1867 case SB_THUMBPOSITION
:
1868 LISTBOX_SetTopItem( hwnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1872 info
.cbSize
= sizeof(info
);
1873 info
.fMask
= SIF_TRACKPOS
;
1874 GetScrollInfo( hwnd
, SB_VERT
, &info
);
1875 LISTBOX_SetTopItem( hwnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1879 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1882 LISTBOX_SetTopItem( hwnd
, descr
, descr
->nb_items
, TRUE
);
1886 else if (descr
->horz_extent
)
1888 switch(LOWORD(wParam
))
1891 LISTBOX_SetHorizontalPos( hwnd
, descr
, descr
->horz_pos
- 1 );
1894 LISTBOX_SetHorizontalPos( hwnd
, descr
, descr
->horz_pos
+ 1 );
1897 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1898 descr
->horz_pos
- descr
->width
);
1901 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1902 descr
->horz_pos
+ descr
->width
);
1904 case SB_THUMBPOSITION
:
1905 LISTBOX_SetHorizontalPos( hwnd
, descr
, HIWORD(wParam
) );
1908 info
.cbSize
= sizeof(info
);
1909 info
.fMask
= SIF_TRACKPOS
;
1910 GetScrollInfo( hwnd
, SB_HORZ
, &info
);
1911 LISTBOX_SetHorizontalPos( hwnd
, descr
, info
.nTrackPos
);
1914 LISTBOX_SetHorizontalPos( hwnd
, descr
, 0 );
1917 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1918 descr
->horz_extent
- descr
->width
);
1925 static LRESULT
LISTBOX_HandleMouseWheel(HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1927 short gcWheelDelta
= 0;
1928 UINT pulScrollLines
= 3;
1930 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1932 gcWheelDelta
-= (short) HIWORD(wParam
);
1934 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
1936 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
1937 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
1938 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+ cLineScroll
, TRUE
);
1943 /***********************************************************************
1944 * LISTBOX_HandleLButtonDown
1946 static LRESULT
LISTBOX_HandleLButtonDown( HWND hwnd
, LB_DESCR
*descr
,
1947 WPARAM wParam
, INT x
, INT y
)
1949 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
1950 TRACE("[%p]: lbuttondown %d,%d item %d\n", hwnd
, x
, y
, index
);
1951 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
1953 if (!descr
->in_focus
)
1955 if( !descr
->lphc
) SetFocus( hwnd
);
1956 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
1959 if (index
== -1) return 0;
1961 if (descr
->style
& LBS_EXTENDEDSEL
)
1963 /* we should perhaps make sure that all items are deselected
1964 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1965 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1966 LISTBOX_SetSelection( hwnd, descr, -1, FALSE, FALSE);
1969 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1970 if (wParam
& MK_CONTROL
)
1972 LISTBOX_SetCaretIndex( hwnd
, descr
, index
, FALSE
);
1973 LISTBOX_SetSelection( hwnd
, descr
, index
,
1974 !descr
->items
[index
].selected
,
1975 (descr
->style
& LBS_NOTIFY
) != 0);
1977 else LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
1981 LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
1982 LISTBOX_SetSelection( hwnd
, descr
, index
,
1983 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1984 !descr
->items
[index
].selected
),
1985 (descr
->style
& LBS_NOTIFY
) != 0 );
1988 descr
->captured
= TRUE
;
1993 if (descr
->style
& LBS_NOTIFY
)
1994 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
1995 MAKELPARAM( x
, y
) );
1996 if (GetWindowLongA( hwnd
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2003 if (DragDetect( hwnd
, pt
))
2004 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2011 /*************************************************************************
2012 * LISTBOX_HandleLButtonDownCombo [Internal]
2014 * Process LButtonDown message for the ComboListBox
2017 * pWnd [I] The windows internal structure
2018 * pDescr [I] The ListBox internal structure
2019 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2020 * x [I] X Mouse Coordinate
2021 * y [I] Y Mouse Coordinate
2024 * 0 since we are processing the WM_LBUTTONDOWN Message
2027 * This function is only to be used when a ListBox is a ComboListBox
2030 static LRESULT
LISTBOX_HandleLButtonDownCombo( HWND hwnd
, LB_DESCR
*pDescr
,
2031 UINT msg
, WPARAM wParam
, INT x
, INT y
)
2033 RECT clientRect
, screenRect
;
2039 GetClientRect(hwnd
, &clientRect
);
2041 if(PtInRect(&clientRect
, mousePos
))
2043 /* MousePos is in client, resume normal processing */
2044 if (msg
== WM_LBUTTONDOWN
)
2046 pDescr
->lphc
->droppedIndex
= pDescr
->nb_items
? pDescr
->selected_item
: -1;
2047 return LISTBOX_HandleLButtonDown( hwnd
, pDescr
, wParam
, x
, y
);
2049 else if (pDescr
->style
& LBS_NOTIFY
)
2050 SEND_NOTIFICATION( hwnd
, pDescr
, LBN_DBLCLK
);
2055 POINT screenMousePos
;
2056 HWND hWndOldCapture
;
2058 /* Check the Non-Client Area */
2059 screenMousePos
= mousePos
;
2060 hWndOldCapture
= GetCapture();
2062 GetWindowRect(hwnd
, &screenRect
);
2063 ClientToScreen(hwnd
, &screenMousePos
);
2065 if(!PtInRect(&screenRect
, screenMousePos
))
2067 LISTBOX_SetCaretIndex( hwnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
);
2068 LISTBOX_SetSelection( hwnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2069 COMBO_FlipListbox( pDescr
->lphc
, FALSE
, FALSE
);
2074 /* Check to see the NC is a scrollbar */
2076 LONG style
= GetWindowLongA( hwnd
, GWL_STYLE
);
2077 /* Check Vertical scroll bar */
2078 if (style
& WS_VSCROLL
)
2080 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2081 if (PtInRect( &clientRect
, mousePos
))
2083 nHitTestType
= HTVSCROLL
;
2086 /* Check horizontal scroll bar */
2087 if (style
& WS_HSCROLL
)
2089 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2090 if (PtInRect( &clientRect
, mousePos
))
2092 nHitTestType
= HTHSCROLL
;
2095 /* Windows sends this message when a scrollbar is clicked
2098 if(nHitTestType
!= 0)
2100 SendMessageW(hwnd
, WM_NCLBUTTONDOWN
, nHitTestType
,
2101 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2103 /* Resume the Capture after scrolling is complete
2105 if(hWndOldCapture
!= 0)
2107 SetCapture(hWndOldCapture
);
2114 /***********************************************************************
2115 * LISTBOX_HandleLButtonUp
2117 static LRESULT
LISTBOX_HandleLButtonUp( HWND hwnd
, LB_DESCR
*descr
)
2119 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2120 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2121 LISTBOX_Timer
= LB_TIMER_NONE
;
2122 if (descr
->captured
)
2124 descr
->captured
= FALSE
;
2125 if (GetCapture() == hwnd
) ReleaseCapture();
2126 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2127 SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2133 /***********************************************************************
2134 * LISTBOX_HandleTimer
2136 * Handle scrolling upon a timer event.
2137 * Return TRUE if scrolling should continue.
2139 static LRESULT
LISTBOX_HandleTimer( HWND hwnd
, LB_DESCR
*descr
,
2140 INT index
, TIMER_DIRECTION dir
)
2145 if (descr
->top_item
) index
= descr
->top_item
- 1;
2149 if (descr
->top_item
) index
-= descr
->page_size
;
2152 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2153 if (index
== descr
->focus_item
) index
++;
2154 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2156 case LB_TIMER_RIGHT
:
2157 if (index
+ descr
->page_size
< descr
->nb_items
)
2158 index
+= descr
->page_size
;
2163 if (index
== descr
->focus_item
) return FALSE
;
2164 LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
2169 /***********************************************************************
2170 * LISTBOX_HandleSystemTimer
2172 * WM_SYSTIMER handler.
2174 static LRESULT
LISTBOX_HandleSystemTimer( HWND hwnd
, LB_DESCR
*descr
)
2176 if (!LISTBOX_HandleTimer( hwnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
2178 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2179 LISTBOX_Timer
= LB_TIMER_NONE
;
2185 /***********************************************************************
2186 * LISTBOX_HandleMouseMove
2188 * WM_MOUSEMOVE handler.
2190 static void LISTBOX_HandleMouseMove( HWND hwnd
, LB_DESCR
*descr
,
2194 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2196 if (!descr
->captured
) return;
2198 if (descr
->style
& LBS_MULTICOLUMN
)
2201 else if (y
>= descr
->item_height
* descr
->page_size
)
2202 y
= descr
->item_height
* descr
->page_size
- 1;
2206 dir
= LB_TIMER_LEFT
;
2209 else if (x
>= descr
->width
)
2211 dir
= LB_TIMER_RIGHT
;
2212 x
= descr
->width
- 1;
2217 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2218 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2221 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2222 if (index
== -1) index
= descr
->focus_item
;
2223 if (!LISTBOX_HandleTimer( hwnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2225 /* Start/stop the system timer */
2227 if (dir
!= LB_TIMER_NONE
)
2228 SetSystemTimer( hwnd
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2229 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2230 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2231 LISTBOX_Timer
= dir
;
2235 /***********************************************************************
2236 * LISTBOX_HandleKeyDown
2238 static LRESULT
LISTBOX_HandleKeyDown( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
2241 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2242 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2243 bForceSelection
= FALSE
; /* only for single select list */
2245 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2247 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2248 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2250 if (caret
== -2) return 0;
2252 if (caret
== -1) switch(wParam
)
2255 if (descr
->style
& LBS_MULTICOLUMN
)
2257 bForceSelection
= FALSE
;
2258 if (descr
->focus_item
>= descr
->page_size
)
2259 caret
= descr
->focus_item
- descr
->page_size
;
2264 caret
= descr
->focus_item
- 1;
2265 if (caret
< 0) caret
= 0;
2268 if (descr
->style
& LBS_MULTICOLUMN
)
2270 bForceSelection
= FALSE
;
2271 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2272 caret
= descr
->focus_item
+ descr
->page_size
;
2277 caret
= descr
->focus_item
+ 1;
2278 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2282 if (descr
->style
& LBS_MULTICOLUMN
)
2284 INT page
= descr
->width
/ descr
->column_width
;
2285 if (page
< 1) page
= 1;
2286 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2288 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2289 if (caret
< 0) caret
= 0;
2292 if (descr
->style
& LBS_MULTICOLUMN
)
2294 INT page
= descr
->width
/ descr
->column_width
;
2295 if (page
< 1) page
= 1;
2296 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2298 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2299 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2305 caret
= descr
->nb_items
- 1;
2308 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2309 else if (descr
->style
& LBS_MULTIPLESEL
)
2311 LISTBOX_SetSelection( hwnd
, descr
, descr
->focus_item
,
2312 !descr
->items
[descr
->focus_item
].selected
,
2313 (descr
->style
& LBS_NOTIFY
) != 0 );
2317 bForceSelection
= FALSE
;
2319 if (bForceSelection
) /* focused item is used instead of key */
2320 caret
= descr
->focus_item
;
2323 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
2324 !(GetKeyState( VK_SHIFT
) & 0x8000))
2325 descr
->anchor_item
= caret
;
2326 LISTBOX_MoveCaret( hwnd
, descr
, caret
, TRUE
);
2327 LISTBOX_SetSelection( hwnd
, descr
, caret
, TRUE
, FALSE
);
2328 if (descr
->style
& LBS_NOTIFY
)
2332 /* make sure that combo parent doesn't hide us */
2333 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2335 if (descr
->nb_items
) SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2342 /***********************************************************************
2343 * LISTBOX_HandleChar
2345 static LRESULT
LISTBOX_HandleChar( HWND hwnd
, LB_DESCR
*descr
, WCHAR charW
)
2353 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2355 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2356 MAKEWPARAM(charW
, descr
->focus_item
),
2358 if (caret
== -2) return 0;
2361 caret
= LISTBOX_FindString( hwnd
, descr
, descr
->focus_item
, str
, FALSE
);
2364 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2365 LISTBOX_SetSelection( hwnd
, descr
, caret
, TRUE
, FALSE
);
2366 LISTBOX_MoveCaret( hwnd
, descr
, caret
, TRUE
);
2367 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2368 SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2374 /***********************************************************************
2377 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2380 MEASUREITEMSTRUCT mis
;
2383 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2386 GetClientRect( hwnd
, &rect
);
2387 descr
->owner
= GetParent( hwnd
);
2388 descr
->style
= GetWindowLongA( hwnd
, GWL_STYLE
);
2389 descr
->width
= rect
.right
- rect
.left
;
2390 descr
->height
= rect
.bottom
- rect
.top
;
2391 descr
->items
= NULL
;
2392 descr
->nb_items
= 0;
2393 descr
->top_item
= 0;
2394 descr
->selected_item
= -1;
2395 descr
->focus_item
= 0;
2396 descr
->anchor_item
= -1;
2397 descr
->item_height
= 1;
2398 descr
->page_size
= 1;
2399 descr
->column_width
= 150;
2400 descr
->horz_extent
= (descr
->style
& WS_HSCROLL
) ? 1 : 0;
2401 descr
->horz_pos
= 0;
2404 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2405 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2406 descr
->in_focus
= FALSE
;
2407 descr
->captured
= FALSE
;
2409 descr
->locale
= 0; /* FIXME */
2412 if (is_old_app(hwnd
) && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2414 /* Win95 document "List Box Differences" from MSDN:
2415 If a list box in a version 3.x application has either the
2416 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2417 horizontal and vertical scroll bars.
2419 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2424 TRACE_(combo
)("[%p]: resetting owner %p -> %p\n", hwnd
, descr
->owner
, lphc
->self
);
2425 descr
->owner
= lphc
->self
;
2428 SetWindowLongA( hwnd
, 0, (LONG
)descr
);
2430 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2432 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2433 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2434 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2435 descr
->item_height
= LISTBOX_SetFont( hwnd
, descr
, 0 );
2437 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2439 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2441 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2442 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2446 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
2447 mis
.CtlType
= ODT_LISTBOX
;
2452 mis
.itemHeight
= descr
->item_height
;
2453 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2454 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2458 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2463 /***********************************************************************
2466 static BOOL
LISTBOX_Destroy( HWND hwnd
, LB_DESCR
*descr
)
2468 LISTBOX_ResetContent( hwnd
, descr
);
2469 SetWindowLongA( hwnd
, 0, 0 );
2470 HeapFree( GetProcessHeap(), 0, descr
);
2475 /***********************************************************************
2476 * ListBoxWndProc_common
2478 static LRESULT WINAPI
ListBoxWndProc_common( HWND hwnd
, UINT msg
,
2479 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2484 if (!(descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 )))
2486 if (msg
== WM_CREATE
)
2488 if (!LISTBOX_Create( hwnd
, NULL
))
2490 TRACE("creating wnd=%p descr=%lx\n", hwnd
, GetWindowLongA( hwnd
, 0 ) );
2493 /* Ignore all other messages before we get a WM_CREATE */
2494 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2495 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2498 TRACE("[%p]: msg %s wp %08x lp %08lx\n",
2499 hwnd
, SPY_GetMsgName(msg
, hwnd
), wParam
, lParam
);
2502 case LB_RESETCONTENT16
:
2503 case LB_RESETCONTENT
:
2504 LISTBOX_ResetContent( hwnd
, descr
);
2505 LISTBOX_UpdateScroll( hwnd
, descr
);
2506 InvalidateRect( hwnd
, NULL
, TRUE
);
2509 case LB_ADDSTRING16
:
2510 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2516 if(unicode
|| !HAS_STRINGS(descr
))
2517 textW
= (LPWSTR
)lParam
;
2520 LPSTR textA
= (LPSTR
)lParam
;
2521 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2522 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2523 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2525 wParam
= LISTBOX_FindStringPos( hwnd
, descr
, textW
, FALSE
);
2526 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2527 if (!unicode
&& HAS_STRINGS(descr
))
2528 HeapFree(GetProcessHeap(), 0, textW
);
2532 case LB_INSERTSTRING16
:
2533 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2534 wParam
= (INT
)(INT16
)wParam
;
2536 case LB_INSERTSTRING
:
2540 if(unicode
|| !HAS_STRINGS(descr
))
2541 textW
= (LPWSTR
)lParam
;
2544 LPSTR textA
= (LPSTR
)lParam
;
2545 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2546 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2547 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2549 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2550 if(!unicode
&& HAS_STRINGS(descr
))
2551 HeapFree(GetProcessHeap(), 0, textW
);
2556 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2562 if(unicode
|| !HAS_STRINGS(descr
))
2563 textW
= (LPWSTR
)lParam
;
2566 LPSTR textA
= (LPSTR
)lParam
;
2567 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2568 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2569 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2571 wParam
= LISTBOX_FindFileStrPos( hwnd
, descr
, textW
);
2572 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2573 if(!unicode
&& HAS_STRINGS(descr
))
2574 HeapFree(GetProcessHeap(), 0, textW
);
2578 case LB_DELETESTRING16
:
2579 case LB_DELETESTRING
:
2580 if (LISTBOX_RemoveItem( hwnd
, descr
, wParam
) != LB_ERR
)
2581 return descr
->nb_items
;
2585 case LB_GETITEMDATA16
:
2586 case LB_GETITEMDATA
:
2587 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2589 return descr
->items
[wParam
].data
;
2591 case LB_SETITEMDATA16
:
2592 case LB_SETITEMDATA
:
2593 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2595 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2600 return descr
->nb_items
;
2603 lParam
= (LPARAM
)MapSL(lParam
);
2606 return LISTBOX_GetText( descr
, wParam
, lParam
, unicode
);
2608 case LB_GETTEXTLEN16
:
2611 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2613 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2614 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2615 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2616 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2618 case LB_GETCURSEL16
:
2620 if (descr
->nb_items
==0)
2622 if (!IS_MULTISELECT(descr
))
2623 return descr
->selected_item
;
2625 if (descr
->selected_item
!=-1)
2626 return descr
->selected_item
;
2628 return descr
->focus_item
;
2629 /* otherwise, if the user tries to move the selection with the */
2630 /* arrow keys, we will give the application something to choke on */
2631 case LB_GETTOPINDEX16
:
2632 case LB_GETTOPINDEX
:
2633 return descr
->top_item
;
2635 case LB_GETITEMHEIGHT16
:
2636 case LB_GETITEMHEIGHT
:
2637 return LISTBOX_GetItemHeight( descr
, wParam
);
2639 case LB_SETITEMHEIGHT16
:
2640 lParam
= LOWORD(lParam
);
2642 case LB_SETITEMHEIGHT
:
2643 return LISTBOX_SetItemHeight( hwnd
, descr
, wParam
, lParam
, TRUE
);
2645 case LB_ITEMFROMPOINT
:
2650 pt
.x
= LOWORD(lParam
);
2651 pt
.y
= HIWORD(lParam
);
2654 rect
.right
= descr
->width
;
2655 rect
.bottom
= descr
->height
;
2657 return MAKELONG( LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
),
2658 !PtInRect( &rect
, pt
) );
2661 case LB_SETCARETINDEX16
:
2662 case LB_SETCARETINDEX
:
2663 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2664 if (LISTBOX_SetCaretIndex( hwnd
, descr
, wParam
, !lParam
) == LB_ERR
)
2671 case LB_GETCARETINDEX16
:
2672 case LB_GETCARETINDEX
:
2673 return descr
->focus_item
;
2675 case LB_SETTOPINDEX16
:
2676 case LB_SETTOPINDEX
:
2677 return LISTBOX_SetTopItem( hwnd
, descr
, wParam
, TRUE
);
2679 case LB_SETCOLUMNWIDTH16
:
2680 case LB_SETCOLUMNWIDTH
:
2681 return LISTBOX_SetColumnWidth( hwnd
, descr
, wParam
);
2683 case LB_GETITEMRECT16
:
2686 ret
= LISTBOX_GetItemRect( descr
, (INT16
)wParam
, &rect
);
2687 CONV_RECT32TO16( &rect
, MapSL(lParam
) );
2691 case LB_GETITEMRECT
:
2692 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2694 case LB_FINDSTRING16
:
2695 wParam
= (INT
)(INT16
)wParam
;
2696 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2702 if(unicode
|| !HAS_STRINGS(descr
))
2703 textW
= (LPWSTR
)lParam
;
2706 LPSTR textA
= (LPSTR
)lParam
;
2707 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2708 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2709 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2711 ret
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, FALSE
);
2712 if(!unicode
&& HAS_STRINGS(descr
))
2713 HeapFree(GetProcessHeap(), 0, textW
);
2717 case LB_FINDSTRINGEXACT16
:
2718 wParam
= (INT
)(INT16
)wParam
;
2719 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2721 case LB_FINDSTRINGEXACT
:
2725 if(unicode
|| !HAS_STRINGS(descr
))
2726 textW
= (LPWSTR
)lParam
;
2729 LPSTR textA
= (LPSTR
)lParam
;
2730 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2731 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2732 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2734 ret
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, TRUE
);
2735 if(!unicode
&& HAS_STRINGS(descr
))
2736 HeapFree(GetProcessHeap(), 0, textW
);
2740 case LB_SELECTSTRING16
:
2741 wParam
= (INT
)(INT16
)wParam
;
2742 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2744 case LB_SELECTSTRING
:
2749 if(HAS_STRINGS(descr
))
2750 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2751 debugstr_a((LPSTR
)lParam
));
2752 if(unicode
|| !HAS_STRINGS(descr
))
2753 textW
= (LPWSTR
)lParam
;
2756 LPSTR textA
= (LPSTR
)lParam
;
2757 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2758 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2759 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2761 index
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, FALSE
);
2762 if(!unicode
&& HAS_STRINGS(descr
))
2763 HeapFree(GetProcessHeap(), 0, textW
);
2764 if (index
!= LB_ERR
)
2766 LISTBOX_SetCaretIndex( hwnd
, descr
, index
, TRUE
);
2767 LISTBOX_SetSelection( hwnd
, descr
, index
, TRUE
, FALSE
);
2773 wParam
= (INT
)(INT16
)wParam
;
2776 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2778 return descr
->items
[wParam
].selected
;
2781 lParam
= (INT
)(INT16
)lParam
;
2784 return LISTBOX_SetSelection( hwnd
, descr
, lParam
, wParam
, FALSE
);
2786 case LB_SETCURSEL16
:
2787 wParam
= (INT
)(INT16
)wParam
;
2790 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2791 LISTBOX_SetCaretIndex( hwnd
, descr
, wParam
, TRUE
);
2792 return LISTBOX_SetSelection( hwnd
, descr
, wParam
, TRUE
, FALSE
);
2794 case LB_GETSELCOUNT16
:
2795 case LB_GETSELCOUNT
:
2796 return LISTBOX_GetSelCount( descr
);
2798 case LB_GETSELITEMS16
:
2799 return LISTBOX_GetSelItems16( descr
, wParam
, (LPINT16
)MapSL(lParam
) );
2801 case LB_GETSELITEMS
:
2802 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2804 case LB_SELITEMRANGE16
:
2805 case LB_SELITEMRANGE
:
2806 if (LOWORD(lParam
) <= HIWORD(lParam
))
2807 return LISTBOX_SelectItemRange( hwnd
, descr
, LOWORD(lParam
),
2808 HIWORD(lParam
), wParam
);
2810 return LISTBOX_SelectItemRange( hwnd
, descr
, HIWORD(lParam
),
2811 LOWORD(lParam
), wParam
);
2813 case LB_SELITEMRANGEEX16
:
2814 case LB_SELITEMRANGEEX
:
2815 if ((INT
)lParam
>= (INT
)wParam
)
2816 return LISTBOX_SelectItemRange( hwnd
, descr
, wParam
, lParam
, TRUE
);
2818 return LISTBOX_SelectItemRange( hwnd
, descr
, lParam
, wParam
, FALSE
);
2820 case LB_GETHORIZONTALEXTENT16
:
2821 case LB_GETHORIZONTALEXTENT
:
2822 return descr
->horz_extent
;
2824 case LB_SETHORIZONTALEXTENT16
:
2825 case LB_SETHORIZONTALEXTENT
:
2826 return LISTBOX_SetHorizontalExtent( hwnd
, descr
, wParam
);
2828 case LB_GETANCHORINDEX16
:
2829 case LB_GETANCHORINDEX
:
2830 return descr
->anchor_item
;
2832 case LB_SETANCHORINDEX16
:
2833 wParam
= (INT
)(INT16
)wParam
;
2835 case LB_SETANCHORINDEX
:
2836 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2838 descr
->anchor_item
= (INT
)wParam
;
2842 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2843 * be set automatically (this is different in Win32) */
2844 if (wParam
& DDL_DRIVES
) wParam
|= DDL_EXCLUSIVE
;
2845 lParam
= (LPARAM
)MapSL(lParam
);
2852 textW
= (LPWSTR
)lParam
;
2855 LPSTR textA
= (LPSTR
)lParam
;
2856 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2857 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2858 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2860 ret
= LISTBOX_Directory( hwnd
, descr
, wParam
, textW
, msg
== LB_DIR
);
2862 HeapFree(GetProcessHeap(), 0, textW
);
2867 return descr
->locale
;
2870 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2873 case LB_INITSTORAGE
:
2874 return LISTBOX_InitStorage( hwnd
, descr
, wParam
);
2877 return LISTBOX_SetCount( hwnd
, descr
, (INT
)wParam
);
2879 case LB_SETTABSTOPS16
:
2880 return LISTBOX_SetTabStops( hwnd
, descr
, (INT
)(INT16
)wParam
, MapSL(lParam
), TRUE
);
2882 case LB_SETTABSTOPS
:
2883 return LISTBOX_SetTabStops( hwnd
, descr
, wParam
, (LPINT
)lParam
, FALSE
);
2887 if (descr
->caret_on
)
2889 descr
->caret_on
= TRUE
;
2890 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2891 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2896 if (!descr
->caret_on
)
2898 descr
->caret_on
= FALSE
;
2899 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
2900 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2904 return LISTBOX_Destroy( hwnd
, descr
);
2907 InvalidateRect( hwnd
, NULL
, TRUE
);
2911 LISTBOX_SetRedraw( hwnd
, descr
, wParam
!= 0 );
2915 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2920 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( hwnd
, &ps
);
2921 ret
= LISTBOX_Paint( hwnd
, descr
, hdc
);
2922 if( !wParam
) EndPaint( hwnd
, &ps
);
2926 LISTBOX_UpdateSize( hwnd
, descr
);
2929 return (LRESULT
)descr
->font
;
2931 LISTBOX_SetFont( hwnd
, descr
, (HFONT
)wParam
);
2932 if (lParam
) InvalidateRect( hwnd
, 0, TRUE
);
2935 descr
->in_focus
= TRUE
;
2936 descr
->caret_on
= TRUE
;
2937 if (descr
->focus_item
!= -1)
2938 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2939 SEND_NOTIFICATION( hwnd
, descr
, LBN_SETFOCUS
);
2942 descr
->in_focus
= FALSE
;
2943 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2944 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2945 SEND_NOTIFICATION( hwnd
, descr
, LBN_KILLFOCUS
);
2948 return LISTBOX_HandleHScroll( hwnd
, descr
, wParam
);
2950 return LISTBOX_HandleVScroll( hwnd
, descr
, wParam
);
2952 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2953 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
2954 return LISTBOX_HandleMouseWheel( hwnd
, descr
, wParam
);
2955 case WM_LBUTTONDOWN
:
2956 return LISTBOX_HandleLButtonDown( hwnd
, descr
, wParam
,
2957 (INT16
)LOWORD(lParam
),
2958 (INT16
)HIWORD(lParam
) );
2959 case WM_LBUTTONDBLCLK
:
2960 if (descr
->style
& LBS_NOTIFY
)
2961 SEND_NOTIFICATION( hwnd
, descr
, LBN_DBLCLK
);
2964 if (GetCapture() == hwnd
)
2965 LISTBOX_HandleMouseMove( hwnd
, descr
, (INT16
)LOWORD(lParam
),
2966 (INT16
)HIWORD(lParam
) );
2969 return LISTBOX_HandleLButtonUp( hwnd
, descr
);
2971 return LISTBOX_HandleKeyDown( hwnd
, descr
, wParam
);
2976 charW
= (WCHAR
)wParam
;
2979 CHAR charA
= (CHAR
)wParam
;
2980 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
2982 return LISTBOX_HandleChar( hwnd
, descr
, charW
);
2985 return LISTBOX_HandleSystemTimer( hwnd
, descr
);
2987 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
2990 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
2991 wParam
, (LPARAM
)hwnd
);
2992 TRACE("hbrush = %p\n", hbrush
);
2994 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
2997 GetClientRect(hwnd
, &rect
);
2998 FillRect((HDC
)wParam
, &rect
, hbrush
);
3004 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3005 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3009 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3010 WARN("[%p]: unknown msg %04x wp %08x lp %08lx\n",
3011 hwnd
, msg
, wParam
, lParam
);
3012 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3013 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3018 /***********************************************************************
3021 * This is just a wrapper for the real wndproc, it only does window locking
3024 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3026 if (!IsWindow(hwnd
)) return 0;
3027 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3030 /***********************************************************************
3033 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3035 if (!IsWindow(hwnd
)) return 0;
3036 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);
3039 /***********************************************************************
3040 * ComboLBWndProc_common
3042 * The real combo listbox wndproc
3044 static LRESULT WINAPI
ComboLBWndProc_common( HWND hwnd
, UINT msg
,
3045 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
3051 if (!(descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 )))
3053 if (msg
== WM_CREATE
)
3055 CREATESTRUCTA
*lpcs
= (CREATESTRUCTA
*)lParam
;
3056 TRACE_(combo
)("\tpassed parent handle = %p\n",lpcs
->lpCreateParams
);
3057 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
3058 return LISTBOX_Create( hwnd
, lphc
);
3060 /* Ignore all other messages before we get a WM_CREATE */
3061 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3062 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3065 TRACE_(combo
)("[%p]: msg %s wp %08x lp %08lx\n",
3066 hwnd
, SPY_GetMsgName(msg
, hwnd
), wParam
, lParam
);
3068 if ((lphc
= descr
->lphc
) != NULL
)
3073 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
3074 (CB_GETTYPE(lphc
) != CBS_SIMPLE
) )
3080 mousePos
.x
= (INT16
)LOWORD(lParam
);
3081 mousePos
.y
= (INT16
)HIWORD(lParam
);
3084 * If we are in a dropdown combobox, we simulate that
3085 * the mouse is captured to show the tracking of the item.
3087 GetClientRect(hwnd
, &clientRect
);
3089 if (PtInRect( &clientRect
, mousePos
))
3091 captured
= descr
->captured
;
3092 descr
->captured
= TRUE
;
3094 LISTBOX_HandleMouseMove( hwnd
, descr
,
3095 mousePos
.x
, mousePos
.y
);
3097 descr
->captured
= captured
;
3102 LISTBOX_HandleMouseMove( hwnd
, descr
,
3103 mousePos
.x
, mousePos
.y
);
3109 /* else we are in Win3.1 look, go with the default behavior. */
3113 if (TWEAK_WineLook
> WIN31_LOOK
)
3119 * If the mouse button "up" is not in the listbox,
3120 * we make sure there is no selection by re-selecting the
3121 * item that was selected when the listbox was made visible.
3123 mousePos
.x
= (INT16
)LOWORD(lParam
);
3124 mousePos
.y
= (INT16
)HIWORD(lParam
);
3126 GetClientRect(hwnd
, &clientRect
);
3129 * When the user clicks outside the combobox and the focus
3130 * is lost, the owning combobox will send a fake buttonup with
3131 * 0xFFFFFFF as the mouse location, we must also revert the
3132 * selection to the original selection.
3134 if ( (lParam
== (LPARAM
)-1) ||
3135 (!PtInRect( &clientRect
, mousePos
)) )
3137 LISTBOX_MoveCaret( hwnd
, descr
, lphc
->droppedIndex
, FALSE
);
3140 return LISTBOX_HandleLButtonUp( hwnd
, descr
);
3141 case WM_LBUTTONDBLCLK
:
3142 case WM_LBUTTONDOWN
:
3143 return LISTBOX_HandleLButtonDownCombo(hwnd
, descr
, msg
, wParam
,
3144 (INT16
)LOWORD(lParam
),
3145 (INT16
)HIWORD(lParam
) );
3149 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3151 /* for some reason(?) Windows makes it possible to
3152 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3154 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3155 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3156 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3158 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3162 return LISTBOX_HandleKeyDown( hwnd
, descr
, wParam
);
3164 case LB_SETCURSEL16
:
3166 lRet
= unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3167 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3168 lRet
=(lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
3171 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3177 /* default handling: call listbox wnd proc */
3178 lRet
= unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3179 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3181 TRACE_(combo
)("\t default on msg [%04x]\n", (UINT16
)msg
);
3186 /***********************************************************************
3189 * NOTE: in Windows, winproc address of the ComboLBox is the same
3190 * as that of the Listbox.
3192 LRESULT WINAPI
ComboLBWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3194 if (!IsWindow(hwnd
)) return 0;
3195 return ComboLBWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3198 /***********************************************************************
3201 LRESULT WINAPI
ComboLBWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3203 if (!IsWindow(hwnd
)) return 0;
3204 return ComboLBWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);