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
25 /* Start of hack section -------------------------------- */
27 typedef short *LPINT16
;
29 BOOL
is_old_app(HWND hwnd
)
34 #define WM_LBTRACKPOINT 0x0131
35 #define WS_EX_DRAGDETECT 0x00000002L
36 #define WM_BEGINDRAG 0x022C
38 UINT STDCALL
SetSystemTimer(HWND
,UINT_PTR
,UINT
,TIMERPROC
);
39 BOOL STDCALL
KillSystemTimer(HWND
,UINT_PTR
);
41 /* End of hack section -------------------------------- */
47 * Probably needs improvement:
51 /* Items array granularity */
52 #define LB_ARRAY_GRANULARITY 16
54 /* Scrolling timeout in ms */
55 #define LB_SCROLL_TIMEOUT 50
57 /* Listbox system timer id */
60 /* flag listbox changed while setredraw false - internal style */
61 #define LBS_DISPLAYCHANGED 0x80000000
66 LPWSTR str
; /* Item text */
67 BOOL selected
; /* Is item selected? */
68 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
69 DWORD data
; /* User data */
72 /* Listbox structure */
75 HWND self
; /* Our own window handle */
76 HWND owner
; /* Owner window to send notifications to */
77 UINT style
; /* Window style */
78 INT width
; /* Window width */
79 INT height
; /* Window height */
80 LB_ITEMDATA
*items
; /* Array of items */
81 INT nb_items
; /* Number of items */
82 INT top_item
; /* Top visible item */
83 INT selected_item
; /* Selected item */
84 INT focus_item
; /* Item that has the focus */
85 INT anchor_item
; /* Anchor item for extended selection */
86 INT item_height
; /* Default item height */
87 INT page_size
; /* Items per listbox page */
88 INT column_width
; /* Column width for multi-column listboxes */
89 INT horz_extent
; /* Horizontal extent (0 if no hscroll) */
90 INT horz_pos
; /* Horizontal position */
91 INT nb_tabs
; /* Number of tabs in array */
92 INT
*tabs
; /* Array of tabs */
93 INT avg_char_width
; /* Average width of characters */
94 BOOL caret_on
; /* Is caret on? */
95 BOOL captured
; /* Is mouse captured? */
97 HFONT font
; /* Current font */
98 LCID locale
; /* Current locale for string comparisons */
99 LPHEADCOMBO lphc
; /* ComboLBox */
103 #define IS_OWNERDRAW(descr) \
104 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
106 #define HAS_STRINGS(descr) \
107 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
110 #define IS_MULTISELECT(descr) \
111 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
112 !((descr)->style & LBS_NOSEL))
114 #define SEND_NOTIFICATION(descr,code) \
115 (SendMessageW( (descr)->owner, WM_COMMAND, \
116 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
118 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
120 /* Current timer status */
130 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
132 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
133 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
135 static LRESULT
LISTBOX_GetItemRect( LB_DESCR
*descr
, INT index
, RECT
*rect
);
137 /*********************************************************************
138 * listbox class descriptor
140 const struct builtin_class_descr LISTBOX_builtin_class
=
143 L
"ListBox", /* name */
144 CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
145 (WNDPROC
)ListBoxWndProcW
, /* procW */
146 (WNDPROC
)ListBoxWndProcA
, /* procA */
147 sizeof(LB_DESCR
*), /* extra */
148 (LPCWSTR
) IDC_ARROW
, /* cursor */
151 "ListBox", /* name */
152 CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
153 ListBoxWndProcA
, /* procA */
154 ListBoxWndProcW
, /* procW */
155 sizeof(LB_DESCR
*), /* extra */
156 IDC_ARROW
, /* cursor */
162 /*********************************************************************
163 * combolbox class descriptor
165 const struct builtin_class_descr COMBOLBOX_builtin_class
=
168 L
"ComboLBox", /* name */
169 CS_DBLCLKS
| CS_SAVEBITS
, /* style */
170 (WNDPROC
)ListBoxWndProcW
, /* procW */
171 (WNDPROC
)ListBoxWndProcA
, /* procA */
172 sizeof(LB_DESCR
*), /* extra */
173 (LPCWSTR
) IDC_ARROW
, /* cursor */
176 "ComboLBox", /* name */
177 CS_DBLCLKS
| CS_SAVEBITS
, /* style */
178 ListBoxWndProcA
, /* procA */
179 ListBoxWndProcW
, /* procW */
180 sizeof(LB_DESCR
*), /* extra */
181 IDC_ARROW
, /* cursor */
187 /* check whether app is a Win 3.1 app */
188 inline static BOOL
is_old_app( HWND hwnd
)
190 return (GetExpWinVer16( GetWindowLongA(hwnd
,GWL_HINSTANCE
) ) & 0xFF00 ) == 0x0300;
195 /***********************************************************************
198 void LISTBOX_Dump( HWND hwnd
)
202 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 );
204 TRACE( "Listbox:\n" );
205 TRACE( "hwnd=%p descr=%08x items=%d top=%d\n",
206 hwnd
, (UINT
)descr
, descr
->nb_items
, descr
->top_item
);
207 for (i
= 0, item
= descr
->items
; i
< descr
->nb_items
; i
++, item
++)
209 TRACE( "%4d: %-40s %d %08lx %3d\n",
210 i
, debugstr_w(item
->str
), item
->selected
, item
->data
, item
->height
);
215 /***********************************************************************
216 * LISTBOX_GetCurrentPageSize
218 * Return the current page size
220 static INT
LISTBOX_GetCurrentPageSize( LB_DESCR
*descr
)
223 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
224 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
226 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
228 if (i
== descr
->top_item
) return 1;
229 else return i
- descr
->top_item
;
233 /***********************************************************************
234 * LISTBOX_GetMaxTopIndex
236 * Return the maximum possible index for the top of the listbox.
238 static INT
LISTBOX_GetMaxTopIndex( LB_DESCR
*descr
)
242 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
244 page
= descr
->height
;
245 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
246 if ((page
-= descr
->items
[max
].height
) < 0) break;
247 if (max
< descr
->nb_items
- 1) max
++;
249 else if (descr
->style
& LBS_MULTICOLUMN
)
251 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
252 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
253 max
= (max
- page
) * descr
->page_size
;
257 max
= descr
->nb_items
- descr
->page_size
;
259 if (max
< 0) max
= 0;
264 /***********************************************************************
265 * LISTBOX_UpdateScroll
267 * Update the scrollbars. Should be called whenever the content
268 * of the listbox changes.
270 static void LISTBOX_UpdateScroll( LB_DESCR
*descr
)
274 /* Check the listbox scroll bar flags individually before we call
275 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
276 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
277 scroll bar when we do not need one.
278 if (!(descr->style & WS_VSCROLL)) return;
281 /* It is important that we check descr->style, and not wnd->dwStyle,
282 for WS_VSCROLL, as the former is exactly the one passed in
283 argument to CreateWindow.
284 In Windows (and from now on in Wine :) a listbox created
285 with such a style (no WS_SCROLL) does not update
286 the scrollbar with listbox-related data, thus letting
287 the programmer use it for his/her own purposes. */
289 if (descr
->style
& LBS_NOREDRAW
) return;
290 info
.cbSize
= sizeof(info
);
292 if (descr
->style
& LBS_MULTICOLUMN
)
295 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
296 info
.nPos
= descr
->top_item
/ descr
->page_size
;
297 info
.nPage
= descr
->width
/ descr
->column_width
;
298 if (info
.nPage
< 1) info
.nPage
= 1;
299 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
300 if (descr
->style
& LBS_DISABLENOSCROLL
)
301 info
.fMask
|= SIF_DISABLENOSCROLL
;
302 if (descr
->style
& WS_HSCROLL
)
303 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
305 info
.fMask
= SIF_RANGE
;
306 if (descr
->style
& WS_VSCROLL
)
307 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
312 info
.nMax
= descr
->nb_items
- 1;
313 info
.nPos
= descr
->top_item
;
314 info
.nPage
= LISTBOX_GetCurrentPageSize( descr
);
315 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
316 if (descr
->style
& LBS_DISABLENOSCROLL
)
317 info
.fMask
|= SIF_DISABLENOSCROLL
;
318 if (descr
->style
& WS_VSCROLL
)
319 SetScrollInfo( descr
->self
, SB_VERT
, &info
, TRUE
);
321 if (descr
->horz_extent
)
324 info
.nMax
= descr
->horz_extent
- 1;
325 info
.nPos
= descr
->horz_pos
;
326 info
.nPage
= descr
->width
;
327 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
328 if (descr
->style
& LBS_DISABLENOSCROLL
)
329 info
.fMask
|= SIF_DISABLENOSCROLL
;
330 if (descr
->style
& WS_HSCROLL
)
331 SetScrollInfo( descr
->self
, SB_HORZ
, &info
, TRUE
);
337 /***********************************************************************
340 * Set the top item of the listbox, scrolling up or down if necessary.
342 static LRESULT
LISTBOX_SetTopItem( LB_DESCR
*descr
, INT index
,
345 INT max
= LISTBOX_GetMaxTopIndex( descr
);
346 if (index
> max
) index
= max
;
347 if (index
< 0) index
= 0;
348 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
349 if (descr
->top_item
== index
) return LB_OKAY
;
350 if (descr
->style
& LBS_MULTICOLUMN
)
352 INT diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
353 if (scroll
&& (abs(diff
) < descr
->width
))
354 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
355 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
363 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
367 if (index
> descr
->top_item
)
369 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
370 diff
-= descr
->items
[i
].height
;
374 for (i
= index
; i
< descr
->top_item
; i
++)
375 diff
+= descr
->items
[i
].height
;
379 diff
= (descr
->top_item
- index
) * descr
->item_height
;
381 if (abs(diff
) < descr
->height
)
382 ScrollWindowEx( descr
->self
, 0, diff
, NULL
, NULL
, 0, NULL
,
383 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
387 if (!scroll
) InvalidateRect( descr
->self
, NULL
, TRUE
);
388 descr
->top_item
= index
;
389 LISTBOX_UpdateScroll( descr
);
394 /***********************************************************************
397 * Update the page size. Should be called when the size of
398 * the client area or the item height changes.
400 static void LISTBOX_UpdatePage( LB_DESCR
*descr
)
404 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
406 if (page_size
== descr
->page_size
) return;
407 descr
->page_size
= page_size
;
408 if (descr
->style
& LBS_MULTICOLUMN
)
409 InvalidateRect( descr
->self
, NULL
, TRUE
);
410 LISTBOX_SetTopItem( descr
, descr
->top_item
, FALSE
);
414 /***********************************************************************
417 * Update the size of the listbox. Should be called when the size of
418 * the client area changes.
420 static void LISTBOX_UpdateSize( LB_DESCR
*descr
)
424 GetClientRect( descr
->self
, &rect
);
425 descr
->width
= rect
.right
- rect
.left
;
426 descr
->height
= rect
.bottom
- rect
.top
;
427 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !(descr
->style
& LBS_OWNERDRAWVARIABLE
))
432 GetWindowRect( descr
->self
, &rect
);
433 if(descr
->item_height
!= 0)
434 remaining
= descr
->height
% descr
->item_height
;
437 if ((descr
->height
> descr
->item_height
) && remaining
)
440 if (is_old_app(hwnd
))
441 { /* give a margin for error to 16 bits programs - if we need
442 less than the height of the nonclient area, round to the
443 *next* number of items */
444 int ncheight
= rect
.bottom
- rect
.top
- descr
->height
;
445 if ((descr
->item_height
- remaining
) <= ncheight
)
446 remaining
= remaining
- descr
->item_height
;
449 TRACE("[%p]: changing height %d -> %d\n",
450 descr
->self
, descr
->height
, descr
->height
- remaining
);
451 SetWindowPos( descr
->self
, 0, 0, 0, rect
.right
- rect
.left
,
452 rect
.bottom
- rect
.top
- remaining
,
453 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
457 TRACE("[%p]: new size = %d,%d\n", descr
->self
, descr
->width
, descr
->height
);
458 LISTBOX_UpdatePage( descr
);
459 LISTBOX_UpdateScroll( descr
);
461 /* Invalidate the focused item so it will be repainted correctly */
462 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
464 InvalidateRect( descr
->self
, &rect
, FALSE
);
469 /***********************************************************************
470 * LISTBOX_GetItemRect
472 * Get the rectangle enclosing an item, in listbox client coordinates.
473 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
475 static LRESULT
LISTBOX_GetItemRect( LB_DESCR
*descr
, INT index
, RECT
*rect
)
477 /* Index <= 0 is legal even on empty listboxes */
478 if (index
&& (index
>= descr
->nb_items
))
480 memset(rect
, 0, sizeof(*rect
));
481 SetLastError(ERROR_INVALID_INDEX
);
484 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
485 if (descr
->style
& LBS_MULTICOLUMN
)
487 INT col
= (index
/ descr
->page_size
) -
488 (descr
->top_item
/ descr
->page_size
);
489 rect
->left
+= col
* descr
->column_width
;
490 rect
->right
= rect
->left
+ descr
->column_width
;
491 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
492 rect
->bottom
= rect
->top
+ descr
->item_height
;
494 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
497 rect
->right
+= descr
->horz_pos
;
498 if ((index
>= 0) && (index
< descr
->nb_items
))
500 if (index
< descr
->top_item
)
502 for (i
= descr
->top_item
-1; i
>= index
; i
--)
503 rect
->top
-= descr
->items
[i
].height
;
507 for (i
= descr
->top_item
; i
< index
; i
++)
508 rect
->top
+= descr
->items
[i
].height
;
510 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
516 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
517 rect
->bottom
= rect
->top
+ descr
->item_height
;
518 rect
->right
+= descr
->horz_pos
;
521 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
522 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
526 /***********************************************************************
527 * LISTBOX_GetItemFromPoint
529 * Return the item nearest from point (x,y) (in client coordinates).
531 static INT
LISTBOX_GetItemFromPoint( LB_DESCR
*descr
, INT x
, INT y
)
533 INT index
= descr
->top_item
;
535 if (!descr
->nb_items
) return -1; /* No items */
536 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
541 while (index
< descr
->nb_items
)
543 if ((pos
+= descr
->items
[index
].height
) > y
) break;
552 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
556 else if (descr
->style
& LBS_MULTICOLUMN
)
558 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
559 if (y
>= 0) index
+= y
/ descr
->item_height
;
560 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
561 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
565 index
+= (y
/ descr
->item_height
);
567 if (index
< 0) return 0;
568 if (index
>= descr
->nb_items
) return -1;
573 /***********************************************************************
578 static void LISTBOX_PaintItem( LB_DESCR
*descr
, HDC hdc
,
579 const RECT
*rect
, INT index
, UINT action
, BOOL ignoreFocus
)
581 LB_ITEMDATA
*item
= NULL
;
582 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
584 if (IS_OWNERDRAW(descr
))
592 if (action
== ODA_FOCUS
)
593 DrawFocusRect( hdc
, rect
);
595 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
599 /* some programs mess with the clipping region when
600 drawing the item, *and* restore the previous region
601 after they are done, so a region has better to exist
602 else everything ends clipped */
603 GetClientRect(descr
->self
, &r
);
604 hrgn
= CreateRectRgnIndirect(&r
);
605 SelectClipRgn( hdc
, hrgn
);
606 DeleteObject( hrgn
);
608 dis
.CtlType
= ODT_LISTBOX
;
609 dis
.CtlID
= GetWindowLongPtrW( descr
->self
, GWLP_ID
);
610 dis
.hwndItem
= descr
->self
;
611 dis
.itemAction
= action
;
615 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
616 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
618 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
619 if (!IsWindowEnabled(descr
->self
)) dis
.itemState
|= ODS_DISABLED
;
620 dis
.itemData
= item
? item
->data
: 0;
622 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%ld,%ld-%ld,%ld\n",
623 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
624 dis
.itemState
, rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
625 SendMessageW(descr
->owner
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
629 COLORREF oldText
= 0, oldBk
= 0;
631 if (action
== ODA_FOCUS
)
633 DrawFocusRect( hdc
, rect
);
636 if (item
&& item
->selected
)
638 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
639 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
642 TRACE("[%p]: painting %d (%s) action=%02x rect=%ld,%ld-%ld,%ld\n",
643 descr
->self
, index
, item
? debugstr_w(item
->str
) : "", action
,
644 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
646 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
647 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
648 else if (!(descr
->style
& LBS_USETABSTOPS
))
649 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
650 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
651 strlenW(item
->str
), NULL
);
654 /* Output empty string to paint background in the full width. */
655 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
656 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
657 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
658 item
->str
, strlenW(item
->str
),
659 descr
->nb_tabs
, descr
->tabs
, 0);
661 if (item
&& item
->selected
)
663 SetBkColor( hdc
, oldBk
);
664 SetTextColor( hdc
, oldText
);
666 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
668 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
673 /***********************************************************************
676 * Change the redraw flag.
678 static void LISTBOX_SetRedraw( LB_DESCR
*descr
, BOOL on
)
682 if (!(descr
->style
& LBS_NOREDRAW
)) return;
683 descr
->style
&= ~LBS_NOREDRAW
;
684 if (descr
->style
& LBS_DISPLAYCHANGED
)
685 { /* page was changed while setredraw false, refresh automatically */
686 InvalidateRect(descr
->self
, NULL
, TRUE
);
687 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
688 { /* reset top of page if less than number of items/page */
689 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
690 if (descr
->top_item
< 0) descr
->top_item
= 0;
692 descr
->style
&= ~LBS_DISPLAYCHANGED
;
694 LISTBOX_UpdateScroll( descr
);
696 else descr
->style
|= LBS_NOREDRAW
;
700 /***********************************************************************
701 * LISTBOX_RepaintItem
703 * Repaint a single item synchronously.
705 static void LISTBOX_RepaintItem( LB_DESCR
*descr
, INT index
,
711 HBRUSH hbrush
, oldBrush
= 0;
713 /* Do not repaint the item if the item is not visible */
714 if (!IsWindowVisible(descr
->self
)) return;
715 if (descr
->style
& LBS_NOREDRAW
)
717 descr
->style
|= LBS_DISPLAYCHANGED
;
720 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
721 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
))) return;
722 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
723 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
724 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
725 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
726 if (!IsWindowEnabled(descr
->self
))
727 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
728 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
729 LISTBOX_PaintItem( descr
, hdc
, &rect
, index
, action
, FALSE
);
730 if (oldFont
) SelectObject( hdc
, oldFont
);
731 if (oldBrush
) SelectObject( hdc
, oldBrush
);
732 ReleaseDC( descr
->self
, hdc
);
736 /***********************************************************************
737 * LISTBOX_InitStorage
739 static LRESULT
LISTBOX_InitStorage( LB_DESCR
*descr
, INT nb_items
)
743 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
744 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
746 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
747 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
748 nb_items
* sizeof(LB_ITEMDATA
));
751 item
= HeapAlloc( GetProcessHeap(), 0,
752 nb_items
* sizeof(LB_ITEMDATA
));
757 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
765 /***********************************************************************
766 * LISTBOX_SetTabStops
768 static BOOL
LISTBOX_SetTabStops( LB_DESCR
*descr
, INT count
,
769 LPINT tabs
, BOOL short_ints
)
773 if (!(descr
->style
& LBS_USETABSTOPS
))
775 SetLastError(ERROR_LB_WITHOUT_TABSTOPS
);
779 HeapFree( GetProcessHeap(), 0, descr
->tabs
);
780 if (!(descr
->nb_tabs
= count
))
785 if (!(descr
->tabs
= HeapAlloc( GetProcessHeap(), 0,
786 descr
->nb_tabs
* sizeof(INT
) )))
792 LPINT16 p
= (LPINT16
)tabs
;
794 TRACE("[%p]: settabstops ", hwnd
);
795 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
796 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
797 if (TRACE_ON(listbox
)) TRACE("%hd ", descr
->tabs
[i
]);
799 if (TRACE_ON(listbox
)) TRACE("\n");
801 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
803 memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
806 /* convert into "dialog units"*/
807 for (i
= 0; i
< descr
->nb_tabs
; i
++)
808 descr
->tabs
[i
] = MulDiv(descr
->tabs
[i
], descr
->avg_char_width
, 4);
814 /***********************************************************************
817 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPWSTR buffer
, BOOL unicode
)
819 if ((index
< 0) || (index
>= descr
->nb_items
))
821 SetLastError(ERROR_INVALID_INDEX
);
824 if (HAS_STRINGS(descr
))
828 DWORD len
= strlenW(descr
->items
[index
].str
);
831 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[index
].str
, len
,
832 NULL
, 0, NULL
, NULL
);
835 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
839 strcpyW( buffer
, descr
->items
[index
].str
);
840 return strlenW(buffer
);
844 return WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1, (LPSTR
)buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
848 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
849 return sizeof(DWORD
);
853 static inline INT
LISTBOX_lstrcmpiW( LCID lcid
, LPCWSTR str1
, LPCWSTR str2
)
855 INT ret
= CompareStringW( lcid
, NORM_IGNORECASE
, str1
, -1, str2
, -1 );
856 if (ret
== CSTR_LESS_THAN
)
858 if (ret
== CSTR_EQUAL
)
860 if (ret
== CSTR_GREATER_THAN
)
865 /***********************************************************************
866 * LISTBOX_FindStringPos
868 * Find the nearest string located before a given string in sort order.
869 * If 'exact' is TRUE, return an error if we don't get an exact match.
871 static INT
LISTBOX_FindStringPos( LB_DESCR
*descr
, LPCWSTR str
, BOOL exact
)
873 INT index
, min
, max
, res
= -1;
875 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
877 max
= descr
->nb_items
;
880 index
= (min
+ max
) / 2;
881 if (HAS_STRINGS(descr
))
882 res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, descr
->items
[index
].str
);
885 COMPAREITEMSTRUCT cis
;
886 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
888 cis
.CtlType
= ODT_LISTBOX
;
890 cis
.hwndItem
= descr
->self
;
891 /* note that some application (MetaStock) expects the second item
892 * to be in the listbox */
894 cis
.itemData1
= (DWORD
)str
;
896 cis
.itemData2
= descr
->items
[index
].data
;
897 cis
.dwLocaleId
= descr
->locale
;
898 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
900 if (!res
) return index
;
901 if (res
< 0) max
= index
;
902 else min
= index
+ 1;
904 return exact
? -1 : max
;
907 /***********************************************************************
908 * LISTBOX_FindFileStrPos
910 * Find the nearest string located before a given string in directory
911 * sort order (i.e. first files, then directories, then drives).
913 static INT
LISTBOX_FindFileStrPos( LB_DESCR
*descr
, LPCWSTR str
)
915 INT min
, max
, res
= -1;
917 if (!HAS_STRINGS(descr
))
918 return LISTBOX_FindStringPos( descr
, str
, FALSE
);
920 max
= descr
->nb_items
;
923 INT index
= (min
+ max
) / 2;
924 LPCWSTR p
= descr
->items
[index
].str
;
925 if (*p
== '[') /* drive or directory */
927 if (*str
!= '[') res
= -1;
928 else if (p
[1] == '-') /* drive */
930 if (str
[1] == '-') res
= str
[2] - p
[2];
935 if (str
[1] == '-') res
= 1;
936 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
941 if (*str
== '[') res
= 1;
942 else res
= LISTBOX_lstrcmpiW( descr
->locale
, str
, p
);
944 if (!res
) return index
;
945 if (res
< 0) max
= index
;
946 else min
= index
+ 1;
952 /***********************************************************************
955 * Find the item beginning with a given string.
957 static INT
LISTBOX_FindString( LB_DESCR
*descr
, INT start
, LPCWSTR str
, BOOL exact
)
962 if (start
>= descr
->nb_items
) start
= -1;
963 item
= descr
->items
+ start
+ 1;
964 if (HAS_STRINGS(descr
))
966 if (!str
|| ! str
[0] ) return LB_ERR
;
969 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
970 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
971 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
972 if (!LISTBOX_lstrcmpiW( descr
->locale
, str
, item
->str
)) return i
;
976 /* Special case for drives and directories: ignore prefix */
977 #define CHECK_DRIVE(item) \
978 if ((item)->str[0] == '[') \
980 if (!strncmpiW( str, (item)->str+1, len )) return i; \
981 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
985 INT len
= strlenW(str
);
986 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
988 if (!strncmpiW( str
, item
->str
, len
)) return i
;
991 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
993 if (!strncmpiW( str
, item
->str
, len
)) return i
;
1001 if (exact
&& (descr
->style
& LBS_SORT
))
1002 /* If sorted, use a WM_COMPAREITEM binary search */
1003 return LISTBOX_FindStringPos( descr
, str
, TRUE
);
1005 /* Otherwise use a linear search */
1006 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
1007 if (item
->data
== (ULONG_PTR
)str
) return i
;
1008 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
1009 if (item
->data
== (ULONG_PTR
)str
) return i
;
1015 /***********************************************************************
1016 * LISTBOX_GetSelCount
1018 static LRESULT
LISTBOX_GetSelCount( LB_DESCR
*descr
)
1021 LB_ITEMDATA
*item
= descr
->items
;
1023 if (!(descr
->style
& LBS_MULTIPLESEL
) ||
1024 (descr
->style
& LBS_NOSEL
))
1026 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
1027 if (item
->selected
) count
++;
1033 /***********************************************************************
1034 * LISTBOX_GetSelItems16
1036 static LRESULT
LISTBOX_GetSelItems16( LB_DESCR
*descr
, INT16 max
, LPINT16 array
)
1039 LB_ITEMDATA
*item
= descr
->items
;
1041 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1042 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
1043 if (item
->selected
) array
[count
++] = (INT16
)i
;
1049 /***********************************************************************
1050 * LISTBOX_GetSelItems
1052 static LRESULT
LISTBOX_GetSelItems( LB_DESCR
*descr
, INT max
, LPINT array
)
1055 LB_ITEMDATA
*item
= descr
->items
;
1057 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1058 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
1059 if (item
->selected
) array
[count
++] = i
;
1064 /***********************************************************************
1067 static LRESULT
LISTBOX_Paint( LB_DESCR
*descr
, HDC hdc
)
1069 INT i
, col_pos
= descr
->page_size
- 1;
1071 RECT focusRect
= {-1, -1, -1, -1};
1073 HBRUSH hbrush
, oldBrush
= 0;
1075 if (descr
->style
& LBS_NOREDRAW
) return 0;
1077 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1078 if (descr
->style
& LBS_MULTICOLUMN
)
1079 rect
.right
= rect
.left
+ descr
->column_width
;
1080 else if (descr
->horz_pos
)
1082 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1083 rect
.right
+= descr
->horz_pos
;
1086 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1087 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
1088 (WPARAM
)hdc
, (LPARAM
)descr
->self
);
1089 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1090 if (!IsWindowEnabled(descr
->self
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1092 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1095 /* Special case for empty listbox: paint focus rect */
1096 rect
.bottom
= rect
.top
+ descr
->item_height
;
1097 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1098 &rect
, NULL
, 0, NULL
);
1099 LISTBOX_PaintItem( descr
, hdc
, &rect
, descr
->focus_item
,
1101 rect
.top
= rect
.bottom
;
1104 /* Paint all the item, regarding the selection
1105 Focus state will be painted after */
1107 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1109 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1110 rect
.bottom
= rect
.top
+ descr
->item_height
;
1112 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1114 if (i
== descr
->focus_item
)
1116 /* keep the focus rect, to paint the focus item after */
1117 focusRect
.left
= rect
.left
;
1118 focusRect
.right
= rect
.right
;
1119 focusRect
.top
= rect
.top
;
1120 focusRect
.bottom
= rect
.bottom
;
1122 LISTBOX_PaintItem( descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1123 rect
.top
= rect
.bottom
;
1125 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1127 if (!IS_OWNERDRAW(descr
))
1129 /* Clear the bottom of the column */
1130 if (rect
.top
< descr
->height
)
1132 rect
.bottom
= descr
->height
;
1133 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1134 &rect
, NULL
, 0, NULL
);
1138 /* Go to the next column */
1139 rect
.left
+= descr
->column_width
;
1140 rect
.right
+= descr
->column_width
;
1142 col_pos
= descr
->page_size
- 1;
1147 if (rect
.top
>= descr
->height
) break;
1151 /* Paint the focus item now */
1152 if (focusRect
.top
!= focusRect
.bottom
&&
1153 descr
->caret_on
&& descr
->in_focus
)
1154 LISTBOX_PaintItem( descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1156 if (!IS_OWNERDRAW(descr
))
1158 /* Clear the remainder of the client area */
1159 if (rect
.top
< descr
->height
)
1161 rect
.bottom
= descr
->height
;
1162 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1163 &rect
, NULL
, 0, NULL
);
1165 if (rect
.right
< descr
->width
)
1167 rect
.left
= rect
.right
;
1168 rect
.right
= descr
->width
;
1170 rect
.bottom
= descr
->height
;
1171 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1172 &rect
, NULL
, 0, NULL
);
1175 if (oldFont
) SelectObject( hdc
, oldFont
);
1176 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1181 /***********************************************************************
1182 * LISTBOX_InvalidateItems
1184 * Invalidate all items from a given item. If the specified item is not
1185 * visible, nothing happens.
1187 static void LISTBOX_InvalidateItems( LB_DESCR
*descr
, INT index
)
1191 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1193 if (descr
->style
& LBS_NOREDRAW
)
1195 descr
->style
|= LBS_DISPLAYCHANGED
;
1198 rect
.bottom
= descr
->height
;
1199 InvalidateRect( descr
->self
, &rect
, TRUE
);
1200 if (descr
->style
& LBS_MULTICOLUMN
)
1202 /* Repaint the other columns */
1203 rect
.left
= rect
.right
;
1204 rect
.right
= descr
->width
;
1206 InvalidateRect( descr
->self
, &rect
, TRUE
);
1211 static void LISTBOX_InvalidateItemRect( LB_DESCR
*descr
, INT index
)
1215 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1216 InvalidateRect( descr
->self
, &rect
, TRUE
);
1219 /***********************************************************************
1220 * LISTBOX_GetItemHeight
1222 static LRESULT
LISTBOX_GetItemHeight( LB_DESCR
*descr
, INT index
)
1224 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1226 if ((index
< 0) || (index
>= descr
->nb_items
))
1228 SetLastError(ERROR_INVALID_INDEX
);
1231 return descr
->items
[index
].height
;
1233 else return descr
->item_height
;
1237 /***********************************************************************
1238 * LISTBOX_SetItemHeight
1240 static LRESULT
LISTBOX_SetItemHeight( LB_DESCR
*descr
, INT index
,
1241 INT height
, BOOL repaint
)
1243 if (!height
) height
= 1;
1245 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1247 if ((index
< 0) || (index
>= descr
->nb_items
))
1249 SetLastError(ERROR_INVALID_INDEX
);
1252 TRACE("[%p]: item %d height = %d\n", descr
->self
, index
, height
);
1253 descr
->items
[index
].height
= height
;
1254 LISTBOX_UpdateScroll( descr
);
1256 LISTBOX_InvalidateItems( descr
, index
);
1258 else if (height
!= descr
->item_height
)
1260 TRACE("[%p]: new height = %d\n", descr
->self
, height
);
1261 descr
->item_height
= height
;
1262 LISTBOX_UpdatePage( descr
);
1263 LISTBOX_UpdateScroll( descr
);
1265 InvalidateRect( descr
->self
, 0, TRUE
);
1271 /***********************************************************************
1272 * LISTBOX_SetHorizontalPos
1274 static void LISTBOX_SetHorizontalPos( LB_DESCR
*descr
, INT pos
)
1278 if (pos
> descr
->horz_extent
- descr
->width
)
1279 pos
= descr
->horz_extent
- descr
->width
;
1280 if (pos
< 0) pos
= 0;
1281 if (!(diff
= descr
->horz_pos
- pos
)) return;
1282 TRACE("[%p]: new horz pos = %d\n", descr
->self
, pos
);
1283 descr
->horz_pos
= pos
;
1284 LISTBOX_UpdateScroll( descr
);
1285 if (abs(diff
) < descr
->width
)
1288 /* Invalidate the focused item so it will be repainted correctly */
1289 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1290 InvalidateRect( descr
->self
, &rect
, TRUE
);
1291 ScrollWindowEx( descr
->self
, diff
, 0, NULL
, NULL
, 0, NULL
,
1292 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1295 InvalidateRect( descr
->self
, NULL
, TRUE
);
1299 /***********************************************************************
1300 * LISTBOX_SetHorizontalExtent
1302 static LRESULT
LISTBOX_SetHorizontalExtent( LB_DESCR
*descr
,
1305 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1307 if (extent
<= 0) extent
= 1;
1308 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1309 TRACE("[%p]: new horz extent = %d\n", descr
->self
, extent
);
1310 descr
->horz_extent
= extent
;
1311 if (descr
->horz_pos
> extent
- descr
->width
)
1312 LISTBOX_SetHorizontalPos( descr
, extent
- descr
->width
);
1314 LISTBOX_UpdateScroll( descr
);
1319 /***********************************************************************
1320 * LISTBOX_SetColumnWidth
1322 static LRESULT
LISTBOX_SetColumnWidth( LB_DESCR
*descr
, INT width
)
1324 if (width
== descr
->column_width
) return LB_OKAY
;
1325 TRACE("[%p]: new column width = %d\n", descr
->self
, width
);
1326 descr
->column_width
= width
;
1327 LISTBOX_UpdatePage( descr
);
1332 /***********************************************************************
1335 * Returns the item height.
1337 static INT
LISTBOX_SetFont( LB_DESCR
*descr
, HFONT font
)
1341 const char *alphabet
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1346 if (!(hdc
= GetDCEx( descr
->self
, 0, DCX_CACHE
)))
1348 ERR("unable to get DC.\n" );
1351 if (font
) oldFont
= SelectObject( hdc
, font
);
1352 GetTextExtentPointA( hdc
, alphabet
, 52, &sz
);
1353 if (oldFont
) SelectObject( hdc
, oldFont
);
1354 ReleaseDC( descr
->self
, hdc
);
1356 descr
->avg_char_width
= (sz
.cx
/ 26 + 1) / 2;
1357 if (!IS_OWNERDRAW(descr
))
1358 LISTBOX_SetItemHeight( descr
, 0, sz
.cy
, FALSE
);
1363 /***********************************************************************
1364 * LISTBOX_MakeItemVisible
1366 * Make sure that a given item is partially or fully visible.
1368 static void LISTBOX_MakeItemVisible( LB_DESCR
*descr
, INT index
,
1373 if (index
<= descr
->top_item
) top
= index
;
1374 else if (descr
->style
& LBS_MULTICOLUMN
)
1376 INT cols
= descr
->width
;
1377 if (!fully
) cols
+= descr
->column_width
- 1;
1378 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1380 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1381 top
= index
- descr
->page_size
* (cols
- 1);
1383 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1385 INT height
= fully
? descr
->items
[index
].height
: 1;
1386 for (top
= index
; top
> descr
->top_item
; top
--)
1387 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1391 if (index
< descr
->top_item
+ descr
->page_size
) return;
1392 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1393 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1394 top
= index
- descr
->page_size
+ 1;
1396 LISTBOX_SetTopItem( descr
, top
, TRUE
);
1399 /***********************************************************************
1400 * LISTBOX_SetCaretIndex
1403 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1406 static LRESULT
LISTBOX_SetCaretIndex( LB_DESCR
*descr
, INT index
,
1407 BOOL fully_visible
)
1409 INT oldfocus
= descr
->focus_item
;
1411 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1412 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1413 if (index
== oldfocus
) return LB_OKAY
;
1414 descr
->focus_item
= index
;
1415 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1416 LISTBOX_RepaintItem( descr
, oldfocus
, ODA_FOCUS
);
1418 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1419 if (descr
->caret_on
&& (descr
->in_focus
))
1420 LISTBOX_RepaintItem( descr
, index
, ODA_FOCUS
);
1426 /***********************************************************************
1427 * LISTBOX_SelectItemRange
1429 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1431 static LRESULT
LISTBOX_SelectItemRange( LB_DESCR
*descr
, INT first
,
1436 /* A few sanity checks */
1438 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1439 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1440 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1441 if (last
== -1) last
= descr
->nb_items
- 1;
1442 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1443 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1445 if (on
) /* Turn selection on */
1447 for (i
= first
; i
<= last
; i
++)
1449 if (descr
->items
[i
].selected
) continue;
1450 descr
->items
[i
].selected
= TRUE
;
1451 LISTBOX_InvalidateItemRect(descr
, i
);
1454 else /* Turn selection off */
1456 for (i
= first
; i
<= last
; i
++)
1458 if (!descr
->items
[i
].selected
) continue;
1459 descr
->items
[i
].selected
= FALSE
;
1460 LISTBOX_InvalidateItemRect(descr
, i
);
1466 /***********************************************************************
1467 * LISTBOX_SetSelection
1469 static LRESULT
LISTBOX_SetSelection( LB_DESCR
*descr
, INT index
,
1470 BOOL on
, BOOL send_notify
)
1472 TRACE( "index=%d notify=%s\n", index
, send_notify
? "YES" : "NO" );
1474 if (descr
->style
& LBS_NOSEL
)
1476 descr
->selected_item
= index
;
1479 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1480 if (descr
->style
& LBS_MULTIPLESEL
)
1482 if (index
== -1) /* Select all items */
1483 return LISTBOX_SelectItemRange( descr
, 0, -1, on
);
1484 else /* Only one item */
1485 return LISTBOX_SelectItemRange( descr
, index
, index
, on
);
1489 INT oldsel
= descr
->selected_item
;
1490 if (index
== oldsel
) return LB_OKAY
;
1491 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1492 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1493 descr
->selected_item
= index
;
1494 if (oldsel
!= -1) LISTBOX_RepaintItem( descr
, oldsel
, ODA_SELECT
);
1495 if (index
!= -1) LISTBOX_RepaintItem( descr
, index
, ODA_SELECT
);
1496 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( descr
,
1497 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1499 if( descr
->lphc
) /* set selection change flag for parent combo */
1500 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1506 /***********************************************************************
1509 * Change the caret position and extend the selection to the new caret.
1511 static void LISTBOX_MoveCaret( LB_DESCR
*descr
, INT index
,
1512 BOOL fully_visible
)
1514 INT oldfocus
= descr
->focus_item
;
1516 if ((index
< 0) || (index
>= descr
->nb_items
))
1519 /* Important, repaint needs to be done in this order if
1520 you want to mimic Windows behavior:
1521 1. Remove the focus and paint the item
1522 2. Remove the selection and paint the item(s)
1523 3. Set the selection and repaint the item(s)
1524 4. Set the focus to 'index' and repaint the item */
1526 /* 1. remove the focus and repaint the item */
1527 descr
->focus_item
= -1;
1528 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1529 LISTBOX_RepaintItem( descr
, oldfocus
, ODA_FOCUS
);
1531 /* 2. then turn off the previous selection */
1532 /* 3. repaint the new selected item */
1533 if (descr
->style
& LBS_EXTENDEDSEL
)
1535 if (descr
->anchor_item
!= -1)
1537 INT first
= min( index
, descr
->anchor_item
);
1538 INT last
= max( index
, descr
->anchor_item
);
1540 LISTBOX_SelectItemRange( descr
, 0, first
- 1, FALSE
);
1541 LISTBOX_SelectItemRange( descr
, last
+ 1, -1, FALSE
);
1542 LISTBOX_SelectItemRange( descr
, first
, last
, TRUE
);
1545 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1547 /* Set selection to new caret item */
1548 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
1551 /* 4. repaint the new item with the focus */
1552 descr
->focus_item
= index
;
1553 LISTBOX_MakeItemVisible( descr
, index
, fully_visible
);
1554 if (descr
->caret_on
&& (descr
->in_focus
))
1555 LISTBOX_RepaintItem( descr
, index
, ODA_FOCUS
);
1559 /***********************************************************************
1560 * LISTBOX_InsertItem
1562 static LRESULT
LISTBOX_InsertItem( LB_DESCR
*descr
, INT index
,
1563 LPWSTR str
, DWORD data
)
1567 INT oldfocus
= descr
->focus_item
;
1569 if (index
== -1) index
= descr
->nb_items
;
1570 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1571 if (!descr
->items
) max_items
= 0;
1572 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1573 if (descr
->nb_items
== max_items
)
1575 /* We need to grow the array */
1576 max_items
+= LB_ARRAY_GRANULARITY
;
1578 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1579 max_items
* sizeof(LB_ITEMDATA
) );
1581 item
= HeapAlloc( GetProcessHeap(), 0,
1582 max_items
* sizeof(LB_ITEMDATA
) );
1585 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1588 descr
->items
= item
;
1591 /* Insert the item structure */
1593 item
= &descr
->items
[index
];
1594 if (index
< descr
->nb_items
)
1595 RtlMoveMemory( item
+ 1, item
,
1596 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1600 item
->selected
= FALSE
;
1603 /* Get item height */
1605 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1607 MEASUREITEMSTRUCT mis
;
1608 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1610 mis
.CtlType
= ODT_LISTBOX
;
1613 mis
.itemData
= descr
->items
[index
].data
;
1614 mis
.itemHeight
= descr
->item_height
;
1615 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1616 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1617 TRACE("[%p]: measure item %d (%s) = %d\n",
1618 descr
->self
, index
, str
? debugstr_w(str
) : "", item
->height
);
1621 /* Repaint the items */
1623 LISTBOX_UpdateScroll( descr
);
1624 LISTBOX_InvalidateItems( descr
, index
);
1626 /* Move selection and focused item */
1627 /* If listbox was empty, set focus to the first item */
1628 if (descr
->nb_items
== 1)
1629 LISTBOX_SetCaretIndex( descr
, 0, FALSE
);
1630 /* single select don't change selection index in win31 */
1631 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1633 descr
->selected_item
++;
1634 LISTBOX_SetSelection( descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1638 if (index
<= descr
->selected_item
)
1640 descr
->selected_item
++;
1641 descr
->focus_item
= oldfocus
; /* focus not changed */
1648 /***********************************************************************
1649 * LISTBOX_InsertString
1651 static LRESULT
LISTBOX_InsertString( LB_DESCR
*descr
, INT index
,
1654 LPWSTR new_str
= NULL
;
1658 if (HAS_STRINGS(descr
))
1660 static const WCHAR empty_stringW
[] = { 0 };
1661 if (!str
) str
= empty_stringW
;
1662 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1664 SEND_NOTIFICATION( descr
, LBN_ERRSPACE
);
1667 strcpyW(new_str
, str
);
1669 else data
= (DWORD
)str
;
1671 if (index
== -1) index
= descr
->nb_items
;
1672 if ((ret
= LISTBOX_InsertItem( descr
, index
, new_str
, data
)) != 0)
1674 HeapFree( GetProcessHeap(), 0, new_str
);
1678 TRACE("[%p]: added item %d %s\n",
1679 descr
->self
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1684 /***********************************************************************
1685 * LISTBOX_DeleteItem
1687 * Delete the content of an item. 'index' must be a valid index.
1689 static void LISTBOX_DeleteItem( LB_DESCR
*descr
, INT index
)
1691 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1692 * while Win95 sends it for all items with user data.
1693 * It's probably better to send it too often than not
1694 * often enough, so this is what we do here.
1696 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1698 DELETEITEMSTRUCT dis
;
1699 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
1701 dis
.CtlType
= ODT_LISTBOX
;
1704 dis
.hwndItem
= descr
->self
;
1705 dis
.itemData
= descr
->items
[index
].data
;
1706 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1708 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1709 HeapFree( GetProcessHeap(), 0, descr
->items
[index
].str
);
1713 /***********************************************************************
1714 * LISTBOX_RemoveItem
1716 * Remove an item from the listbox and delete its content.
1718 static LRESULT
LISTBOX_RemoveItem( LB_DESCR
*descr
, INT index
)
1723 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1725 /* We need to invalidate the original rect instead of the updated one. */
1726 LISTBOX_InvalidateItems( descr
, index
);
1728 LISTBOX_DeleteItem( descr
, index
);
1730 /* Remove the item */
1732 item
= &descr
->items
[index
];
1733 if (index
< descr
->nb_items
-1)
1734 RtlMoveMemory( item
, item
+ 1,
1735 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1737 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1739 /* Shrink the item array if possible */
1741 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1742 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1744 max_items
-= LB_ARRAY_GRANULARITY
;
1745 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1746 max_items
* sizeof(LB_ITEMDATA
) );
1747 if (item
) descr
->items
= item
;
1749 /* Repaint the items */
1751 LISTBOX_UpdateScroll( descr
);
1752 /* if we removed the scrollbar, reset the top of the list
1753 (correct for owner-drawn ???) */
1754 if (descr
->nb_items
== descr
->page_size
)
1755 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1757 /* Move selection and focused item */
1758 if (!IS_MULTISELECT(descr
))
1760 if (index
== descr
->selected_item
)
1761 descr
->selected_item
= -1;
1762 else if (index
< descr
->selected_item
)
1764 descr
->selected_item
--;
1765 if (ISWIN31
) /* win 31 do not change the selected item number */
1766 LISTBOX_SetSelection( descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1770 if (descr
->focus_item
>= descr
->nb_items
)
1772 descr
->focus_item
= descr
->nb_items
- 1;
1773 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1779 /***********************************************************************
1780 * LISTBOX_ResetContent
1782 static void LISTBOX_ResetContent( LB_DESCR
*descr
)
1786 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( descr
, i
);
1787 HeapFree( GetProcessHeap(), 0, descr
->items
);
1788 descr
->nb_items
= 0;
1789 descr
->top_item
= 0;
1790 descr
->selected_item
= -1;
1791 descr
->focus_item
= 0;
1792 descr
->anchor_item
= -1;
1793 descr
->items
= NULL
;
1797 /***********************************************************************
1800 static LRESULT
LISTBOX_SetCount( LB_DESCR
*descr
, INT count
)
1804 if (HAS_STRINGS(descr
))
1806 SetLastError(ERROR_SETCOUNT_ON_BAD_LB
);
1810 /* FIXME: this is far from optimal... */
1811 if (count
> descr
->nb_items
)
1813 while (count
> descr
->nb_items
)
1814 if ((ret
= LISTBOX_InsertString( descr
, -1, 0 )) < 0)
1817 else if (count
< descr
->nb_items
)
1819 while (count
< descr
->nb_items
)
1820 if ((ret
= LISTBOX_RemoveItem( descr
, -1 )) < 0)
1827 /***********************************************************************
1830 static LRESULT
LISTBOX_Directory( LB_DESCR
*descr
, UINT attrib
,
1831 LPCWSTR filespec
, BOOL long_names
)
1834 LRESULT ret
= LB_OKAY
;
1835 WIN32_FIND_DATAW entry
;
1838 /* don't scan directory if we just want drives exclusively */
1839 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1840 /* scan directory */
1841 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1843 int le
= GetLastError();
1844 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1851 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1853 static const WCHAR bracketW
[] = { ']',0 };
1854 static const WCHAR dotW
[] = { '.',0 };
1855 if (!(attrib
& DDL_DIRECTORY
) ||
1856 !strcmpW( entry
.cFileName
, dotW
)) continue;
1858 if (!long_names
&& entry
.cAlternateFileName
[0])
1859 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1861 strcpyW( buffer
+ 1, entry
.cFileName
);
1862 strcatW(buffer
, bracketW
);
1864 else /* not a directory */
1866 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1867 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1869 if ((attrib
& DDL_EXCLUSIVE
) &&
1870 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1873 if (!long_names
&& entry
.cAlternateFileName
[0])
1874 strcpyW( buffer
, entry
.cAlternateFileName
);
1876 strcpyW( buffer
, entry
.cFileName
);
1878 if (!long_names
) CharLowerW( buffer
);
1879 pos
= LISTBOX_FindFileStrPos( descr
, buffer
);
1880 if ((ret
= LISTBOX_InsertString( descr
, pos
, buffer
)) < 0)
1882 } while (FindNextFileW( handle
, &entry
));
1883 FindClose( handle
);
1888 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1890 WCHAR buffer
[] = {'[','-','a','-',']',0};
1891 WCHAR root
[] = {'A',':','\\',0};
1893 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1895 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1896 if ((ret
= LISTBOX_InsertString( descr
, -1, buffer
)) < 0)
1904 /***********************************************************************
1905 * LISTBOX_HandleVScroll
1907 static LRESULT
LISTBOX_HandleVScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1911 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1915 LISTBOX_SetTopItem( descr
, descr
->top_item
- 1, TRUE
);
1918 LISTBOX_SetTopItem( descr
, descr
->top_item
+ 1, TRUE
);
1921 LISTBOX_SetTopItem( descr
, descr
->top_item
-
1922 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1925 LISTBOX_SetTopItem( descr
, descr
->top_item
+
1926 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1928 case SB_THUMBPOSITION
:
1929 LISTBOX_SetTopItem( descr
, pos
, TRUE
);
1932 info
.cbSize
= sizeof(info
);
1933 info
.fMask
= SIF_TRACKPOS
;
1934 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1935 LISTBOX_SetTopItem( descr
, info
.nTrackPos
, TRUE
);
1938 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1941 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1948 /***********************************************************************
1949 * LISTBOX_HandleHScroll
1951 static LRESULT
LISTBOX_HandleHScroll( LB_DESCR
*descr
, WORD scrollReq
, WORD pos
)
1956 if (descr
->style
& LBS_MULTICOLUMN
)
1961 LISTBOX_SetTopItem( descr
, descr
->top_item
-descr
->page_size
,
1965 LISTBOX_SetTopItem( descr
, descr
->top_item
+descr
->page_size
,
1969 page
= descr
->width
/ descr
->column_width
;
1970 if (page
< 1) page
= 1;
1971 LISTBOX_SetTopItem( descr
,
1972 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1975 page
= descr
->width
/ descr
->column_width
;
1976 if (page
< 1) page
= 1;
1977 LISTBOX_SetTopItem( descr
,
1978 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1980 case SB_THUMBPOSITION
:
1981 LISTBOX_SetTopItem( descr
, pos
*descr
->page_size
,
1985 info
.cbSize
= sizeof(info
);
1986 info
.fMask
= SIF_TRACKPOS
;
1987 GetScrollInfo( descr
->self
, SB_VERT
, &info
);
1988 LISTBOX_SetTopItem( descr
, info
.nTrackPos
*descr
->page_size
,
1992 LISTBOX_SetTopItem( descr
, 0, TRUE
);
1995 LISTBOX_SetTopItem( descr
, descr
->nb_items
, TRUE
);
1999 else if (descr
->horz_extent
)
2004 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
- 1 );
2007 LISTBOX_SetHorizontalPos( descr
, descr
->horz_pos
+ 1 );
2010 LISTBOX_SetHorizontalPos( descr
,
2011 descr
->horz_pos
- descr
->width
);
2014 LISTBOX_SetHorizontalPos( descr
,
2015 descr
->horz_pos
+ descr
->width
);
2017 case SB_THUMBPOSITION
:
2018 LISTBOX_SetHorizontalPos( descr
, pos
);
2021 info
.cbSize
= sizeof(info
);
2022 info
.fMask
= SIF_TRACKPOS
;
2023 GetScrollInfo( descr
->self
, SB_HORZ
, &info
);
2024 LISTBOX_SetHorizontalPos( descr
, info
.nTrackPos
);
2027 LISTBOX_SetHorizontalPos( descr
, 0 );
2030 LISTBOX_SetHorizontalPos( descr
,
2031 descr
->horz_extent
- descr
->width
);
2038 static LRESULT
LISTBOX_HandleMouseWheel(LB_DESCR
*descr
, SHORT delta
)
2040 short gcWheelDelta
= 0;
2041 UINT pulScrollLines
= 3;
2043 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
2045 gcWheelDelta
-= delta
;
2047 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
2049 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
2050 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
2051 LISTBOX_SetTopItem( descr
, descr
->top_item
+ cLineScroll
, TRUE
);
2056 /***********************************************************************
2057 * LISTBOX_HandleLButtonDown
2059 static LRESULT
LISTBOX_HandleLButtonDown( LB_DESCR
*descr
,
2060 DWORD keys
, INT x
, INT y
)
2062 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2063 TRACE("[%p]: lbuttondown %d,%d item %d\n", descr
->self
, x
, y
, index
);
2064 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2066 if (!descr
->in_focus
)
2068 if( !descr
->lphc
) SetFocus( descr
->self
);
2069 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2072 if (index
== -1) return 0;
2074 if (descr
->style
& (LBS_EXTENDEDSEL
| LBS_MULTIPLESEL
))
2076 /* we should perhaps make sure that all items are deselected
2077 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2078 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2079 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2082 if (!(keys
& MK_SHIFT
)) descr
->anchor_item
= index
;
2083 if (keys
& MK_CONTROL
)
2085 LISTBOX_SetCaretIndex( descr
, index
, FALSE
);
2086 LISTBOX_SetSelection( descr
, index
,
2087 !descr
->items
[index
].selected
,
2088 (descr
->style
& LBS_NOTIFY
) != 0);
2092 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2094 if (descr
->style
& LBS_EXTENDEDSEL
)
2096 LISTBOX_SetSelection( descr
, index
,
2097 descr
->items
[index
].selected
,
2098 (descr
->style
& LBS_NOTIFY
) != 0 );
2102 LISTBOX_SetSelection( descr
, index
,
2103 !descr
->items
[index
].selected
,
2104 (descr
->style
& LBS_NOTIFY
) != 0 );
2110 descr
->anchor_item
= index
;
2111 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2112 LISTBOX_SetSelection( descr
, index
,
2113 TRUE
, (descr
->style
& LBS_NOTIFY
) != 0 );
2116 descr
->captured
= TRUE
;
2117 SetCapture( descr
->self
);
2121 if (descr
->style
& LBS_NOTIFY
)
2122 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2123 MAKELPARAM( x
, y
) );
2124 if (GetWindowLongW( descr
->self
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2131 if (DragDetect( descr
->self
, pt
))
2132 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2139 /*************************************************************************
2140 * LISTBOX_HandleLButtonDownCombo [Internal]
2142 * Process LButtonDown message for the ComboListBox
2145 * pWnd [I] The windows internal structure
2146 * pDescr [I] The ListBox internal structure
2147 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2148 * x [I] X Mouse Coordinate
2149 * y [I] Y Mouse Coordinate
2152 * 0 since we are processing the WM_LBUTTONDOWN Message
2155 * This function is only to be used when a ListBox is a ComboListBox
2158 static LRESULT
LISTBOX_HandleLButtonDownCombo( LB_DESCR
*pDescr
,
2159 UINT msg
, DWORD keys
, INT x
, INT y
)
2161 RECT clientRect
, screenRect
;
2167 GetClientRect(pDescr
->self
, &clientRect
);
2169 if(PtInRect(&clientRect
, mousePos
))
2171 /* MousePos is in client, resume normal processing */
2172 if (msg
== WM_LBUTTONDOWN
)
2174 pDescr
->lphc
->droppedIndex
= pDescr
->nb_items
? pDescr
->selected_item
: -1;
2175 return LISTBOX_HandleLButtonDown( pDescr
, keys
, x
, y
);
2177 else if (pDescr
->style
& LBS_NOTIFY
)
2178 SEND_NOTIFICATION( pDescr
, LBN_DBLCLK
);
2182 POINT screenMousePos
;
2183 HWND hWndOldCapture
;
2185 /* Check the Non-Client Area */
2186 screenMousePos
= mousePos
;
2187 hWndOldCapture
= GetCapture();
2189 GetWindowRect(pDescr
->self
, &screenRect
);
2190 ClientToScreen(pDescr
->self
, &screenMousePos
);
2192 if(!PtInRect(&screenRect
, screenMousePos
))
2194 LISTBOX_SetCaretIndex( pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
);
2195 LISTBOX_SetSelection( pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2196 COMBO_FlipListbox( pDescr
->lphc
, FALSE
, FALSE
);
2200 /* Check to see the NC is a scrollbar */
2202 LONG style
= GetWindowLongW( pDescr
->self
, GWL_STYLE
);
2203 /* Check Vertical scroll bar */
2204 if (style
& WS_VSCROLL
)
2207 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2208 if (PtInRect( &clientRect
, mousePos
))
2209 nHitTestType
= HTVSCROLL
;
2211 /* Check horizontal scroll bar */
2212 if (style
& WS_HSCROLL
)
2214 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2215 if (PtInRect( &clientRect
, mousePos
))
2216 nHitTestType
= HTHSCROLL
;
2218 /* Windows sends this message when a scrollbar is clicked
2221 if(nHitTestType
!= 0)
2223 SendMessageW(pDescr
->self
, WM_NCLBUTTONDOWN
, nHitTestType
,
2224 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2226 /* Resume the Capture after scrolling is complete
2228 if(hWndOldCapture
!= 0)
2229 SetCapture(hWndOldCapture
);
2235 /***********************************************************************
2236 * LISTBOX_HandleLButtonUp
2238 static LRESULT
LISTBOX_HandleLButtonUp( LB_DESCR
*descr
)
2240 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2241 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2242 LISTBOX_Timer
= LB_TIMER_NONE
;
2243 if (descr
->captured
)
2245 descr
->captured
= FALSE
;
2246 if (GetCapture() == descr
->self
) ReleaseCapture();
2247 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2248 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2254 /***********************************************************************
2255 * LISTBOX_HandleTimer
2257 * Handle scrolling upon a timer event.
2258 * Return TRUE if scrolling should continue.
2260 static LRESULT
LISTBOX_HandleTimer( LB_DESCR
*descr
,
2261 INT index
, TIMER_DIRECTION dir
)
2266 if (descr
->top_item
) index
= descr
->top_item
- 1;
2270 if (descr
->top_item
) index
-= descr
->page_size
;
2273 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2274 if (index
== descr
->focus_item
) index
++;
2275 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2277 case LB_TIMER_RIGHT
:
2278 if (index
+ descr
->page_size
< descr
->nb_items
)
2279 index
+= descr
->page_size
;
2284 if (index
== descr
->focus_item
) return FALSE
;
2285 LISTBOX_MoveCaret( descr
, index
, FALSE
);
2290 /***********************************************************************
2291 * LISTBOX_HandleSystemTimer
2293 * WM_SYSTIMER handler.
2295 static LRESULT
LISTBOX_HandleSystemTimer( LB_DESCR
*descr
)
2297 if (!LISTBOX_HandleTimer( descr
, descr
->focus_item
, LISTBOX_Timer
))
2299 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2300 LISTBOX_Timer
= LB_TIMER_NONE
;
2306 /***********************************************************************
2307 * LISTBOX_HandleMouseMove
2309 * WM_MOUSEMOVE handler.
2311 static void LISTBOX_HandleMouseMove( LB_DESCR
*descr
,
2315 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2317 if (!descr
->captured
) return;
2319 if (descr
->style
& LBS_MULTICOLUMN
)
2322 else if (y
>= descr
->item_height
* descr
->page_size
)
2323 y
= descr
->item_height
* descr
->page_size
- 1;
2327 dir
= LB_TIMER_LEFT
;
2330 else if (x
>= descr
->width
)
2332 dir
= LB_TIMER_RIGHT
;
2333 x
= descr
->width
- 1;
2338 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2339 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2342 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2343 if (index
== -1) index
= descr
->focus_item
;
2344 if (!LISTBOX_HandleTimer( descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2346 /* Start/stop the system timer */
2348 if (dir
!= LB_TIMER_NONE
)
2349 SetSystemTimer( descr
->self
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2350 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2351 KillSystemTimer( descr
->self
, LB_TIMER_ID
);
2352 LISTBOX_Timer
= dir
;
2356 /***********************************************************************
2357 * LISTBOX_HandleKeyDown
2359 static LRESULT
LISTBOX_HandleKeyDown( LB_DESCR
*descr
, DWORD key
)
2362 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2363 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2364 bForceSelection
= FALSE
; /* only for single select list */
2366 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2368 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2369 MAKEWPARAM(LOWORD(key
), descr
->focus_item
),
2370 (LPARAM
)descr
->self
);
2371 if (caret
== -2) return 0;
2373 if (caret
== -1) switch(key
)
2376 if (descr
->style
& LBS_MULTICOLUMN
)
2378 bForceSelection
= FALSE
;
2379 if (descr
->focus_item
>= descr
->page_size
)
2380 caret
= descr
->focus_item
- descr
->page_size
;
2385 caret
= descr
->focus_item
- 1;
2386 if (caret
< 0) caret
= 0;
2389 if (descr
->style
& LBS_MULTICOLUMN
)
2391 bForceSelection
= FALSE
;
2392 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2393 caret
= descr
->focus_item
+ descr
->page_size
;
2398 caret
= descr
->focus_item
+ 1;
2399 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2403 if (descr
->style
& LBS_MULTICOLUMN
)
2405 INT page
= descr
->width
/ descr
->column_width
;
2406 if (page
< 1) page
= 1;
2407 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2409 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2410 if (caret
< 0) caret
= 0;
2413 if (descr
->style
& LBS_MULTICOLUMN
)
2415 INT page
= descr
->width
/ descr
->column_width
;
2416 if (page
< 1) page
= 1;
2417 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2419 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2420 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2426 caret
= descr
->nb_items
- 1;
2429 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2430 else if (descr
->style
& LBS_MULTIPLESEL
)
2432 LISTBOX_SetSelection( descr
, descr
->focus_item
,
2433 !descr
->items
[descr
->focus_item
].selected
,
2434 (descr
->style
& LBS_NOTIFY
) != 0 );
2438 bForceSelection
= FALSE
;
2440 if (bForceSelection
) /* focused item is used instead of key */
2441 caret
= descr
->focus_item
;
2444 if (((descr
->style
& LBS_EXTENDEDSEL
) &&
2445 !(GetKeyState( VK_SHIFT
) & 0x8000)) ||
2446 !IS_MULTISELECT(descr
))
2447 descr
->anchor_item
= caret
;
2448 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2450 if (descr
->style
& LBS_MULTIPLESEL
)
2451 descr
->selected_item
= caret
;
2453 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2454 if (descr
->style
& LBS_NOTIFY
)
2458 /* make sure that combo parent doesn't hide us */
2459 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2461 if (descr
->nb_items
) SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2469 /***********************************************************************
2470 * LISTBOX_HandleChar
2472 static LRESULT
LISTBOX_HandleChar( LB_DESCR
*descr
, WCHAR charW
)
2480 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2482 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2483 MAKEWPARAM(charW
, descr
->focus_item
),
2484 (LPARAM
)descr
->self
);
2485 if (caret
== -2) return 0;
2488 caret
= LISTBOX_FindString( descr
, descr
->focus_item
, str
, FALSE
);
2491 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2492 LISTBOX_SetSelection( descr
, caret
, TRUE
, FALSE
);
2493 LISTBOX_MoveCaret( descr
, caret
, TRUE
);
2494 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2495 SEND_NOTIFICATION( descr
, LBN_SELCHANGE
);
2501 /***********************************************************************
2504 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2507 MEASUREITEMSTRUCT mis
;
2510 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2513 GetClientRect( hwnd
, &rect
);
2515 descr
->owner
= GetParent( descr
->self
);
2516 descr
->style
= GetWindowLongA( descr
->self
, GWL_STYLE
);
2517 descr
->width
= rect
.right
- rect
.left
;
2518 descr
->height
= rect
.bottom
- rect
.top
;
2519 descr
->items
= NULL
;
2520 descr
->nb_items
= 0;
2521 descr
->top_item
= 0;
2522 descr
->selected_item
= -1;
2523 descr
->focus_item
= 0;
2524 descr
->anchor_item
= -1;
2525 descr
->item_height
= 1;
2526 descr
->page_size
= 1;
2527 descr
->column_width
= 150;
2528 descr
->horz_extent
= (descr
->style
& WS_HSCROLL
) ? 1 : 0;
2529 descr
->horz_pos
= 0;
2532 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2533 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2534 descr
->in_focus
= FALSE
;
2535 descr
->captured
= FALSE
;
2537 descr
->locale
= GetUserDefaultLCID();
2541 if (is_old_app(hwnd
) && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2543 /* Win95 document "List Box Differences" from MSDN:
2544 If a list box in a version 3.x application has either the
2545 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2546 horizontal and vertical scroll bars.
2548 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2554 TRACE_(combo
)("[%p]: resetting owner %p -> %p\n", hwnd
, descr
->owner
, lphc
->self
);
2555 descr
->owner
= lphc
->self
;
2558 SetWindowLongPtrW( descr
->self
, 0, (LONG_PTR
)descr
);
2560 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2562 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2563 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2564 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2565 descr
->item_height
= LISTBOX_SetFont( descr
, 0 );
2567 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2569 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2571 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2572 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2576 UINT id
= (UINT
)GetWindowLongPtrW( descr
->self
, GWLP_ID
);
2577 mis
.CtlType
= ODT_LISTBOX
;
2582 mis
.itemHeight
= descr
->item_height
;
2583 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2584 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2588 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2593 /***********************************************************************
2596 static BOOL
LISTBOX_Destroy( LB_DESCR
*descr
)
2598 LISTBOX_ResetContent( descr
);
2599 SetWindowLongPtrW( descr
->self
, 0, 0 );
2600 HeapFree( GetProcessHeap(), 0, descr
);
2605 /***********************************************************************
2606 * ListBoxWndProc_common
2608 static LRESULT WINAPI
ListBoxWndProc_common( HWND hwnd
, UINT msg
,
2609 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2611 LB_DESCR
*descr
= (LB_DESCR
*)GetWindowLongPtrW( hwnd
, 0 );
2613 LPHEADCOMBO lphc
= 0;
2617 if (!IsWindow(hwnd
)) return 0;
2619 if (msg
== WM_CREATE
)
2621 CREATESTRUCTW
*lpcs
= (CREATESTRUCTW
*)lParam
;
2622 if (lpcs
->style
& LBS_COMBOBOX
) lphc
= (LPHEADCOMBO
)lpcs
->lpCreateParams
;
2623 if (!LISTBOX_Create( hwnd
, lphc
))
2625 TRACE("creating wnd=%p descr=%lx\n", hwnd
, GetWindowLongPtrW( hwnd
, 0 ) );
2628 /* Ignore all other messages before we get a WM_CREATE */
2629 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2630 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2632 if (descr
->style
& LBS_COMBOBOX
) lphc
= descr
->lphc
;
2634 //TRACE("[%p]: msg %s wp %08x lp %08lx\n",
2635 // hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
2639 case LB_RESETCONTENT16
:
2641 case LB_RESETCONTENT
:
2642 LISTBOX_ResetContent( descr
);
2643 LISTBOX_UpdateScroll( descr
);
2644 InvalidateRect( descr
->self
, NULL
, TRUE
);
2648 case LB_ADDSTRING16
:
2649 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2656 if(unicode
|| !HAS_STRINGS(descr
))
2657 textW
= (LPWSTR
)lParam
;
2660 LPSTR textA
= (LPSTR
)lParam
;
2661 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2662 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2663 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2665 wParam
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2666 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2667 if (!unicode
&& HAS_STRINGS(descr
))
2668 HeapFree(GetProcessHeap(), 0, textW
);
2673 case LB_INSERTSTRING16
:
2674 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2675 wParam
= (INT
)(INT16
)wParam
;
2678 case LB_INSERTSTRING
:
2682 if(unicode
|| !HAS_STRINGS(descr
))
2683 textW
= (LPWSTR
)lParam
;
2686 LPSTR textA
= (LPSTR
)lParam
;
2687 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2688 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2689 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2691 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2692 if(!unicode
&& HAS_STRINGS(descr
))
2693 HeapFree(GetProcessHeap(), 0, textW
);
2699 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2706 if(unicode
|| !HAS_STRINGS(descr
))
2707 textW
= (LPWSTR
)lParam
;
2710 LPSTR textA
= (LPSTR
)lParam
;
2711 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2712 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2713 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2715 wParam
= LISTBOX_FindFileStrPos( descr
, textW
);
2716 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2717 if(!unicode
&& HAS_STRINGS(descr
))
2718 HeapFree(GetProcessHeap(), 0, textW
);
2723 case LB_DELETESTRING16
:
2725 case LB_DELETESTRING
:
2726 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2727 return descr
->nb_items
;
2730 SetLastError(ERROR_INVALID_INDEX
);
2735 case LB_GETITEMDATA16
:
2737 case LB_GETITEMDATA
:
2738 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2740 SetLastError(ERROR_INVALID_INDEX
);
2743 return descr
->items
[wParam
].data
;
2746 case LB_SETITEMDATA16
:
2748 case LB_SETITEMDATA
:
2749 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2751 SetLastError(ERROR_INVALID_INDEX
);
2754 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2761 return descr
->nb_items
;
2765 lParam
= (LPARAM
)MapSL(lParam
);
2769 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, unicode
);
2772 case LB_GETTEXTLEN16
:
2776 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2778 SetLastError(ERROR_INVALID_INDEX
);
2781 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2782 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2783 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2784 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2787 case LB_GETCURSEL16
:
2790 if (descr
->nb_items
==0)
2792 if (!IS_MULTISELECT(descr
))
2793 return descr
->selected_item
;
2795 if (descr
->selected_item
!=-1)
2796 return descr
->selected_item
;
2798 return descr
->focus_item
;
2799 /* otherwise, if the user tries to move the selection with the */
2800 /* arrow keys, we will give the application something to choke on */
2802 case LB_GETTOPINDEX16
:
2804 case LB_GETTOPINDEX
:
2805 return descr
->top_item
;
2808 case LB_GETITEMHEIGHT16
:
2810 case LB_GETITEMHEIGHT
:
2811 return LISTBOX_GetItemHeight( descr
, wParam
);
2814 case LB_SETITEMHEIGHT16
:
2815 lParam
= LOWORD(lParam
);
2818 case LB_SETITEMHEIGHT
:
2819 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2821 case LB_ITEMFROMPOINT
:
2826 pt
.x
= LOWORD(lParam
);
2827 pt
.y
= HIWORD(lParam
);
2830 rect
.right
= descr
->width
;
2831 rect
.bottom
= descr
->height
;
2833 return MAKELONG( LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
),
2834 !PtInRect( &rect
, pt
) );
2838 case LB_SETCARETINDEX16
:
2840 case LB_SETCARETINDEX
:
2841 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2842 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2850 case LB_GETCARETINDEX16
:
2852 case LB_GETCARETINDEX
:
2853 return descr
->focus_item
;
2856 case LB_SETTOPINDEX16
:
2858 case LB_SETTOPINDEX
:
2859 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2862 case LB_SETCOLUMNWIDTH16
:
2864 case LB_SETCOLUMNWIDTH
:
2865 return LISTBOX_SetColumnWidth( descr
, wParam
);
2868 case LB_GETITEMRECT16
:
2871 ret
= LISTBOX_GetItemRect( descr
, (INT16
)wParam
, &rect
);
2872 CONV_RECT32TO16( &rect
, MapSL(lParam
) );
2877 case LB_GETITEMRECT
:
2878 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2881 case LB_FINDSTRING16
:
2882 wParam
= (INT
)(INT16
)wParam
;
2883 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2890 if(unicode
|| !HAS_STRINGS(descr
))
2891 textW
= (LPWSTR
)lParam
;
2894 LPSTR textA
= (LPSTR
)lParam
;
2895 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2896 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2897 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2899 ret
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2900 if(!unicode
&& HAS_STRINGS(descr
))
2901 HeapFree(GetProcessHeap(), 0, textW
);
2906 case LB_FINDSTRINGEXACT16
:
2907 wParam
= (INT
)(INT16
)wParam
;
2908 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2911 case LB_FINDSTRINGEXACT
:
2915 if(unicode
|| !HAS_STRINGS(descr
))
2916 textW
= (LPWSTR
)lParam
;
2919 LPSTR textA
= (LPSTR
)lParam
;
2920 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2921 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2922 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2924 ret
= LISTBOX_FindString( descr
, wParam
, textW
, TRUE
);
2925 if(!unicode
&& HAS_STRINGS(descr
))
2926 HeapFree(GetProcessHeap(), 0, textW
);
2931 case LB_SELECTSTRING16
:
2932 wParam
= (INT
)(INT16
)wParam
;
2933 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2936 case LB_SELECTSTRING
:
2941 if(HAS_STRINGS(descr
))
2942 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2943 debugstr_a((LPSTR
)lParam
));
2944 if(unicode
|| !HAS_STRINGS(descr
))
2945 textW
= (LPWSTR
)lParam
;
2948 LPSTR textA
= (LPSTR
)lParam
;
2949 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2950 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2951 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2953 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2954 if(!unicode
&& HAS_STRINGS(descr
))
2955 HeapFree(GetProcessHeap(), 0, textW
);
2956 if (index
!= LB_ERR
)
2958 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2959 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2966 wParam
= (INT
)(INT16
)wParam
;
2970 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2972 return descr
->items
[wParam
].selected
;
2976 lParam
= (INT
)(INT16
)lParam
;
2980 return LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2983 case LB_SETCURSEL16
:
2984 wParam
= (INT
)(INT16
)wParam
;
2988 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2989 LISTBOX_SetCaretIndex( descr
, wParam
, TRUE
);
2990 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
2991 if (lphc
&& ret
!= LB_ERR
) ret
= descr
->selected_item
;
2995 case LB_GETSELCOUNT16
:
2997 case LB_GETSELCOUNT
:
2998 return LISTBOX_GetSelCount( descr
);
3001 case LB_GETSELITEMS16
:
3002 return LISTBOX_GetSelItems16( descr
, wParam
, (LPINT16
)MapSL(lParam
) );
3005 case LB_GETSELITEMS
:
3006 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
3009 case LB_SELITEMRANGE16
:
3011 case LB_SELITEMRANGE
:
3012 if (LOWORD(lParam
) <= HIWORD(lParam
))
3013 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
3014 HIWORD(lParam
), wParam
);
3016 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
3017 LOWORD(lParam
), wParam
);
3020 case LB_SELITEMRANGEEX16
:
3022 case LB_SELITEMRANGEEX
:
3023 if ((INT
)lParam
>= (INT
)wParam
)
3024 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
3026 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
3029 case LB_GETHORIZONTALEXTENT16
:
3031 case LB_GETHORIZONTALEXTENT
:
3032 return descr
->horz_extent
;
3035 case LB_SETHORIZONTALEXTENT16
:
3037 case LB_SETHORIZONTALEXTENT
:
3038 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
3041 case LB_GETANCHORINDEX16
:
3043 case LB_GETANCHORINDEX
:
3044 return descr
->anchor_item
;
3047 case LB_SETANCHORINDEX16
:
3048 wParam
= (INT
)(INT16
)wParam
;
3051 case LB_SETANCHORINDEX
:
3052 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
3054 SetLastError(ERROR_INVALID_INDEX
);
3057 descr
->anchor_item
= (INT
)wParam
;
3062 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
3063 * be set automatically (this is different in Win32) */
3064 if (wParam
& DDL_DRIVES
) wParam
|= DDL_EXCLUSIVE
;
3065 lParam
= (LPARAM
)MapSL(lParam
);
3073 textW
= (LPWSTR
)lParam
;
3076 LPSTR textA
= (LPSTR
)lParam
;
3077 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
3078 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
3079 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
3081 ret
= LISTBOX_Directory( descr
, wParam
, textW
, msg
== LB_DIR
);
3083 HeapFree(GetProcessHeap(), 0, textW
);
3088 return descr
->locale
;
3093 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
3095 ret
= descr
->locale
;
3096 descr
->locale
= (LCID
)wParam
;
3100 case LB_INITSTORAGE
:
3101 return LISTBOX_InitStorage( descr
, wParam
);
3104 return LISTBOX_SetCount( descr
, (INT
)wParam
);
3107 case LB_SETTABSTOPS16
:
3108 return LISTBOX_SetTabStops( descr
, (INT
)(INT16
)wParam
, MapSL(lParam
), TRUE
);
3111 case LB_SETTABSTOPS
:
3112 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
, FALSE
);
3118 if (descr
->caret_on
)
3120 descr
->caret_on
= TRUE
;
3121 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3122 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3129 if (!descr
->caret_on
)
3131 descr
->caret_on
= FALSE
;
3132 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3133 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3137 return LISTBOX_Destroy( descr
);
3140 InvalidateRect( hwnd
, NULL
, TRUE
);
3144 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
3148 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
3153 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( hwnd
, &ps
);
3154 ret
= LISTBOX_Paint( descr
, hdc
);
3155 if( !wParam
) EndPaint( hwnd
, &ps
);
3159 LISTBOX_UpdateSize( descr
);
3162 return (LRESULT
)descr
->font
;
3164 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
3165 if (lParam
) InvalidateRect( hwnd
, 0, TRUE
);
3168 descr
->in_focus
= TRUE
;
3169 descr
->caret_on
= TRUE
;
3170 if (descr
->focus_item
!= -1)
3171 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3172 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
3175 descr
->in_focus
= FALSE
;
3176 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
3177 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3178 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
3181 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3183 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3185 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
3186 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
3187 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
3188 case WM_LBUTTONDOWN
:
3190 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3191 (INT16
)LOWORD(lParam
),
3192 (INT16
)HIWORD(lParam
) );
3193 return LISTBOX_HandleLButtonDown(descr
, wParam
,
3194 (INT16
)LOWORD(lParam
),
3195 (INT16
)HIWORD(lParam
) );
3196 case WM_LBUTTONDBLCLK
:
3198 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3199 (INT16
)LOWORD(lParam
),
3200 (INT16
)HIWORD(lParam
) );
3201 if (descr
->style
& LBS_NOTIFY
)
3202 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
3205 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
3207 BOOL captured
= descr
->captured
;
3211 mousePos
.x
= (INT16
)LOWORD(lParam
);
3212 mousePos
.y
= (INT16
)HIWORD(lParam
);
3215 * If we are in a dropdown combobox, we simulate that
3216 * the mouse is captured to show the tracking of the item.
3218 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
3219 descr
->captured
= TRUE
;
3221 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
3223 descr
->captured
= captured
;
3225 else if (GetCapture() == descr
->self
)
3227 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
3228 (INT16
)HIWORD(lParam
) );
3238 * If the mouse button "up" is not in the listbox,
3239 * we make sure there is no selection by re-selecting the
3240 * item that was selected when the listbox was made visible.
3242 mousePos
.x
= (INT16
)LOWORD(lParam
);
3243 mousePos
.y
= (INT16
)HIWORD(lParam
);
3245 GetClientRect(hwnd
, &clientRect
);
3248 * When the user clicks outside the combobox and the focus
3249 * is lost, the owning combobox will send a fake buttonup with
3250 * 0xFFFFFFF as the mouse location, we must also revert the
3251 * selection to the original selection.
3253 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
3254 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
3256 return LISTBOX_HandleLButtonUp( descr
);
3258 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3260 /* for some reason Windows makes it possible to
3261 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3263 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3264 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3265 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3267 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3271 return LISTBOX_HandleKeyDown( descr
, wParam
);
3276 charW
= (WCHAR
)wParam
;
3279 CHAR charA
= (CHAR
)wParam
;
3280 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
3282 return LISTBOX_HandleChar( descr
, charW
);
3285 return LISTBOX_HandleSystemTimer( descr
);
3287 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3290 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3291 wParam
, (LPARAM
)descr
->self
);
3292 TRACE("hbrush = %p\n", hbrush
);
3294 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3297 GetClientRect(descr
->self
, &rect
);
3298 FillRect((HDC
)wParam
, &rect
, hbrush
);
3303 if( lphc
) return 0;
3304 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3305 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3307 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3316 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3317 WARN("[%p]: unknown msg %04x wp %08x lp %08lx\n",
3318 hwnd
, msg
, wParam
, lParam
);
3321 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3322 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3325 /***********************************************************************
3328 * This is just a wrapper for the real wndproc, it only does window locking
3331 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3333 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3336 /***********************************************************************
3339 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3341 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);
3344 /***********************************************************************
3348 GetListBoxInfo(HWND hwnd
)