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 "user32/regcontrol.h"
27 #include "wine/debug.h"
30 #include "wine/unicode.h"
31 /* Start of hack section -------------------------------- */
33 typedef short *LPINT16
;
35 BOOL
is_old_app(HWND hwnd
)
40 #define WM_LBTRACKPOINT 0x0131
41 #define WS_EX_DRAGDETECT 0x00000002L
42 #define WM_BEGINDRAG 0x022C
43 #define WM_SYSTIMER 280
45 UINT STDCALL
SetSystemTimer(HWND
,UINT_PTR
,UINT
,TIMERPROC
);
46 WINBOOL STDCALL
KillSystemTimer(HWND
,UINT_PTR
);
48 /* End of hack section -------------------------------- */
55 * Probably needs improvement:
59 /* Items array granularity */
60 #define LB_ARRAY_GRANULARITY 16
62 /* Scrolling timeout in ms */
63 #define LB_SCROLL_TIMEOUT 50
65 /* Listbox system timer id */
68 /* flag listbox changed while setredraw false - internal style */
69 #define LBS_DISPLAYCHANGED 0x80000000
74 LPWSTR str
; /* Item text */
75 BOOL selected
; /* Is item selected? */
76 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
77 DWORD data
; /* User data */
80 /* Listbox structure */
83 HWND owner
; /* Owner window to send notifications to */
84 UINT style
; /* Window style */
85 INT width
; /* Window width */
86 INT height
; /* Window height */
87 LB_ITEMDATA
*items
; /* Array of items */
88 INT nb_items
; /* Number of items */
89 INT top_item
; /* Top visible item */
90 INT selected_item
; /* Selected item */
91 INT focus_item
; /* Item that has the focus */
92 INT anchor_item
; /* Anchor item for extended selection */
93 INT item_height
; /* Default item height */
94 INT page_size
; /* Items per listbox page */
95 INT column_width
; /* Column width for multi-column listboxes */
96 INT horz_extent
; /* Horizontal extent (0 if no hscroll) */
97 INT horz_pos
; /* Horizontal position */
98 INT nb_tabs
; /* Number of tabs in array */
99 INT
*tabs
; /* Array of tabs */
100 BOOL caret_on
; /* Is caret on? */
101 BOOL captured
; /* Is mouse captured? */
103 HFONT font
; /* Current font */
104 LCID locale
; /* Current locale for string comparisons */
105 LPHEADCOMBO lphc
; /* ComboLBox */
109 #define IS_OWNERDRAW(descr) \
110 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
112 #define HAS_STRINGS(descr) \
113 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
116 #define IS_MULTISELECT(descr) \
117 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
119 #define SEND_NOTIFICATION(hwnd,descr,code) \
120 (SendMessageW( (descr)->owner, WM_COMMAND, \
121 MAKEWPARAM( GetWindowLongA((hwnd),GWL_ID), (code)), (LPARAM)(hwnd) ))
123 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
125 /* Current timer status */
135 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
137 static LRESULT WINAPI
ComboLBWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
138 static LRESULT WINAPI
ComboLBWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
139 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
140 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
142 static LRESULT
LISTBOX_GetItemRect( LB_DESCR
*descr
, INT index
, RECT
*rect
);
144 /*********************************************************************
145 * listbox class descriptor
147 const struct builtin_class_descr LISTBOX_builtin_class
=
150 L
"ListBox", /* name */
151 CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
152 (WNDPROC
)ListBoxWndProcW
, /* procW */
153 (WNDPROC
)ListBoxWndProcA
, /* procA */
154 sizeof(LB_DESCR
*), /* extra */
155 (LPCWSTR
) IDC_ARROW
, /* cursor */
158 "ListBox", /* name */
159 CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
160 ListBoxWndProcA
, /* procA */
161 ListBoxWndProcW
, /* procW */
162 sizeof(LB_DESCR
*), /* extra */
163 IDC_ARROW
, /* cursor */
169 /*********************************************************************
170 * combolbox class descriptor
172 const struct builtin_class_descr COMBOLBOX_builtin_class
=
175 L
"ComboLBox", /* name */
176 CS_DBLCLKS
| CS_SAVEBITS
, /* style */
177 (WNDPROC
)ComboLBWndProcW
, /* procW */
178 (WNDPROC
)ComboLBWndProcA
, /* procA */
179 sizeof(LB_DESCR
*), /* extra */
180 (LPCWSTR
) IDC_ARROW
, /* cursor */
183 "ComboLBox", /* name */
184 CS_DBLCLKS
| CS_SAVEBITS
, /* style */
185 ComboLBWndProcA
, /* procA */
186 ComboLBWndProcW
, /* procW */
187 sizeof(LB_DESCR
*), /* extra */
188 IDC_ARROW
, /* cursor */
194 /* check whether app is a Win 3.1 app */
195 inline static BOOL
is_old_app( HWND hwnd
)
197 return (GetExpWinVer16( GetWindowLongA(hwnd
,GWL_HINSTANCE
) ) & 0xFF00 ) == 0x0300;
202 /***********************************************************************
205 void LISTBOX_Dump( HWND hwnd
)
209 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 );
211 TRACE( "Listbox:\n" );
212 TRACE( "hwnd=%p descr=%08x items=%d top=%d\n",
213 hwnd
, (UINT
)descr
, descr
->nb_items
, descr
->top_item
);
214 for (i
= 0, item
= descr
->items
; i
< descr
->nb_items
; i
++, item
++)
216 TRACE( "%4d: %-40s %d %08lx %3d\n",
217 i
, debugstr_w(item
->str
), item
->selected
, item
->data
, item
->height
);
222 /***********************************************************************
223 * LISTBOX_GetCurrentPageSize
225 * Return the current page size
227 static INT
LISTBOX_GetCurrentPageSize( LB_DESCR
*descr
)
230 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
231 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
233 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
235 if (i
== descr
->top_item
) return 1;
236 else return i
- descr
->top_item
;
240 /***********************************************************************
241 * LISTBOX_GetMaxTopIndex
243 * Return the maximum possible index for the top of the listbox.
245 static INT
LISTBOX_GetMaxTopIndex( LB_DESCR
*descr
)
249 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
251 page
= descr
->height
;
252 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
253 if ((page
-= descr
->items
[max
].height
) < 0) break;
254 if (max
< descr
->nb_items
- 1) max
++;
256 else if (descr
->style
& LBS_MULTICOLUMN
)
258 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
259 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
260 max
= (max
- page
) * descr
->page_size
;
264 max
= descr
->nb_items
- descr
->page_size
;
266 if (max
< 0) max
= 0;
271 /***********************************************************************
272 * LISTBOX_UpdateScroll
274 * Update the scrollbars. Should be called whenever the content
275 * of the listbox changes.
277 static void LISTBOX_UpdateScroll( HWND hwnd
, LB_DESCR
*descr
)
281 /* Check the listbox scroll bar flags individually before we call
282 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
283 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
284 scroll bar when we do not need one.
285 if (!(descr->style & WS_VSCROLL)) return;
288 /* It is important that we check descr->style, and not wnd->dwStyle,
289 for WS_VSCROLL, as the former is exactly the one passed in
290 argument to CreateWindow.
291 In Windows (and from now on in Wine :) a listbox created
292 with such a style (no WS_SCROLL) does not update
293 the scrollbar with listbox-related data, thus letting
294 the programmer use it for his/her own purposes. */
296 if (descr
->style
& LBS_NOREDRAW
) return;
297 info
.cbSize
= sizeof(info
);
299 if (descr
->style
& LBS_MULTICOLUMN
)
302 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
303 info
.nPos
= descr
->top_item
/ descr
->page_size
;
304 info
.nPage
= descr
->width
/ descr
->column_width
;
305 if (info
.nPage
< 1) info
.nPage
= 1;
306 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
307 if (descr
->style
& LBS_DISABLENOSCROLL
)
308 info
.fMask
|= SIF_DISABLENOSCROLL
;
309 if (descr
->style
& WS_HSCROLL
)
310 SetScrollInfo( hwnd
, SB_HORZ
, &info
, TRUE
);
312 info
.fMask
= SIF_RANGE
;
313 if (descr
->style
& WS_VSCROLL
)
314 SetScrollInfo( hwnd
, SB_VERT
, &info
, TRUE
);
319 info
.nMax
= descr
->nb_items
- 1;
320 info
.nPos
= descr
->top_item
;
321 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
322 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
323 if (descr
->style
& LBS_DISABLENOSCROLL
)
324 info
.fMask
|= SIF_DISABLENOSCROLL
;
325 if (descr
->style
& WS_VSCROLL
)
326 SetScrollInfo( hwnd
, SB_VERT
, &info
, TRUE
);
328 if (descr
->horz_extent
)
331 info
.nMax
= descr
->horz_extent
- 1;
332 info
.nPos
= descr
->horz_pos
;
333 info
.nPage
= descr
->width
;
334 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
335 if (descr
->style
& LBS_DISABLENOSCROLL
)
336 info
.fMask
|= SIF_DISABLENOSCROLL
;
337 if (descr
->style
& WS_HSCROLL
)
338 SetScrollInfo( hwnd
, SB_HORZ
, &info
, TRUE
);
344 /***********************************************************************
347 * Set the top item of the listbox, scrolling up or down if necessary.
349 static LRESULT
LISTBOX_SetTopItem( HWND hwnd
, LB_DESCR
*descr
, INT index
,
352 INT max
= LISTBOX_GetMaxTopIndex( descr
);
353 if (index
> max
) index
= max
;
354 if (index
< 0) index
= 0;
355 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
356 if (descr
->top_item
== index
) return LB_OKAY
;
357 if (descr
->style
& LBS_MULTICOLUMN
)
359 INT diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
360 if (scroll
&& (abs(diff
) < descr
->width
))
361 ScrollWindowEx( hwnd
, diff
, 0, NULL
, NULL
, 0, NULL
,
362 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
370 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
374 if (index
> descr
->top_item
)
376 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
377 diff
-= descr
->items
[i
].height
;
381 for (i
= index
; i
< descr
->top_item
; i
++)
382 diff
+= descr
->items
[i
].height
;
386 diff
= (descr
->top_item
- index
) * descr
->item_height
;
388 if (abs(diff
) < descr
->height
)
389 ScrollWindowEx( hwnd
, 0, diff
, NULL
, NULL
, 0, NULL
,
390 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
394 if (!scroll
) InvalidateRect( hwnd
, NULL
, TRUE
);
395 descr
->top_item
= index
;
396 LISTBOX_UpdateScroll( hwnd
, descr
);
401 /***********************************************************************
404 * Update the page size. Should be called when the size of
405 * the client area or the item height changes.
407 static void LISTBOX_UpdatePage( HWND hwnd
, LB_DESCR
*descr
)
411 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
413 if (page_size
== descr
->page_size
) return;
414 descr
->page_size
= page_size
;
415 if (descr
->style
& LBS_MULTICOLUMN
)
416 InvalidateRect( hwnd
, NULL
, TRUE
);
417 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
, FALSE
);
421 /***********************************************************************
424 * Update the size of the listbox. Should be called when the size of
425 * the client area changes.
427 static void LISTBOX_UpdateSize( HWND hwnd
, LB_DESCR
*descr
)
431 GetClientRect( hwnd
, &rect
);
432 descr
->width
= rect
.right
- rect
.left
;
433 descr
->height
= rect
.bottom
- rect
.top
;
434 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
439 GetWindowRect( hwnd
, &rect
);
440 if(descr
->item_height
!= 0)
441 remaining
= descr
->height
% descr
->item_height
;
444 if ((descr
->height
> descr
->item_height
) && remaining
)
447 if (is_old_app(hwnd
))
448 { /* give a margin for error to 16 bits programs - if we need
449 less than the height of the nonclient area, round to the
450 *next* number of items */
451 int ncheight
= rect
.bottom
- rect
.top
- descr
->height
;
452 if ((descr
->item_height
- remaining
) <= ncheight
)
453 remaining
= remaining
- descr
->item_height
;
456 TRACE("[%p]: changing height %d -> %d\n",
457 hwnd
, descr
->height
, descr
->height
- remaining
);
458 SetWindowPos( hwnd
, 0, 0, 0, rect
.right
- rect
.left
,
459 rect
.bottom
- rect
.top
- remaining
,
460 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
464 TRACE("[%p]: new size = %d,%d\n", hwnd
, descr
->width
, descr
->height
);
465 LISTBOX_UpdatePage( hwnd
, descr
);
466 LISTBOX_UpdateScroll( hwnd
, descr
);
468 /* Invalidate the focused item so it will be repainted correctly */
469 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
471 InvalidateRect( hwnd
, &rect
, FALSE
);
476 /***********************************************************************
477 * LISTBOX_GetItemRect
479 * Get the rectangle enclosing an item, in listbox client coordinates.
480 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
482 static LRESULT
LISTBOX_GetItemRect( LB_DESCR
*descr
, INT index
, RECT
*rect
)
484 /* Index <= 0 is legal even on empty listboxes */
485 if (index
&& (index
>= descr
->nb_items
)) return -1;
486 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
487 if (descr
->style
& LBS_MULTICOLUMN
)
489 INT col
= (index
/ descr
->page_size
) -
490 (descr
->top_item
/ descr
->page_size
);
491 rect
->left
+= col
* descr
->column_width
;
492 rect
->right
= rect
->left
+ descr
->column_width
;
493 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
494 rect
->bottom
= rect
->top
+ descr
->item_height
;
496 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
499 rect
->right
+= descr
->horz_pos
;
500 if ((index
>= 0) && (index
< descr
->nb_items
))
502 if (index
< descr
->top_item
)
504 for (i
= descr
->top_item
-1; i
>= index
; i
--)
505 rect
->top
-= descr
->items
[i
].height
;
509 for (i
= descr
->top_item
; i
< index
; i
++)
510 rect
->top
+= descr
->items
[i
].height
;
512 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
518 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
519 rect
->bottom
= rect
->top
+ descr
->item_height
;
520 rect
->right
+= descr
->horz_pos
;
523 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
524 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
528 /***********************************************************************
529 * LISTBOX_GetItemFromPoint
531 * Return the item nearest from point (x,y) (in client coordinates).
533 static INT
LISTBOX_GetItemFromPoint( LB_DESCR
*descr
, INT x
, INT y
)
535 INT index
= descr
->top_item
;
537 if (!descr
->nb_items
) return -1; /* No items */
538 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
543 while (index
< descr
->nb_items
)
545 if ((pos
+= descr
->items
[index
].height
) > y
) break;
554 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
558 else if (descr
->style
& LBS_MULTICOLUMN
)
560 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
561 if (y
>= 0) index
+= y
/ descr
->item_height
;
562 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
563 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
567 index
+= (y
/ descr
->item_height
);
569 if (index
< 0) return 0;
570 if (index
>= descr
->nb_items
) return -1;
575 /***********************************************************************
580 static void LISTBOX_PaintItem( HWND hwnd
, LB_DESCR
*descr
, HDC hdc
,
581 const RECT
*rect
, INT index
, UINT action
, BOOL ignoreFocus
)
583 LB_ITEMDATA
*item
= NULL
;
584 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
586 if (IS_OWNERDRAW(descr
))
591 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
595 if (action
== ODA_FOCUS
)
596 DrawFocusRect( hdc
, rect
);
598 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
602 /* some programs mess with the clipping region when
603 drawing the item, *and* restore the previous region
604 after they are done, so a region has better to exist
605 else everything ends clipped */
606 GetClientRect(hwnd
, &r
);
607 hrgn
= CreateRectRgnIndirect(&r
);
608 SelectClipRgn( hdc
, hrgn
);
609 DeleteObject( hrgn
);
611 dis
.CtlType
= ODT_LISTBOX
;
614 dis
.itemAction
= action
;
618 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
619 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
621 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
622 if (!IsWindowEnabled(hwnd
)) dis
.itemState
|= ODS_DISABLED
;
623 dis
.itemData
= item
? item
->data
: 0;
625 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%ld,%ld-%ld,%ld\n",
626 hwnd
, index
, item
? debugstr_w(item
->str
) : "", action
,
627 dis
.itemState
, rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
628 SendMessageW(descr
->owner
, WM_DRAWITEM
, id
, (LPARAM
)&dis
);
632 COLORREF oldText
= 0, oldBk
= 0;
634 if (action
== ODA_FOCUS
)
636 DrawFocusRect( hdc
, rect
);
639 if (item
&& item
->selected
)
641 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
642 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
645 TRACE("[%p]: painting %d (%s) action=%02x rect=%ld,%ld-%ld,%ld\n",
646 hwnd
, index
, item
? debugstr_w(item
->str
) : "", action
,
647 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
649 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
650 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
651 else if (!(descr
->style
& LBS_USETABSTOPS
))
652 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
653 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
654 strlenW(item
->str
), NULL
);
657 /* Output empty string to paint background in the full width. */
658 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
659 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
660 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
661 item
->str
, strlenW(item
->str
),
662 descr
->nb_tabs
, descr
->tabs
, 0);
664 if (item
&& item
->selected
)
666 SetBkColor( hdc
, oldBk
);
667 SetTextColor( hdc
, oldText
);
669 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
671 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
676 /***********************************************************************
679 * Change the redraw flag.
681 static void LISTBOX_SetRedraw( HWND hwnd
, LB_DESCR
*descr
, BOOL on
)
685 if (!(descr
->style
& LBS_NOREDRAW
)) return;
686 descr
->style
&= ~LBS_NOREDRAW
;
687 if (descr
->style
& LBS_DISPLAYCHANGED
)
688 { /* page was changed while setredraw false, refresh automatically */
689 InvalidateRect(hwnd
, NULL
, TRUE
);
690 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
691 { /* reset top of page if less than number of items/page */
692 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
693 if (descr
->top_item
< 0) descr
->top_item
= 0;
695 descr
->style
&= ~LBS_DISPLAYCHANGED
;
697 LISTBOX_UpdateScroll( hwnd
, descr
);
699 else descr
->style
|= LBS_NOREDRAW
;
703 /***********************************************************************
704 * LISTBOX_RepaintItem
706 * Repaint a single item synchronously.
708 static void LISTBOX_RepaintItem( HWND hwnd
, LB_DESCR
*descr
, INT index
,
714 HBRUSH hbrush
, oldBrush
= 0;
716 /* Do not repaint the item if the item is not visible */
717 if (!IsWindowVisible(hwnd
)) return;
718 if (descr
->style
& LBS_NOREDRAW
)
720 descr
->style
|= LBS_DISPLAYCHANGED
;
723 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
724 if (!(hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
))) return;
725 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
726 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
727 (WPARAM
)hdc
, (LPARAM
)hwnd
);
728 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
729 if (!IsWindowEnabled(hwnd
))
730 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
731 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
732 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, index
, action
, FALSE
);
733 if (oldFont
) SelectObject( hdc
, oldFont
);
734 if (oldBrush
) SelectObject( hdc
, oldBrush
);
735 ReleaseDC( hwnd
, hdc
);
739 /***********************************************************************
740 * LISTBOX_InitStorage
742 static LRESULT
LISTBOX_InitStorage( HWND hwnd
, LB_DESCR
*descr
, INT nb_items
)
746 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
747 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
749 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
750 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
751 nb_items
* sizeof(LB_ITEMDATA
));
754 item
= HeapAlloc( GetProcessHeap(), 0,
755 nb_items
* sizeof(LB_ITEMDATA
));
760 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
768 /***********************************************************************
769 * LISTBOX_SetTabStops
771 static BOOL
LISTBOX_SetTabStops( HWND hwnd
, LB_DESCR
*descr
, INT count
,
772 LPINT tabs
, BOOL short_ints
)
774 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
775 if (descr
->tabs
) HeapFree( GetProcessHeap(), 0, descr
->tabs
);
776 if (!(descr
->nb_tabs
= count
))
781 /* FIXME: count = 1 */
782 if (!(descr
->tabs
= (INT
*)HeapAlloc( GetProcessHeap(), 0,
783 descr
->nb_tabs
* sizeof(INT
) )))
789 LPINT16 p
= (LPINT16
)tabs
;
791 TRACE("[%p]: settabstops ", hwnd
);
792 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
793 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
794 if (TRACE_ON(listbox
)) TRACE("%hd ", descr
->tabs
[i
]);
796 if (TRACE_ON(listbox
)) TRACE("\n");
798 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
800 memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
802 /* FIXME: repaint the window? */
807 /***********************************************************************
810 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPARAM lParam
, BOOL unicode
)
812 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
813 if (HAS_STRINGS(descr
))
816 return strlenW(descr
->items
[index
].str
);
818 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
822 LPWSTR buffer
= (LPWSTR
)lParam
;
823 strcpyW( buffer
, descr
->items
[index
].str
);
824 return strlenW(buffer
);
828 LPSTR buffer
= (LPSTR
)lParam
;
829 return WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1, buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
833 *((LPDWORD
)lParam
)=*(LPDWORD
)(&descr
->items
[index
].data
);
834 return sizeof(DWORD
);
839 /***********************************************************************
840 * LISTBOX_FindStringPos
842 * Find the nearest string located before a given string in sort order.
843 * If 'exact' is TRUE, return an error if we don't get an exact match.
845 static INT
LISTBOX_FindStringPos( HWND hwnd
, LB_DESCR
*descr
, LPCWSTR str
,
848 INT index
, min
, max
, res
= -1;
850 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
852 max
= descr
->nb_items
;
855 index
= (min
+ max
) / 2;
856 if (HAS_STRINGS(descr
))
857 res
= lstrcmpiW( str
, descr
->items
[index
].str
);
860 COMPAREITEMSTRUCT cis
;
861 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
863 cis
.CtlType
= ODT_LISTBOX
;
866 /* note that some application (MetaStock) expects the second item
867 * to be in the listbox */
869 cis
.itemData1
= (DWORD
)str
;
871 cis
.itemData2
= descr
->items
[index
].data
;
872 cis
.dwLocaleId
= descr
->locale
;
873 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
875 if (!res
) return index
;
876 if (res
< 0) max
= index
;
877 else min
= index
+ 1;
879 return exact
? -1 : max
;
883 /***********************************************************************
884 * LISTBOX_FindFileStrPos
886 * Find the nearest string located before a given string in directory
887 * sort order (i.e. first files, then directories, then drives).
889 static INT
LISTBOX_FindFileStrPos( HWND hwnd
, LB_DESCR
*descr
, LPCWSTR str
)
891 INT min
, max
, res
= -1;
893 if (!HAS_STRINGS(descr
))
894 return LISTBOX_FindStringPos( hwnd
, descr
, str
, FALSE
);
896 max
= descr
->nb_items
;
899 INT index
= (min
+ max
) / 2;
900 LPCWSTR p
= descr
->items
[index
].str
;
901 if (*p
== '[') /* drive or directory */
903 if (*str
!= '[') res
= -1;
904 else if (p
[1] == '-') /* drive */
906 if (str
[1] == '-') res
= str
[2] - p
[2];
911 if (str
[1] == '-') res
= 1;
912 else res
= lstrcmpiW( str
, p
);
917 if (*str
== '[') res
= 1;
918 else res
= lstrcmpiW( str
, p
);
920 if (!res
) return index
;
921 if (res
< 0) max
= index
;
922 else min
= index
+ 1;
928 /***********************************************************************
931 * Find the item beginning with a given string.
933 static INT
LISTBOX_FindString( HWND hwnd
, LB_DESCR
*descr
, INT start
,
934 LPCWSTR str
, BOOL exact
)
939 if (start
>= descr
->nb_items
) start
= -1;
940 item
= descr
->items
+ start
+ 1;
941 if (HAS_STRINGS(descr
))
943 if (!str
|| ! str
[0] ) return LB_ERR
;
946 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
947 if (!lstrcmpiW( str
, item
->str
)) return i
;
948 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
949 if (!lstrcmpiW( str
, item
->str
)) return i
;
953 /* Special case for drives and directories: ignore prefix */
954 #define CHECK_DRIVE(item) \
955 if ((item)->str[0] == '[') \
957 if (!strncmpiW( str, (item)->str+1, len )) return i; \
958 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
962 INT len
= strlenW(str
);
963 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
965 if (!strncmpiW( str
, item
->str
, len
)) return i
;
968 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
970 if (!strncmpiW( str
, item
->str
, len
)) return i
;
978 if (exact
&& (descr
->style
& LBS_SORT
))
979 /* If sorted, use a WM_COMPAREITEM binary search */
980 return LISTBOX_FindStringPos( hwnd
, descr
, str
, TRUE
);
982 /* Otherwise use a linear search */
983 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
984 if (item
->data
== (DWORD
)str
) return i
;
985 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
986 if (item
->data
== (DWORD
)str
) return i
;
992 /***********************************************************************
993 * LISTBOX_GetSelCount
995 static LRESULT
LISTBOX_GetSelCount( LB_DESCR
*descr
)
998 LB_ITEMDATA
*item
= descr
->items
;
1000 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1001 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
1002 if (item
->selected
) count
++;
1008 /***********************************************************************
1009 * LISTBOX_GetSelItems16
1011 static LRESULT
LISTBOX_GetSelItems16( LB_DESCR
*descr
, INT16 max
, LPINT16 array
)
1014 LB_ITEMDATA
*item
= descr
->items
;
1016 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1017 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
1018 if (item
->selected
) array
[count
++] = (INT16
)i
;
1024 /***********************************************************************
1025 * LISTBOX_GetSelItems
1027 static LRESULT
LISTBOX_GetSelItems( LB_DESCR
*descr
, INT max
, LPINT array
)
1030 LB_ITEMDATA
*item
= descr
->items
;
1032 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1033 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
1034 if (item
->selected
) array
[count
++] = i
;
1039 /***********************************************************************
1042 static LRESULT
LISTBOX_Paint( HWND hwnd
, LB_DESCR
*descr
, HDC hdc
)
1044 INT i
, col_pos
= descr
->page_size
- 1;
1046 RECT focusRect
= {-1, -1, -1, -1};
1048 HBRUSH hbrush
, oldBrush
= 0;
1050 if (descr
->style
& LBS_NOREDRAW
) return 0;
1052 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1053 if (descr
->style
& LBS_MULTICOLUMN
)
1054 rect
.right
= rect
.left
+ descr
->column_width
;
1055 else if (descr
->horz_pos
)
1057 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1058 rect
.right
+= descr
->horz_pos
;
1061 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1062 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
1063 (WPARAM
)hdc
, (LPARAM
)hwnd
);
1064 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1065 if (!IsWindowEnabled(hwnd
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1067 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1070 /* Special case for empty listbox: paint focus rect */
1071 rect
.bottom
= rect
.top
+ descr
->item_height
;
1072 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, descr
->focus_item
,
1074 rect
.top
= rect
.bottom
;
1077 /* Paint all the item, regarding the selection
1078 Focus state will be painted after */
1080 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1082 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1083 rect
.bottom
= rect
.top
+ descr
->item_height
;
1085 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1087 if (i
== descr
->focus_item
)
1089 /* keep the focus rect, to paint the focus item after */
1090 focusRect
.left
= rect
.left
;
1091 focusRect
.right
= rect
.right
;
1092 focusRect
.top
= rect
.top
;
1093 focusRect
.bottom
= rect
.bottom
;
1095 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1096 rect
.top
= rect
.bottom
;
1098 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1100 if (!IS_OWNERDRAW(descr
))
1102 /* Clear the bottom of the column */
1103 if (rect
.top
< descr
->height
)
1105 rect
.bottom
= descr
->height
;
1106 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1107 &rect
, NULL
, 0, NULL
);
1111 /* Go to the next column */
1112 rect
.left
+= descr
->column_width
;
1113 rect
.right
+= descr
->column_width
;
1115 col_pos
= descr
->page_size
- 1;
1120 if (rect
.top
>= descr
->height
) break;
1124 /* Paint the focus item now */
1125 if (focusRect
.top
!= focusRect
.bottom
&& descr
->caret_on
)
1126 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1128 if (!IS_OWNERDRAW(descr
))
1130 /* Clear the remainder of the client area */
1131 if (rect
.top
< descr
->height
)
1133 rect
.bottom
= descr
->height
;
1134 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1135 &rect
, NULL
, 0, NULL
);
1137 if (rect
.right
< descr
->width
)
1139 rect
.left
= rect
.right
;
1140 rect
.right
= descr
->width
;
1142 rect
.bottom
= descr
->height
;
1143 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1144 &rect
, NULL
, 0, NULL
);
1147 if (oldFont
) SelectObject( hdc
, oldFont
);
1148 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1153 /***********************************************************************
1154 * LISTBOX_InvalidateItems
1156 * Invalidate all items from a given item. If the specified item is not
1157 * visible, nothing happens.
1159 static void LISTBOX_InvalidateItems( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1163 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1165 if (descr
->style
& LBS_NOREDRAW
)
1167 descr
->style
|= LBS_DISPLAYCHANGED
;
1170 rect
.bottom
= descr
->height
;
1171 InvalidateRect( hwnd
, &rect
, TRUE
);
1172 if (descr
->style
& LBS_MULTICOLUMN
)
1174 /* Repaint the other columns */
1175 rect
.left
= rect
.right
;
1176 rect
.right
= descr
->width
;
1178 InvalidateRect( hwnd
, &rect
, TRUE
);
1183 static void LISTBOX_InvalidateItemRect( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1187 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1188 InvalidateRect( hwnd
, &rect
, TRUE
);
1191 /***********************************************************************
1192 * LISTBOX_GetItemHeight
1194 static LRESULT
LISTBOX_GetItemHeight( LB_DESCR
*descr
, INT index
)
1196 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1198 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1199 return descr
->items
[index
].height
;
1201 else return descr
->item_height
;
1205 /***********************************************************************
1206 * LISTBOX_SetItemHeight
1208 static LRESULT
LISTBOX_SetItemHeight( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1209 INT height
, BOOL repaint
)
1211 if (!height
) height
= 1;
1213 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1215 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1216 TRACE("[%p]: item %d height = %d\n", hwnd
, index
, height
);
1217 descr
->items
[index
].height
= height
;
1218 LISTBOX_UpdateScroll( hwnd
, descr
);
1220 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1222 else if (height
!= descr
->item_height
)
1224 TRACE("[%p]: new height = %d\n", hwnd
, height
);
1225 descr
->item_height
= height
;
1226 LISTBOX_UpdatePage( hwnd
, descr
);
1227 LISTBOX_UpdateScroll( hwnd
, descr
);
1229 InvalidateRect( hwnd
, 0, TRUE
);
1235 /***********************************************************************
1236 * LISTBOX_SetHorizontalPos
1238 static void LISTBOX_SetHorizontalPos( HWND hwnd
, LB_DESCR
*descr
, INT pos
)
1242 if (pos
> descr
->horz_extent
- descr
->width
)
1243 pos
= descr
->horz_extent
- descr
->width
;
1244 if (pos
< 0) pos
= 0;
1245 if (!(diff
= descr
->horz_pos
- pos
)) return;
1246 TRACE("[%p]: new horz pos = %d\n", hwnd
, pos
);
1247 descr
->horz_pos
= pos
;
1248 LISTBOX_UpdateScroll( hwnd
, descr
);
1249 if (abs(diff
) < descr
->width
)
1252 /* Invalidate the focused item so it will be repainted correctly */
1253 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1254 InvalidateRect( hwnd
, &rect
, TRUE
);
1255 ScrollWindowEx( hwnd
, diff
, 0, NULL
, NULL
, 0, NULL
,
1256 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1259 InvalidateRect( hwnd
, NULL
, TRUE
);
1263 /***********************************************************************
1264 * LISTBOX_SetHorizontalExtent
1266 static LRESULT
LISTBOX_SetHorizontalExtent( HWND hwnd
, LB_DESCR
*descr
,
1269 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1271 if (extent
<= 0) extent
= 1;
1272 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1273 TRACE("[%p]: new horz extent = %d\n", hwnd
, extent
);
1274 descr
->horz_extent
= extent
;
1275 if (descr
->horz_pos
> extent
- descr
->width
)
1276 LISTBOX_SetHorizontalPos( hwnd
, descr
, extent
- descr
->width
);
1278 LISTBOX_UpdateScroll( hwnd
, descr
);
1283 /***********************************************************************
1284 * LISTBOX_SetColumnWidth
1286 static LRESULT
LISTBOX_SetColumnWidth( HWND hwnd
, LB_DESCR
*descr
, INT width
)
1288 if (width
== descr
->column_width
) return LB_OKAY
;
1289 TRACE("[%p]: new column width = %d\n", hwnd
, width
);
1290 descr
->column_width
= width
;
1291 LISTBOX_UpdatePage( hwnd
, descr
);
1296 /***********************************************************************
1299 * Returns the item height.
1301 static INT
LISTBOX_SetFont( HWND hwnd
, LB_DESCR
*descr
, HFONT font
)
1309 if (!(hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
)))
1311 ERR("unable to get DC.\n" );
1314 if (font
) oldFont
= SelectObject( hdc
, font
);
1315 GetTextMetricsW( hdc
, &tm
);
1316 if (oldFont
) SelectObject( hdc
, oldFont
);
1317 ReleaseDC( hwnd
, hdc
);
1318 if (!IS_OWNERDRAW(descr
))
1319 LISTBOX_SetItemHeight( hwnd
, descr
, 0, tm
.tmHeight
, FALSE
);
1320 return tm
.tmHeight
;
1324 /***********************************************************************
1325 * LISTBOX_MakeItemVisible
1327 * Make sure that a given item is partially or fully visible.
1329 static void LISTBOX_MakeItemVisible( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1334 if (index
<= descr
->top_item
) top
= index
;
1335 else if (descr
->style
& LBS_MULTICOLUMN
)
1337 INT cols
= descr
->width
;
1338 if (!fully
) cols
+= descr
->column_width
- 1;
1339 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1341 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1342 top
= index
- descr
->page_size
* (cols
- 1);
1344 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1346 INT height
= fully
? descr
->items
[index
].height
: 1;
1347 for (top
= index
; top
> descr
->top_item
; top
--)
1348 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1352 if (index
< descr
->top_item
+ descr
->page_size
) return;
1353 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1354 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1355 top
= index
- descr
->page_size
+ 1;
1357 LISTBOX_SetTopItem( hwnd
, descr
, top
, TRUE
);
1360 /***********************************************************************
1361 * LISTBOX_SetCaretIndex
1364 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1367 static LRESULT
LISTBOX_SetCaretIndex( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1368 BOOL fully_visible
)
1370 INT oldfocus
= descr
->focus_item
;
1372 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1373 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1374 if (index
== oldfocus
) return LB_OKAY
;
1375 descr
->focus_item
= index
;
1376 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1377 LISTBOX_RepaintItem( hwnd
, descr
, oldfocus
, ODA_FOCUS
);
1379 LISTBOX_MakeItemVisible( hwnd
, descr
, index
, fully_visible
);
1380 if (descr
->caret_on
&& (descr
->in_focus
))
1381 LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_FOCUS
);
1387 /***********************************************************************
1388 * LISTBOX_SelectItemRange
1390 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1392 static LRESULT
LISTBOX_SelectItemRange( HWND hwnd
, LB_DESCR
*descr
, INT first
,
1397 /* A few sanity checks */
1399 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1400 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1401 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1402 if (last
== -1) last
= descr
->nb_items
- 1;
1403 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1404 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1405 /* selected_item reflects last selected/unselected item on multiple sel */
1406 descr
->selected_item
= last
;
1408 if (on
) /* Turn selection on */
1410 for (i
= first
; i
<= last
; i
++)
1412 if (descr
->items
[i
].selected
) continue;
1413 descr
->items
[i
].selected
= TRUE
;
1414 LISTBOX_InvalidateItemRect(hwnd
, descr
, i
);
1416 LISTBOX_SetCaretIndex( hwnd
, descr
, last
, TRUE
);
1418 else /* Turn selection off */
1420 for (i
= first
; i
<= last
; i
++)
1422 if (!descr
->items
[i
].selected
) continue;
1423 descr
->items
[i
].selected
= FALSE
;
1424 LISTBOX_InvalidateItemRect(hwnd
, descr
, i
);
1430 /***********************************************************************
1431 * LISTBOX_SetSelection
1433 static LRESULT
LISTBOX_SetSelection( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1434 BOOL on
, BOOL send_notify
)
1436 TRACE( "index=%d notify=%s\n", index
, send_notify
? "YES" : "NO" );
1438 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1439 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1440 if (descr
->style
& LBS_MULTIPLESEL
)
1442 if (index
== -1) /* Select all items */
1443 return LISTBOX_SelectItemRange( hwnd
, descr
, 0, -1, on
);
1444 else /* Only one item */
1445 return LISTBOX_SelectItemRange( hwnd
, descr
, index
, index
, on
);
1449 INT oldsel
= descr
->selected_item
;
1450 if (index
== oldsel
) return LB_OKAY
;
1451 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1452 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1453 descr
->selected_item
= index
;
1454 if (oldsel
!= -1) LISTBOX_RepaintItem( hwnd
, descr
, oldsel
, ODA_SELECT
);
1455 if (index
!= -1) LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_SELECT
);
1456 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( hwnd
, descr
,
1457 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1459 if( descr
->lphc
) /* set selection change flag for parent combo */
1460 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1466 /***********************************************************************
1469 * Change the caret position and extend the selection to the new caret.
1471 static void LISTBOX_MoveCaret( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1472 BOOL fully_visible
)
1474 INT oldfocus
= descr
->focus_item
;
1476 if ((index
< 0) || (index
>= descr
->nb_items
))
1479 /* Important, repaint needs to be done in this order if
1480 you want to mimic Windows behavior:
1481 1. Remove the focus and paint the item
1482 2. Remove the selection and paint the item(s)
1483 3. Set the selection and repaint the item(s)
1484 4. Set the focus to 'index' and repaint the item */
1486 /* 1. remove the focus and repaint the item */
1487 descr
->focus_item
= -1;
1488 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1489 LISTBOX_RepaintItem( hwnd
, descr
, oldfocus
, ODA_FOCUS
);
1491 /* 2. then turn off the previous selection */
1492 /* 3. repaint the new selected item */
1493 if (descr
->style
& LBS_EXTENDEDSEL
)
1495 if (descr
->anchor_item
!= -1)
1497 INT first
= min( index
, descr
->anchor_item
);
1498 INT last
= max( index
, descr
->anchor_item
);
1500 LISTBOX_SelectItemRange( hwnd
, descr
, 0, first
- 1, FALSE
);
1501 LISTBOX_SelectItemRange( hwnd
, descr
, last
+ 1, -1, FALSE
);
1502 LISTBOX_SelectItemRange( hwnd
, descr
, first
, last
, TRUE
);
1505 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1507 /* Set selection to new caret item */
1508 LISTBOX_SetSelection( hwnd
, descr
, index
, TRUE
, FALSE
);
1511 /* 4. repaint the new item with the focus */
1512 descr
->focus_item
= index
;
1513 LISTBOX_MakeItemVisible( hwnd
, descr
, index
, fully_visible
);
1514 if (descr
->caret_on
&& (descr
->in_focus
))
1515 LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_FOCUS
);
1519 /***********************************************************************
1520 * LISTBOX_InsertItem
1522 static LRESULT
LISTBOX_InsertItem( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1523 LPWSTR str
, DWORD data
)
1527 INT oldfocus
= descr
->focus_item
;
1529 if (index
== -1) index
= descr
->nb_items
;
1530 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1531 if (!descr
->items
) max_items
= 0;
1532 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1533 if (descr
->nb_items
== max_items
)
1535 /* We need to grow the array */
1536 max_items
+= LB_ARRAY_GRANULARITY
;
1538 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1539 max_items
* sizeof(LB_ITEMDATA
) );
1541 item
= HeapAlloc( GetProcessHeap(), 0,
1542 max_items
* sizeof(LB_ITEMDATA
) );
1545 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
1548 descr
->items
= item
;
1551 /* Insert the item structure */
1553 item
= &descr
->items
[index
];
1554 if (index
< descr
->nb_items
)
1555 RtlMoveMemory( item
+ 1, item
,
1556 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1560 item
->selected
= FALSE
;
1563 /* Get item height */
1565 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1567 MEASUREITEMSTRUCT mis
;
1568 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
1570 mis
.CtlType
= ODT_LISTBOX
;
1573 mis
.itemData
= descr
->items
[index
].data
;
1574 mis
.itemHeight
= descr
->item_height
;
1575 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1576 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1577 TRACE("[%p]: measure item %d (%s) = %d\n",
1578 hwnd
, index
, str
? debugstr_w(str
) : "", item
->height
);
1581 /* Repaint the items */
1583 LISTBOX_UpdateScroll( hwnd
, descr
);
1584 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1586 /* Move selection and focused item */
1587 /* If listbox was empty, set focus to the first item */
1588 if (descr
->nb_items
== 1)
1589 LISTBOX_SetCaretIndex( hwnd
, descr
, 0, FALSE
);
1590 /* single select don't change selection index in win31 */
1591 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1593 descr
->selected_item
++;
1594 LISTBOX_SetSelection( hwnd
, descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1598 if (index
<= descr
->selected_item
)
1600 descr
->selected_item
++;
1601 descr
->focus_item
= oldfocus
; /* focus not changed */
1608 /***********************************************************************
1609 * LISTBOX_InsertString
1611 static LRESULT
LISTBOX_InsertString( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1614 LPWSTR new_str
= NULL
;
1618 if (HAS_STRINGS(descr
))
1620 static const WCHAR empty_stringW
[] = { 0 };
1621 if (!str
) str
= empty_stringW
;
1622 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1624 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
1627 strcpyW(new_str
, str
);
1629 else data
= (DWORD
)str
;
1631 if (index
== -1) index
= descr
->nb_items
;
1632 if ((ret
= LISTBOX_InsertItem( hwnd
, descr
, index
, new_str
, data
)) != 0)
1634 if (new_str
) HeapFree( GetProcessHeap(), 0, new_str
);
1638 TRACE("[%p]: added item %d %s\n",
1639 hwnd
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1644 /***********************************************************************
1645 * LISTBOX_DeleteItem
1647 * Delete the content of an item. 'index' must be a valid index.
1649 static void LISTBOX_DeleteItem( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1651 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1652 * while Win95 sends it for all items with user data.
1653 * It's probably better to send it too often than not
1654 * often enough, so this is what we do here.
1656 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1658 DELETEITEMSTRUCT dis
;
1659 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
1661 dis
.CtlType
= ODT_LISTBOX
;
1664 dis
.hwndItem
= hwnd
;
1665 dis
.itemData
= descr
->items
[index
].data
;
1666 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1668 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1669 HeapFree( GetProcessHeap(), 0, descr
->items
[index
].str
);
1673 /***********************************************************************
1674 * LISTBOX_RemoveItem
1676 * Remove an item from the listbox and delete its content.
1678 static LRESULT
LISTBOX_RemoveItem( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1683 if ((index
== -1) && (descr
->nb_items
> 0)) index
= descr
->nb_items
- 1;
1684 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1686 /* We need to invalidate the original rect instead of the updated one. */
1687 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1689 LISTBOX_DeleteItem( hwnd
, descr
, index
);
1691 /* Remove the item */
1693 item
= &descr
->items
[index
];
1694 if (index
< descr
->nb_items
-1)
1695 RtlMoveMemory( item
, item
+ 1,
1696 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1698 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1700 /* Shrink the item array if possible */
1702 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1703 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1705 max_items
-= LB_ARRAY_GRANULARITY
;
1706 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1707 max_items
* sizeof(LB_ITEMDATA
) );
1708 if (item
) descr
->items
= item
;
1710 /* Repaint the items */
1712 LISTBOX_UpdateScroll( hwnd
, descr
);
1713 /* if we removed the scrollbar, reset the top of the list
1714 (correct for owner-drawn ???) */
1715 if (descr
->nb_items
== descr
->page_size
)
1716 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1718 /* Move selection and focused item */
1719 if (!IS_MULTISELECT(descr
))
1721 if (index
== descr
->selected_item
)
1722 descr
->selected_item
= -1;
1723 else if (index
< descr
->selected_item
)
1725 descr
->selected_item
--;
1726 if (ISWIN31
) /* win 31 do not change the selected item number */
1727 LISTBOX_SetSelection( hwnd
, descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1731 if (descr
->focus_item
>= descr
->nb_items
)
1733 descr
->focus_item
= descr
->nb_items
- 1;
1734 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1740 /***********************************************************************
1741 * LISTBOX_ResetContent
1743 static void LISTBOX_ResetContent( HWND hwnd
, LB_DESCR
*descr
)
1747 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( hwnd
, descr
, i
);
1748 if (descr
->items
) HeapFree( GetProcessHeap(), 0, descr
->items
);
1749 descr
->nb_items
= 0;
1750 descr
->top_item
= 0;
1751 descr
->selected_item
= -1;
1752 descr
->focus_item
= 0;
1753 descr
->anchor_item
= -1;
1754 descr
->items
= NULL
;
1758 /***********************************************************************
1761 static LRESULT
LISTBOX_SetCount( HWND hwnd
, LB_DESCR
*descr
, INT count
)
1765 if (HAS_STRINGS(descr
)) return LB_ERR
;
1766 /* FIXME: this is far from optimal... */
1767 if (count
> descr
->nb_items
)
1769 while (count
> descr
->nb_items
)
1770 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, -1, 0 )) < 0)
1773 else if (count
< descr
->nb_items
)
1775 while (count
< descr
->nb_items
)
1776 if ((ret
= LISTBOX_RemoveItem( hwnd
, descr
, -1 )) < 0)
1783 /***********************************************************************
1786 static LRESULT
LISTBOX_Directory( HWND hwnd
, LB_DESCR
*descr
, UINT attrib
,
1787 LPCWSTR filespec
, BOOL long_names
)
1790 LRESULT ret
= LB_OKAY
;
1791 WIN32_FIND_DATAW entry
;
1794 /* don't scan directory if we just want drives exclusively */
1795 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1796 /* scan directory */
1797 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1799 int le
= GetLastError();
1800 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1807 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1809 static const WCHAR bracketW
[] = { ']',0 };
1810 static const WCHAR dotW
[] = { '.',0 };
1811 if (!(attrib
& DDL_DIRECTORY
) ||
1812 !strcmpW( entry
.cFileName
, dotW
)) continue;
1814 if (!long_names
&& entry
.cAlternateFileName
[0])
1815 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1817 strcpyW( buffer
+ 1, entry
.cFileName
);
1818 strcatW(buffer
, bracketW
);
1820 else /* not a directory */
1822 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1823 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1825 if ((attrib
& DDL_EXCLUSIVE
) &&
1826 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1829 if (!long_names
&& entry
.cAlternateFileName
[0])
1830 strcpyW( buffer
, entry
.cAlternateFileName
);
1832 strcpyW( buffer
, entry
.cFileName
);
1834 if (!long_names
) CharLowerW( buffer
);
1835 pos
= LISTBOX_FindFileStrPos( hwnd
, descr
, buffer
);
1836 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, pos
, buffer
)) < 0)
1838 } while (FindNextFileW( handle
, &entry
));
1839 FindClose( handle
);
1844 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1846 WCHAR buffer
[] = {'[','-','a','-',']',0};
1847 WCHAR root
[] = {'A',':','\\',0};
1849 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1851 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1852 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, -1, buffer
)) < 0)
1860 /***********************************************************************
1861 * LISTBOX_HandleVScroll
1863 static LRESULT
LISTBOX_HandleVScroll( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1867 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1868 switch(LOWORD(wParam
))
1871 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
- 1, TRUE
);
1874 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+ 1, TRUE
);
1877 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
-
1878 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1881 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+
1882 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1884 case SB_THUMBPOSITION
:
1885 LISTBOX_SetTopItem( hwnd
, descr
, HIWORD(wParam
), TRUE
);
1888 info
.cbSize
= sizeof(info
);
1889 info
.fMask
= SIF_TRACKPOS
;
1890 GetScrollInfo( hwnd
, SB_VERT
, &info
);
1891 LISTBOX_SetTopItem( hwnd
, descr
, info
.nTrackPos
, TRUE
);
1894 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1897 LISTBOX_SetTopItem( hwnd
, descr
, descr
->nb_items
, TRUE
);
1904 /***********************************************************************
1905 * LISTBOX_HandleHScroll
1907 static LRESULT
LISTBOX_HandleHScroll( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1912 if (descr
->style
& LBS_MULTICOLUMN
)
1914 switch(LOWORD(wParam
))
1917 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
-descr
->page_size
,
1921 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+descr
->page_size
,
1925 page
= descr
->width
/ descr
->column_width
;
1926 if (page
< 1) page
= 1;
1927 LISTBOX_SetTopItem( hwnd
, descr
,
1928 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1931 page
= descr
->width
/ descr
->column_width
;
1932 if (page
< 1) page
= 1;
1933 LISTBOX_SetTopItem( hwnd
, descr
,
1934 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1936 case SB_THUMBPOSITION
:
1937 LISTBOX_SetTopItem( hwnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1941 info
.cbSize
= sizeof(info
);
1942 info
.fMask
= SIF_TRACKPOS
;
1943 GetScrollInfo( hwnd
, SB_VERT
, &info
);
1944 LISTBOX_SetTopItem( hwnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1948 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1951 LISTBOX_SetTopItem( hwnd
, descr
, descr
->nb_items
, TRUE
);
1955 else if (descr
->horz_extent
)
1957 switch(LOWORD(wParam
))
1960 LISTBOX_SetHorizontalPos( hwnd
, descr
, descr
->horz_pos
- 1 );
1963 LISTBOX_SetHorizontalPos( hwnd
, descr
, descr
->horz_pos
+ 1 );
1966 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1967 descr
->horz_pos
- descr
->width
);
1970 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1971 descr
->horz_pos
+ descr
->width
);
1973 case SB_THUMBPOSITION
:
1974 LISTBOX_SetHorizontalPos( hwnd
, descr
, HIWORD(wParam
) );
1977 info
.cbSize
= sizeof(info
);
1978 info
.fMask
= SIF_TRACKPOS
;
1979 GetScrollInfo( hwnd
, SB_HORZ
, &info
);
1980 LISTBOX_SetHorizontalPos( hwnd
, descr
, info
.nTrackPos
);
1983 LISTBOX_SetHorizontalPos( hwnd
, descr
, 0 );
1986 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1987 descr
->horz_extent
- descr
->width
);
1994 static LRESULT
LISTBOX_HandleMouseWheel(HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1996 short gcWheelDelta
= 0;
1997 UINT pulScrollLines
= 3;
1999 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
2001 gcWheelDelta
-= (short) HIWORD(wParam
);
2003 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
2005 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
2006 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
2007 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+ cLineScroll
, TRUE
);
2012 /***********************************************************************
2013 * LISTBOX_HandleLButtonDown
2015 static LRESULT
LISTBOX_HandleLButtonDown( HWND hwnd
, LB_DESCR
*descr
,
2016 WPARAM wParam
, INT x
, INT y
)
2018 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2019 TRACE("[%p]: lbuttondown %d,%d item %d\n", hwnd
, x
, y
, index
);
2020 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2022 if (!descr
->in_focus
)
2024 if( !descr
->lphc
) SetFocus( hwnd
);
2025 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2028 if (index
== -1) return 0;
2030 if (descr
->style
& LBS_EXTENDEDSEL
)
2032 /* we should perhaps make sure that all items are deselected
2033 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2034 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
2035 LISTBOX_SetSelection( hwnd, descr, -1, FALSE, FALSE);
2038 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
2039 if (wParam
& MK_CONTROL
)
2041 LISTBOX_SetCaretIndex( hwnd
, descr
, index
, FALSE
);
2042 LISTBOX_SetSelection( hwnd
, descr
, index
,
2043 !descr
->items
[index
].selected
,
2044 (descr
->style
& LBS_NOTIFY
) != 0);
2046 else LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
2050 LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
2051 LISTBOX_SetSelection( hwnd
, descr
, index
,
2052 (!(descr
->style
& LBS_MULTIPLESEL
) ||
2053 !descr
->items
[index
].selected
),
2054 (descr
->style
& LBS_NOTIFY
) != 0 );
2057 descr
->captured
= TRUE
;
2062 if (descr
->style
& LBS_NOTIFY
)
2063 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2064 MAKELPARAM( x
, y
) );
2065 if (GetWindowLongA( hwnd
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2072 if (DragDetect( hwnd
, pt
))
2073 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2080 /*************************************************************************
2081 * LISTBOX_HandleLButtonDownCombo [Internal]
2083 * Process LButtonDown message for the ComboListBox
2086 * pWnd [I] The windows internal structure
2087 * pDescr [I] The ListBox internal structure
2088 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2089 * x [I] X Mouse Coordinate
2090 * y [I] Y Mouse Coordinate
2093 * 0 since we are processing the WM_LBUTTONDOWN Message
2096 * This function is only to be used when a ListBox is a ComboListBox
2099 static LRESULT
LISTBOX_HandleLButtonDownCombo( HWND hwnd
, LB_DESCR
*pDescr
,
2100 UINT msg
, WPARAM wParam
, INT x
, INT y
)
2102 RECT clientRect
, screenRect
;
2108 GetClientRect(hwnd
, &clientRect
);
2110 if(PtInRect(&clientRect
, mousePos
))
2112 /* MousePos is in client, resume normal processing */
2113 if (msg
== WM_LBUTTONDOWN
)
2115 pDescr
->lphc
->droppedIndex
= pDescr
->nb_items
? pDescr
->selected_item
: -1;
2116 return LISTBOX_HandleLButtonDown( hwnd
, pDescr
, wParam
, x
, y
);
2118 else if (pDescr
->style
& LBS_NOTIFY
)
2119 SEND_NOTIFICATION( hwnd
, pDescr
, LBN_DBLCLK
);
2124 POINT screenMousePos
;
2125 HWND hWndOldCapture
;
2127 /* Check the Non-Client Area */
2128 screenMousePos
= mousePos
;
2129 hWndOldCapture
= GetCapture();
2131 GetWindowRect(hwnd
, &screenRect
);
2132 ClientToScreen(hwnd
, &screenMousePos
);
2134 if(!PtInRect(&screenRect
, screenMousePos
))
2136 LISTBOX_SetCaretIndex( hwnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
);
2137 LISTBOX_SetSelection( hwnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2138 COMBO_FlipListbox( pDescr
->lphc
, FALSE
, FALSE
);
2143 /* Check to see the NC is a scrollbar */
2145 LONG style
= GetWindowLongA( hwnd
, GWL_STYLE
);
2146 /* Check Vertical scroll bar */
2147 if (style
& WS_VSCROLL
)
2149 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2150 if (PtInRect( &clientRect
, mousePos
))
2152 nHitTestType
= HTVSCROLL
;
2155 /* Check horizontal scroll bar */
2156 if (style
& WS_HSCROLL
)
2158 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2159 if (PtInRect( &clientRect
, mousePos
))
2161 nHitTestType
= HTHSCROLL
;
2164 /* Windows sends this message when a scrollbar is clicked
2167 if(nHitTestType
!= 0)
2169 SendMessageW(hwnd
, WM_NCLBUTTONDOWN
, nHitTestType
,
2170 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2172 /* Resume the Capture after scrolling is complete
2174 if(hWndOldCapture
!= 0)
2176 SetCapture(hWndOldCapture
);
2183 /***********************************************************************
2184 * LISTBOX_HandleLButtonUp
2186 static LRESULT
LISTBOX_HandleLButtonUp( HWND hwnd
, LB_DESCR
*descr
)
2188 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2189 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2190 LISTBOX_Timer
= LB_TIMER_NONE
;
2191 if (descr
->captured
)
2193 descr
->captured
= FALSE
;
2194 if (GetCapture() == hwnd
) ReleaseCapture();
2195 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2196 SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2202 /***********************************************************************
2203 * LISTBOX_HandleTimer
2205 * Handle scrolling upon a timer event.
2206 * Return TRUE if scrolling should continue.
2208 static LRESULT
LISTBOX_HandleTimer( HWND hwnd
, LB_DESCR
*descr
,
2209 INT index
, TIMER_DIRECTION dir
)
2214 if (descr
->top_item
) index
= descr
->top_item
- 1;
2218 if (descr
->top_item
) index
-= descr
->page_size
;
2221 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2222 if (index
== descr
->focus_item
) index
++;
2223 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2225 case LB_TIMER_RIGHT
:
2226 if (index
+ descr
->page_size
< descr
->nb_items
)
2227 index
+= descr
->page_size
;
2232 if (index
== descr
->focus_item
) return FALSE
;
2233 LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
2238 /***********************************************************************
2239 * LISTBOX_HandleSystemTimer
2241 * WM_SYSTIMER handler.
2243 static LRESULT
LISTBOX_HandleSystemTimer( HWND hwnd
, LB_DESCR
*descr
)
2245 if (!LISTBOX_HandleTimer( hwnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
2247 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2248 LISTBOX_Timer
= LB_TIMER_NONE
;
2254 /***********************************************************************
2255 * LISTBOX_HandleMouseMove
2257 * WM_MOUSEMOVE handler.
2259 static void LISTBOX_HandleMouseMove( HWND hwnd
, LB_DESCR
*descr
,
2263 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2265 if (!descr
->captured
) return;
2267 if (descr
->style
& LBS_MULTICOLUMN
)
2270 else if (y
>= descr
->item_height
* descr
->page_size
)
2271 y
= descr
->item_height
* descr
->page_size
- 1;
2275 dir
= LB_TIMER_LEFT
;
2278 else if (x
>= descr
->width
)
2280 dir
= LB_TIMER_RIGHT
;
2281 x
= descr
->width
- 1;
2286 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2287 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2290 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2291 if (index
== -1) index
= descr
->focus_item
;
2292 if (!LISTBOX_HandleTimer( hwnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2294 /* Start/stop the system timer */
2296 if (dir
!= LB_TIMER_NONE
)
2297 SetSystemTimer( hwnd
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2298 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2299 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2300 LISTBOX_Timer
= dir
;
2304 /***********************************************************************
2305 * LISTBOX_HandleKeyDown
2307 static LRESULT
LISTBOX_HandleKeyDown( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
2310 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2311 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2312 bForceSelection
= FALSE
; /* only for single select list */
2314 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2316 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2317 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2319 if (caret
== -2) return 0;
2321 if (caret
== -1) switch(wParam
)
2324 if (descr
->style
& LBS_MULTICOLUMN
)
2326 bForceSelection
= FALSE
;
2327 if (descr
->focus_item
>= descr
->page_size
)
2328 caret
= descr
->focus_item
- descr
->page_size
;
2333 caret
= descr
->focus_item
- 1;
2334 if (caret
< 0) caret
= 0;
2337 if (descr
->style
& LBS_MULTICOLUMN
)
2339 bForceSelection
= FALSE
;
2340 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2341 caret
= descr
->focus_item
+ descr
->page_size
;
2346 caret
= descr
->focus_item
+ 1;
2347 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2351 if (descr
->style
& LBS_MULTICOLUMN
)
2353 INT page
= descr
->width
/ descr
->column_width
;
2354 if (page
< 1) page
= 1;
2355 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2357 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2358 if (caret
< 0) caret
= 0;
2361 if (descr
->style
& LBS_MULTICOLUMN
)
2363 INT page
= descr
->width
/ descr
->column_width
;
2364 if (page
< 1) page
= 1;
2365 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2367 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2368 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2374 caret
= descr
->nb_items
- 1;
2377 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2378 else if (descr
->style
& LBS_MULTIPLESEL
)
2380 LISTBOX_SetSelection( hwnd
, descr
, descr
->focus_item
,
2381 !descr
->items
[descr
->focus_item
].selected
,
2382 (descr
->style
& LBS_NOTIFY
) != 0 );
2386 bForceSelection
= FALSE
;
2388 if (bForceSelection
) /* focused item is used instead of key */
2389 caret
= descr
->focus_item
;
2392 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
2393 !(GetKeyState( VK_SHIFT
) & 0x8000))
2394 descr
->anchor_item
= caret
;
2395 LISTBOX_MoveCaret( hwnd
, descr
, caret
, TRUE
);
2396 LISTBOX_SetSelection( hwnd
, descr
, caret
, TRUE
, FALSE
);
2397 if (descr
->style
& LBS_NOTIFY
)
2401 /* make sure that combo parent doesn't hide us */
2402 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2404 if (descr
->nb_items
) SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2411 /***********************************************************************
2412 * LISTBOX_HandleChar
2414 static LRESULT
LISTBOX_HandleChar( HWND hwnd
, LB_DESCR
*descr
, WCHAR charW
)
2422 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2424 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2425 MAKEWPARAM(charW
, descr
->focus_item
),
2427 if (caret
== -2) return 0;
2430 caret
= LISTBOX_FindString( hwnd
, descr
, descr
->focus_item
, str
, FALSE
);
2433 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2434 LISTBOX_SetSelection( hwnd
, descr
, caret
, TRUE
, FALSE
);
2435 LISTBOX_MoveCaret( hwnd
, descr
, caret
, TRUE
);
2436 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2437 SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2443 /***********************************************************************
2446 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2449 MEASUREITEMSTRUCT mis
;
2452 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2455 GetClientRect( hwnd
, &rect
);
2456 descr
->owner
= GetParent( hwnd
);
2457 descr
->style
= GetWindowLongA( hwnd
, GWL_STYLE
);
2458 descr
->width
= rect
.right
- rect
.left
;
2459 descr
->height
= rect
.bottom
- rect
.top
;
2460 descr
->items
= NULL
;
2461 descr
->nb_items
= 0;
2462 descr
->top_item
= 0;
2463 descr
->selected_item
= -1;
2464 descr
->focus_item
= 0;
2465 descr
->anchor_item
= -1;
2466 descr
->item_height
= 1;
2467 descr
->page_size
= 1;
2468 descr
->column_width
= 150;
2469 descr
->horz_extent
= (descr
->style
& WS_HSCROLL
) ? 1 : 0;
2470 descr
->horz_pos
= 0;
2473 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2474 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2475 descr
->in_focus
= FALSE
;
2476 descr
->captured
= FALSE
;
2478 descr
->locale
= 0; /* FIXME */
2482 if (is_old_app(hwnd
) && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2484 /* Win95 document "List Box Differences" from MSDN:
2485 If a list box in a version 3.x application has either the
2486 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2487 horizontal and vertical scroll bars.
2489 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2495 TRACE_(combo
)("[%p]: resetting owner %p -> %p\n", hwnd
, descr
->owner
, lphc
->self
);
2496 descr
->owner
= lphc
->self
;
2499 SetWindowLongA( hwnd
, 0, (LONG
)descr
);
2501 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2503 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2504 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2505 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2506 descr
->item_height
= LISTBOX_SetFont( hwnd
, descr
, 0 );
2508 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2510 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2512 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2513 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2517 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
2518 mis
.CtlType
= ODT_LISTBOX
;
2523 mis
.itemHeight
= descr
->item_height
;
2524 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2525 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2529 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2534 /***********************************************************************
2537 static BOOL
LISTBOX_Destroy( HWND hwnd
, LB_DESCR
*descr
)
2539 LISTBOX_ResetContent( hwnd
, descr
);
2540 SetWindowLongA( hwnd
, 0, 0 );
2541 HeapFree( GetProcessHeap(), 0, descr
);
2546 /***********************************************************************
2547 * ListBoxWndProc_common
2549 static LRESULT WINAPI
ListBoxWndProc_common( HWND hwnd
, UINT msg
,
2550 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2555 if (!(descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 )))
2557 if (msg
== WM_CREATE
)
2559 if (!LISTBOX_Create( hwnd
, NULL
))
2561 TRACE("creating wnd=%p descr=%lx\n", hwnd
, GetWindowLongA( hwnd
, 0 ) );
2564 /* Ignore all other messages before we get a WM_CREATE */
2565 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2566 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2569 TRACE("[%p]: msg %s wp %08x lp %08lx\n",
2570 hwnd
, SPY_GetMsgName(msg
, hwnd
), wParam
, lParam
);
2574 case LB_RESETCONTENT16
:
2576 case LB_RESETCONTENT
:
2577 LISTBOX_ResetContent( hwnd
, descr
);
2578 LISTBOX_UpdateScroll( hwnd
, descr
);
2579 InvalidateRect( hwnd
, NULL
, TRUE
);
2583 case LB_ADDSTRING16
:
2584 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2591 if(unicode
|| !HAS_STRINGS(descr
))
2592 textW
= (LPWSTR
)lParam
;
2595 LPSTR textA
= (LPSTR
)lParam
;
2596 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2597 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2598 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2600 wParam
= LISTBOX_FindStringPos( hwnd
, descr
, textW
, FALSE
);
2601 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2602 if (!unicode
&& HAS_STRINGS(descr
))
2603 HeapFree(GetProcessHeap(), 0, textW
);
2608 case LB_INSERTSTRING16
:
2609 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2610 wParam
= (INT
)(INT16
)wParam
;
2613 case LB_INSERTSTRING
:
2617 if(unicode
|| !HAS_STRINGS(descr
))
2618 textW
= (LPWSTR
)lParam
;
2621 LPSTR textA
= (LPSTR
)lParam
;
2622 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2623 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2624 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2626 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2627 if(!unicode
&& HAS_STRINGS(descr
))
2628 HeapFree(GetProcessHeap(), 0, textW
);
2634 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2641 if(unicode
|| !HAS_STRINGS(descr
))
2642 textW
= (LPWSTR
)lParam
;
2645 LPSTR textA
= (LPSTR
)lParam
;
2646 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2647 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2648 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2650 wParam
= LISTBOX_FindFileStrPos( hwnd
, descr
, textW
);
2651 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2652 if(!unicode
&& HAS_STRINGS(descr
))
2653 HeapFree(GetProcessHeap(), 0, textW
);
2658 case LB_DELETESTRING16
:
2660 case LB_DELETESTRING
:
2661 if (LISTBOX_RemoveItem( hwnd
, descr
, wParam
) != LB_ERR
)
2662 return descr
->nb_items
;
2667 case LB_GETITEMDATA16
:
2669 case LB_GETITEMDATA
:
2670 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2672 return descr
->items
[wParam
].data
;
2675 case LB_SETITEMDATA16
:
2677 case LB_SETITEMDATA
:
2678 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2680 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2687 return descr
->nb_items
;
2691 lParam
= (LPARAM
)MapSL(lParam
);
2695 return LISTBOX_GetText( descr
, wParam
, lParam
, unicode
);
2698 case LB_GETTEXTLEN16
:
2702 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2704 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2705 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2706 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2707 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2710 case LB_GETCURSEL16
:
2713 if (descr
->nb_items
==0)
2715 if (!IS_MULTISELECT(descr
))
2716 return descr
->selected_item
;
2718 if (descr
->selected_item
!=-1)
2719 return descr
->selected_item
;
2721 return descr
->focus_item
;
2722 /* otherwise, if the user tries to move the selection with the */
2723 /* arrow keys, we will give the application something to choke on */
2725 case LB_GETTOPINDEX16
:
2727 case LB_GETTOPINDEX
:
2728 return descr
->top_item
;
2731 case LB_GETITEMHEIGHT16
:
2733 case LB_GETITEMHEIGHT
:
2734 return LISTBOX_GetItemHeight( descr
, wParam
);
2737 case LB_SETITEMHEIGHT16
:
2738 lParam
= LOWORD(lParam
);
2741 case LB_SETITEMHEIGHT
:
2742 return LISTBOX_SetItemHeight( hwnd
, descr
, wParam
, lParam
, TRUE
);
2744 case LB_ITEMFROMPOINT
:
2749 pt
.x
= LOWORD(lParam
);
2750 pt
.y
= HIWORD(lParam
);
2753 rect
.right
= descr
->width
;
2754 rect
.bottom
= descr
->height
;
2756 return MAKELONG( LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
),
2757 !PtInRect( &rect
, pt
) );
2761 case LB_SETCARETINDEX16
:
2763 case LB_SETCARETINDEX
:
2764 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2765 if (LISTBOX_SetCaretIndex( hwnd
, descr
, wParam
, !lParam
) == LB_ERR
)
2773 case LB_GETCARETINDEX16
:
2775 case LB_GETCARETINDEX
:
2776 return descr
->focus_item
;
2779 case LB_SETTOPINDEX16
:
2781 case LB_SETTOPINDEX
:
2782 return LISTBOX_SetTopItem( hwnd
, descr
, wParam
, TRUE
);
2785 case LB_SETCOLUMNWIDTH16
:
2787 case LB_SETCOLUMNWIDTH
:
2788 return LISTBOX_SetColumnWidth( hwnd
, descr
, wParam
);
2791 case LB_GETITEMRECT16
:
2794 ret
= LISTBOX_GetItemRect( descr
, (INT16
)wParam
, &rect
);
2795 CONV_RECT32TO16( &rect
, MapSL(lParam
) );
2800 case LB_GETITEMRECT
:
2801 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2804 case LB_FINDSTRING16
:
2805 wParam
= (INT
)(INT16
)wParam
;
2806 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2813 if(unicode
|| !HAS_STRINGS(descr
))
2814 textW
= (LPWSTR
)lParam
;
2817 LPSTR textA
= (LPSTR
)lParam
;
2818 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2819 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2820 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2822 ret
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, FALSE
);
2823 if(!unicode
&& HAS_STRINGS(descr
))
2824 HeapFree(GetProcessHeap(), 0, textW
);
2829 case LB_FINDSTRINGEXACT16
:
2830 wParam
= (INT
)(INT16
)wParam
;
2831 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2834 case LB_FINDSTRINGEXACT
:
2838 if(unicode
|| !HAS_STRINGS(descr
))
2839 textW
= (LPWSTR
)lParam
;
2842 LPSTR textA
= (LPSTR
)lParam
;
2843 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2844 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2845 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2847 ret
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, TRUE
);
2848 if(!unicode
&& HAS_STRINGS(descr
))
2849 HeapFree(GetProcessHeap(), 0, textW
);
2854 case LB_SELECTSTRING16
:
2855 wParam
= (INT
)(INT16
)wParam
;
2856 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2859 case LB_SELECTSTRING
:
2864 if(HAS_STRINGS(descr
))
2865 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2866 debugstr_a((LPSTR
)lParam
));
2867 if(unicode
|| !HAS_STRINGS(descr
))
2868 textW
= (LPWSTR
)lParam
;
2871 LPSTR textA
= (LPSTR
)lParam
;
2872 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2873 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2874 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2876 index
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, FALSE
);
2877 if(!unicode
&& HAS_STRINGS(descr
))
2878 HeapFree(GetProcessHeap(), 0, textW
);
2879 if (index
!= LB_ERR
)
2881 LISTBOX_MoveCaret( hwnd
, descr
, index
, TRUE
);
2882 LISTBOX_SetSelection( hwnd
, descr
, index
, TRUE
, FALSE
);
2889 wParam
= (INT
)(INT16
)wParam
;
2893 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2895 return descr
->items
[wParam
].selected
;
2899 lParam
= (INT
)(INT16
)lParam
;
2903 return LISTBOX_SetSelection( hwnd
, descr
, lParam
, wParam
, FALSE
);
2906 case LB_SETCURSEL16
:
2907 wParam
= (INT
)(INT16
)wParam
;
2911 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2912 LISTBOX_SetCaretIndex( hwnd
, descr
, wParam
, TRUE
);
2913 return LISTBOX_SetSelection( hwnd
, descr
, wParam
, TRUE
, FALSE
);
2916 case LB_GETSELCOUNT16
:
2918 case LB_GETSELCOUNT
:
2919 return LISTBOX_GetSelCount( descr
);
2922 case LB_GETSELITEMS16
:
2923 return LISTBOX_GetSelItems16( descr
, wParam
, (LPINT16
)MapSL(lParam
) );
2926 case LB_GETSELITEMS
:
2927 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2930 case LB_SELITEMRANGE16
:
2932 case LB_SELITEMRANGE
:
2933 if (LOWORD(lParam
) <= HIWORD(lParam
))
2934 return LISTBOX_SelectItemRange( hwnd
, descr
, LOWORD(lParam
),
2935 HIWORD(lParam
), wParam
);
2937 return LISTBOX_SelectItemRange( hwnd
, descr
, HIWORD(lParam
),
2938 LOWORD(lParam
), wParam
);
2941 case LB_SELITEMRANGEEX16
:
2943 case LB_SELITEMRANGEEX
:
2944 if ((INT
)lParam
>= (INT
)wParam
)
2945 return LISTBOX_SelectItemRange( hwnd
, descr
, wParam
, lParam
, TRUE
);
2947 return LISTBOX_SelectItemRange( hwnd
, descr
, lParam
, wParam
, FALSE
);
2950 case LB_GETHORIZONTALEXTENT16
:
2952 case LB_GETHORIZONTALEXTENT
:
2953 return descr
->horz_extent
;
2956 case LB_SETHORIZONTALEXTENT16
:
2958 case LB_SETHORIZONTALEXTENT
:
2959 return LISTBOX_SetHorizontalExtent( hwnd
, descr
, wParam
);
2962 case LB_GETANCHORINDEX16
:
2964 case LB_GETANCHORINDEX
:
2965 return descr
->anchor_item
;
2968 case LB_SETANCHORINDEX16
:
2969 wParam
= (INT
)(INT16
)wParam
;
2972 case LB_SETANCHORINDEX
:
2973 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2975 descr
->anchor_item
= (INT
)wParam
;
2980 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2981 * be set automatically (this is different in Win32) */
2982 if (wParam
& DDL_DRIVES
) wParam
|= DDL_EXCLUSIVE
;
2983 lParam
= (LPARAM
)MapSL(lParam
);
2991 textW
= (LPWSTR
)lParam
;
2994 LPSTR textA
= (LPSTR
)lParam
;
2995 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2996 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2997 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2999 ret
= LISTBOX_Directory( hwnd
, descr
, wParam
, textW
, msg
== LB_DIR
);
3001 HeapFree(GetProcessHeap(), 0, textW
);
3006 return descr
->locale
;
3009 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
3012 case LB_INITSTORAGE
:
3013 return LISTBOX_InitStorage( hwnd
, descr
, wParam
);
3016 return LISTBOX_SetCount( hwnd
, descr
, (INT
)wParam
);
3019 case LB_SETTABSTOPS16
:
3020 return LISTBOX_SetTabStops( hwnd
, descr
, (INT
)(INT16
)wParam
, MapSL(lParam
), TRUE
);
3023 case LB_SETTABSTOPS
:
3024 return LISTBOX_SetTabStops( hwnd
, descr
, wParam
, (LPINT
)lParam
, FALSE
);
3030 if (descr
->caret_on
)
3032 descr
->caret_on
= TRUE
;
3033 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3034 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
3041 if (!descr
->caret_on
)
3043 descr
->caret_on
= FALSE
;
3044 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3045 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
3049 return LISTBOX_Destroy( hwnd
, descr
);
3052 InvalidateRect( hwnd
, NULL
, TRUE
);
3056 LISTBOX_SetRedraw( hwnd
, descr
, wParam
!= 0 );
3060 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
3065 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( hwnd
, &ps
);
3066 ret
= LISTBOX_Paint( hwnd
, descr
, hdc
);
3067 if( !wParam
) EndPaint( hwnd
, &ps
);
3071 LISTBOX_UpdateSize( hwnd
, descr
);
3074 return (LRESULT
)descr
->font
;
3076 LISTBOX_SetFont( hwnd
, descr
, (HFONT
)wParam
);
3077 if (lParam
) InvalidateRect( hwnd
, 0, TRUE
);
3080 descr
->in_focus
= TRUE
;
3081 descr
->caret_on
= TRUE
;
3082 if (descr
->focus_item
!= -1)
3083 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
3084 SEND_NOTIFICATION( hwnd
, descr
, LBN_SETFOCUS
);
3087 descr
->in_focus
= FALSE
;
3088 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
3089 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
3090 SEND_NOTIFICATION( hwnd
, descr
, LBN_KILLFOCUS
);
3093 return LISTBOX_HandleHScroll( hwnd
, descr
, wParam
);
3095 return LISTBOX_HandleVScroll( hwnd
, descr
, wParam
);
3097 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
3098 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
3099 return LISTBOX_HandleMouseWheel( hwnd
, descr
, wParam
);
3100 case WM_LBUTTONDOWN
:
3101 return LISTBOX_HandleLButtonDown( hwnd
, descr
, wParam
,
3102 (INT16
)LOWORD(lParam
),
3103 (INT16
)HIWORD(lParam
) );
3104 case WM_LBUTTONDBLCLK
:
3105 if (descr
->style
& LBS_NOTIFY
)
3106 SEND_NOTIFICATION( hwnd
, descr
, LBN_DBLCLK
);
3109 if (GetCapture() == hwnd
)
3110 LISTBOX_HandleMouseMove( hwnd
, descr
, (INT16
)LOWORD(lParam
),
3111 (INT16
)HIWORD(lParam
) );
3114 return LISTBOX_HandleLButtonUp( hwnd
, descr
);
3116 return LISTBOX_HandleKeyDown( hwnd
, descr
, wParam
);
3121 charW
= (WCHAR
)wParam
;
3124 CHAR charA
= (CHAR
)wParam
;
3125 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
3127 return LISTBOX_HandleChar( hwnd
, descr
, charW
);
3130 return LISTBOX_HandleSystemTimer( hwnd
, descr
);
3132 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3135 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3136 wParam
, (LPARAM
)hwnd
);
3137 TRACE("hbrush = %p\n", hbrush
);
3139 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3142 GetClientRect(hwnd
, &rect
);
3143 FillRect((HDC
)wParam
, &rect
, hbrush
);
3149 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3150 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3154 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3155 WARN("[%p]: unknown msg %04x wp %08x lp %08lx\n",
3156 hwnd
, msg
, wParam
, lParam
);
3157 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3158 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3163 /***********************************************************************
3166 * This is just a wrapper for the real wndproc, it only does window locking
3169 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3171 if (!IsWindow(hwnd
)) return 0;
3172 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3175 /***********************************************************************
3178 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3180 if (!IsWindow(hwnd
)) return 0;
3181 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);
3184 /***********************************************************************
3185 * ComboLBWndProc_common
3187 * The real combo listbox wndproc
3189 static LRESULT WINAPI
ComboLBWndProc_common( HWND hwnd
, UINT msg
,
3190 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
3196 if (!(descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 )))
3198 if (msg
== WM_CREATE
)
3200 CREATESTRUCTA
*lpcs
= (CREATESTRUCTA
*)lParam
;
3201 TRACE_(combo
)("\tpassed parent handle = %p\n",lpcs
->lpCreateParams
);
3202 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
3203 return LISTBOX_Create( hwnd
, lphc
);
3205 /* Ignore all other messages before we get a WM_CREATE */
3206 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3207 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3210 TRACE_(combo
)("[%p]: msg %s wp %08x lp %08lx\n",
3211 hwnd
, SPY_GetMsgName(msg
, hwnd
), wParam
, lParam
);
3213 if ((lphc
= descr
->lphc
) != NULL
)
3218 if ( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) )
3224 mousePos
.x
= (INT16
)LOWORD(lParam
);
3225 mousePos
.y
= (INT16
)HIWORD(lParam
);
3228 * If we are in a dropdown combobox, we simulate that
3229 * the mouse is captured to show the tracking of the item.
3231 GetClientRect(hwnd
, &clientRect
);
3233 if (PtInRect( &clientRect
, mousePos
))
3235 captured
= descr
->captured
;
3236 descr
->captured
= TRUE
;
3238 LISTBOX_HandleMouseMove( hwnd
, descr
,
3239 mousePos
.x
, mousePos
.y
);
3241 descr
->captured
= captured
;
3246 LISTBOX_HandleMouseMove( hwnd
, descr
,
3247 mousePos
.x
, mousePos
.y
);
3261 * If the mouse button "up" is not in the listbox,
3262 * we make sure there is no selection by re-selecting the
3263 * item that was selected when the listbox was made visible.
3265 mousePos
.x
= (INT16
)LOWORD(lParam
);
3266 mousePos
.y
= (INT16
)HIWORD(lParam
);
3268 GetClientRect(hwnd
, &clientRect
);
3271 * When the user clicks outside the combobox and the focus
3272 * is lost, the owning combobox will send a fake buttonup with
3273 * 0xFFFFFFF as the mouse location, we must also revert the
3274 * selection to the original selection.
3276 if ( (lParam
== (LPARAM
)-1) ||
3277 (!PtInRect( &clientRect
, mousePos
)) )
3279 LISTBOX_MoveCaret( hwnd
, descr
, lphc
->droppedIndex
, FALSE
);
3282 return LISTBOX_HandleLButtonUp( hwnd
, descr
);
3283 case WM_LBUTTONDBLCLK
:
3284 case WM_LBUTTONDOWN
:
3285 return LISTBOX_HandleLButtonDownCombo(hwnd
, descr
, msg
, wParam
,
3286 (INT16
)LOWORD(lParam
),
3287 (INT16
)HIWORD(lParam
) );
3291 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3293 /* for some reason(?) Windows makes it possible to
3294 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3296 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3297 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3298 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3300 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3304 return LISTBOX_HandleKeyDown( hwnd
, descr
, wParam
);
3307 case LB_SETCURSEL16
:
3310 lRet
= unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3311 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3312 lRet
=(lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
3315 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3321 /* default handling: call listbox wnd proc */
3322 lRet
= unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3323 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3325 TRACE_(combo
)("\t default on msg [%04x]\n", (UINT16
)msg
);
3330 /***********************************************************************
3333 * NOTE: in Windows, winproc address of the ComboLBox is the same
3334 * as that of the Listbox.
3336 LRESULT WINAPI
ComboLBWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3338 if (!IsWindow(hwnd
)) return 0;
3339 return ComboLBWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3342 /***********************************************************************
3345 LRESULT WINAPI
ComboLBWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3347 if (!IsWindow(hwnd
)) return 0;
3348 return ComboLBWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);
3351 /***********************************************************************
3355 GetListBoxInfo(HWND hwnd
)