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
);
2653 case LB_ADDSTRING_LOWER
:
2654 case LB_ADDSTRING_UPPER
:
2658 if(unicode
|| !HAS_STRINGS(descr
))
2659 textW
= (LPWSTR
)lParam
;
2662 LPSTR textA
= (LPSTR
)lParam
;
2663 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2664 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2665 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2667 /* in the unicode the version, the string is really overwritten
2668 during the converting case */
2669 if (msg
== LB_ADDSTRING_LOWER
)
2671 else if (msg
== LB_ADDSTRING_UPPER
)
2673 wParam
= LISTBOX_FindStringPos( descr
, textW
, FALSE
);
2674 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2675 if (!unicode
&& HAS_STRINGS(descr
))
2676 HeapFree(GetProcessHeap(), 0, textW
);
2681 case LB_INSERTSTRING16
:
2682 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2683 wParam
= (INT
)(INT16
)wParam
;
2686 case LB_INSERTSTRING
:
2687 case LB_INSERTSTRING_UPPER
:
2688 case LB_INSERTSTRING_LOWER
:
2692 if(unicode
|| !HAS_STRINGS(descr
))
2693 textW
= (LPWSTR
)lParam
;
2696 LPSTR textA
= (LPSTR
)lParam
;
2697 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2698 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2699 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2701 /* in the unicode the version, the string is really overwritten
2702 during the converting case */
2703 if (msg
== LB_INSERTSTRING_LOWER
)
2705 else if (msg
== LB_INSERTSTRING_UPPER
)
2707 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2708 if(!unicode
&& HAS_STRINGS(descr
))
2709 HeapFree(GetProcessHeap(), 0, textW
);
2715 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2722 if(unicode
|| !HAS_STRINGS(descr
))
2723 textW
= (LPWSTR
)lParam
;
2726 LPSTR textA
= (LPSTR
)lParam
;
2727 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2728 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2729 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2731 wParam
= LISTBOX_FindFileStrPos( descr
, textW
);
2732 ret
= LISTBOX_InsertString( descr
, wParam
, textW
);
2733 if(!unicode
&& HAS_STRINGS(descr
))
2734 HeapFree(GetProcessHeap(), 0, textW
);
2739 case LB_DELETESTRING16
:
2741 case LB_DELETESTRING
:
2742 if (LISTBOX_RemoveItem( descr
, wParam
) != LB_ERR
)
2743 return descr
->nb_items
;
2746 SetLastError(ERROR_INVALID_INDEX
);
2751 case LB_GETITEMDATA16
:
2753 case LB_GETITEMDATA
:
2754 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2756 SetLastError(ERROR_INVALID_INDEX
);
2759 return descr
->items
[wParam
].data
;
2762 case LB_SETITEMDATA16
:
2764 case LB_SETITEMDATA
:
2765 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2767 SetLastError(ERROR_INVALID_INDEX
);
2770 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2777 return descr
->nb_items
;
2781 lParam
= (LPARAM
)MapSL(lParam
);
2785 return LISTBOX_GetText( descr
, wParam
, (LPWSTR
)lParam
, unicode
);
2788 case LB_GETTEXTLEN16
:
2792 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2794 SetLastError(ERROR_INVALID_INDEX
);
2797 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2798 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2799 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2800 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2803 case LB_GETCURSEL16
:
2806 if (descr
->nb_items
==0)
2808 if (!IS_MULTISELECT(descr
))
2809 return descr
->selected_item
;
2811 if (descr
->selected_item
!=-1)
2812 return descr
->selected_item
;
2814 return descr
->focus_item
;
2815 /* otherwise, if the user tries to move the selection with the */
2816 /* arrow keys, we will give the application something to choke on */
2818 case LB_GETTOPINDEX16
:
2820 case LB_GETTOPINDEX
:
2821 return descr
->top_item
;
2824 case LB_GETITEMHEIGHT16
:
2826 case LB_GETITEMHEIGHT
:
2827 return LISTBOX_GetItemHeight( descr
, wParam
);
2830 case LB_SETITEMHEIGHT16
:
2831 lParam
= LOWORD(lParam
);
2834 case LB_SETITEMHEIGHT
:
2835 return LISTBOX_SetItemHeight( descr
, wParam
, lParam
, TRUE
);
2837 case LB_ITEMFROMPOINT
:
2842 pt
.x
= LOWORD(lParam
);
2843 pt
.y
= HIWORD(lParam
);
2846 rect
.right
= descr
->width
;
2847 rect
.bottom
= descr
->height
;
2849 return MAKELONG( LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
),
2850 !PtInRect( &rect
, pt
) );
2854 case LB_SETCARETINDEX16
:
2856 case LB_SETCARETINDEX
:
2857 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2858 if (LISTBOX_SetCaretIndex( descr
, wParam
, !lParam
) == LB_ERR
)
2866 case LB_GETCARETINDEX16
:
2868 case LB_GETCARETINDEX
:
2869 return descr
->focus_item
;
2872 case LB_SETTOPINDEX16
:
2874 case LB_SETTOPINDEX
:
2875 return LISTBOX_SetTopItem( descr
, wParam
, TRUE
);
2878 case LB_SETCOLUMNWIDTH16
:
2880 case LB_SETCOLUMNWIDTH
:
2881 return LISTBOX_SetColumnWidth( descr
, wParam
);
2884 case LB_GETITEMRECT16
:
2887 ret
= LISTBOX_GetItemRect( descr
, (INT16
)wParam
, &rect
);
2888 CONV_RECT32TO16( &rect
, MapSL(lParam
) );
2893 case LB_GETITEMRECT
:
2894 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2897 case LB_FINDSTRING16
:
2898 wParam
= (INT
)(INT16
)wParam
;
2899 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2906 if(unicode
|| !HAS_STRINGS(descr
))
2907 textW
= (LPWSTR
)lParam
;
2910 LPSTR textA
= (LPSTR
)lParam
;
2911 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2912 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2913 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2915 ret
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2916 if(!unicode
&& HAS_STRINGS(descr
))
2917 HeapFree(GetProcessHeap(), 0, textW
);
2922 case LB_FINDSTRINGEXACT16
:
2923 wParam
= (INT
)(INT16
)wParam
;
2924 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2927 case LB_FINDSTRINGEXACT
:
2931 if(unicode
|| !HAS_STRINGS(descr
))
2932 textW
= (LPWSTR
)lParam
;
2935 LPSTR textA
= (LPSTR
)lParam
;
2936 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2937 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2938 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2940 ret
= LISTBOX_FindString( descr
, wParam
, textW
, TRUE
);
2941 if(!unicode
&& HAS_STRINGS(descr
))
2942 HeapFree(GetProcessHeap(), 0, textW
);
2947 case LB_SELECTSTRING16
:
2948 wParam
= (INT
)(INT16
)wParam
;
2949 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2952 case LB_SELECTSTRING
:
2957 if(HAS_STRINGS(descr
))
2958 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2959 debugstr_a((LPSTR
)lParam
));
2960 if(unicode
|| !HAS_STRINGS(descr
))
2961 textW
= (LPWSTR
)lParam
;
2964 LPSTR textA
= (LPSTR
)lParam
;
2965 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2966 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2967 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2969 index
= LISTBOX_FindString( descr
, wParam
, textW
, FALSE
);
2970 if(!unicode
&& HAS_STRINGS(descr
))
2971 HeapFree(GetProcessHeap(), 0, textW
);
2972 if (index
!= LB_ERR
)
2974 LISTBOX_MoveCaret( descr
, index
, TRUE
);
2975 LISTBOX_SetSelection( descr
, index
, TRUE
, FALSE
);
2982 wParam
= (INT
)(INT16
)wParam
;
2986 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2988 return descr
->items
[wParam
].selected
;
2992 lParam
= (INT
)(INT16
)lParam
;
2996 return LISTBOX_SetSelection( descr
, lParam
, wParam
, FALSE
);
2999 case LB_SETCURSEL16
:
3000 wParam
= (INT
)(INT16
)wParam
;
3004 if (IS_MULTISELECT(descr
)) return LB_ERR
;
3005 LISTBOX_SetCaretIndex( descr
, wParam
, TRUE
);
3006 ret
= LISTBOX_SetSelection( descr
, wParam
, TRUE
, FALSE
);
3007 if (lphc
&& ret
!= LB_ERR
) ret
= descr
->selected_item
;
3011 case LB_GETSELCOUNT16
:
3013 case LB_GETSELCOUNT
:
3014 return LISTBOX_GetSelCount( descr
);
3017 case LB_GETSELITEMS16
:
3018 return LISTBOX_GetSelItems16( descr
, wParam
, (LPINT16
)MapSL(lParam
) );
3021 case LB_GETSELITEMS
:
3022 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
3025 case LB_SELITEMRANGE16
:
3027 case LB_SELITEMRANGE
:
3028 if (LOWORD(lParam
) <= HIWORD(lParam
))
3029 return LISTBOX_SelectItemRange( descr
, LOWORD(lParam
),
3030 HIWORD(lParam
), wParam
);
3032 return LISTBOX_SelectItemRange( descr
, HIWORD(lParam
),
3033 LOWORD(lParam
), wParam
);
3036 case LB_SELITEMRANGEEX16
:
3038 case LB_SELITEMRANGEEX
:
3039 if ((INT
)lParam
>= (INT
)wParam
)
3040 return LISTBOX_SelectItemRange( descr
, wParam
, lParam
, TRUE
);
3042 return LISTBOX_SelectItemRange( descr
, lParam
, wParam
, FALSE
);
3045 case LB_GETHORIZONTALEXTENT16
:
3047 case LB_GETHORIZONTALEXTENT
:
3048 return descr
->horz_extent
;
3051 case LB_SETHORIZONTALEXTENT16
:
3053 case LB_SETHORIZONTALEXTENT
:
3054 return LISTBOX_SetHorizontalExtent( descr
, wParam
);
3057 case LB_GETANCHORINDEX16
:
3059 case LB_GETANCHORINDEX
:
3060 return descr
->anchor_item
;
3063 case LB_SETANCHORINDEX16
:
3064 wParam
= (INT
)(INT16
)wParam
;
3067 case LB_SETANCHORINDEX
:
3068 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
3070 SetLastError(ERROR_INVALID_INDEX
);
3073 descr
->anchor_item
= (INT
)wParam
;
3078 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
3079 * be set automatically (this is different in Win32) */
3080 if (wParam
& DDL_DRIVES
) wParam
|= DDL_EXCLUSIVE
;
3081 lParam
= (LPARAM
)MapSL(lParam
);
3089 textW
= (LPWSTR
)lParam
;
3092 LPSTR textA
= (LPSTR
)lParam
;
3093 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
3094 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
3095 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
3097 ret
= LISTBOX_Directory( descr
, wParam
, textW
, msg
== LB_DIR
);
3099 HeapFree(GetProcessHeap(), 0, textW
);
3104 return descr
->locale
;
3109 if (!IsValidLocale((LCID
)wParam
, LCID_INSTALLED
))
3111 ret
= descr
->locale
;
3112 descr
->locale
= (LCID
)wParam
;
3116 case LB_INITSTORAGE
:
3117 return LISTBOX_InitStorage( descr
, wParam
);
3120 return LISTBOX_SetCount( descr
, (INT
)wParam
);
3123 case LB_SETTABSTOPS16
:
3124 return LISTBOX_SetTabStops( descr
, (INT
)(INT16
)wParam
, MapSL(lParam
), TRUE
);
3127 case LB_SETTABSTOPS
:
3128 return LISTBOX_SetTabStops( descr
, wParam
, (LPINT
)lParam
, FALSE
);
3134 if (descr
->caret_on
)
3136 descr
->caret_on
= TRUE
;
3137 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3138 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3145 if (!descr
->caret_on
)
3147 descr
->caret_on
= FALSE
;
3148 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3149 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3153 return LISTBOX_Destroy( descr
);
3156 InvalidateRect( hwnd
, NULL
, TRUE
);
3160 LISTBOX_SetRedraw( descr
, wParam
!= 0 );
3164 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
3169 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( hwnd
, &ps
);
3170 ret
= LISTBOX_Paint( descr
, hdc
);
3171 if( !wParam
) EndPaint( hwnd
, &ps
);
3175 LISTBOX_UpdateSize( descr
);
3178 return (LRESULT
)descr
->font
;
3180 LISTBOX_SetFont( descr
, (HFONT
)wParam
);
3181 if (lParam
) InvalidateRect( hwnd
, 0, TRUE
);
3184 descr
->in_focus
= TRUE
;
3185 descr
->caret_on
= TRUE
;
3186 if (descr
->focus_item
!= -1)
3187 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3188 SEND_NOTIFICATION( descr
, LBN_SETFOCUS
);
3191 descr
->in_focus
= FALSE
;
3192 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
3193 LISTBOX_RepaintItem( descr
, descr
->focus_item
, ODA_FOCUS
);
3194 SEND_NOTIFICATION( descr
, LBN_KILLFOCUS
);
3197 return LISTBOX_HandleHScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3199 return LISTBOX_HandleVScroll( descr
, LOWORD(wParam
), HIWORD(wParam
) );
3201 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
3202 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
3203 return LISTBOX_HandleMouseWheel( descr
, (SHORT
)HIWORD(wParam
) );
3204 case WM_LBUTTONDOWN
:
3206 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3207 (INT16
)LOWORD(lParam
),
3208 (INT16
)HIWORD(lParam
) );
3209 return LISTBOX_HandleLButtonDown(descr
, wParam
,
3210 (INT16
)LOWORD(lParam
),
3211 (INT16
)HIWORD(lParam
) );
3212 case WM_LBUTTONDBLCLK
:
3214 return LISTBOX_HandleLButtonDownCombo(descr
, msg
, wParam
,
3215 (INT16
)LOWORD(lParam
),
3216 (INT16
)HIWORD(lParam
) );
3217 if (descr
->style
& LBS_NOTIFY
)
3218 SEND_NOTIFICATION( descr
, LBN_DBLCLK
);
3221 if ( lphc
&& ((lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
) )
3223 BOOL captured
= descr
->captured
;
3227 mousePos
.x
= (INT16
)LOWORD(lParam
);
3228 mousePos
.y
= (INT16
)HIWORD(lParam
);
3231 * If we are in a dropdown combobox, we simulate that
3232 * the mouse is captured to show the tracking of the item.
3234 if (GetClientRect(descr
->self
, &clientRect
) && PtInRect( &clientRect
, mousePos
))
3235 descr
->captured
= TRUE
;
3237 LISTBOX_HandleMouseMove( descr
, mousePos
.x
, mousePos
.y
);
3239 descr
->captured
= captured
;
3241 else if (GetCapture() == descr
->self
)
3243 LISTBOX_HandleMouseMove( descr
, (INT16
)LOWORD(lParam
),
3244 (INT16
)HIWORD(lParam
) );
3254 * If the mouse button "up" is not in the listbox,
3255 * we make sure there is no selection by re-selecting the
3256 * item that was selected when the listbox was made visible.
3258 mousePos
.x
= (INT16
)LOWORD(lParam
);
3259 mousePos
.y
= (INT16
)HIWORD(lParam
);
3261 GetClientRect(hwnd
, &clientRect
);
3264 * When the user clicks outside the combobox and the focus
3265 * is lost, the owning combobox will send a fake buttonup with
3266 * 0xFFFFFFF as the mouse location, we must also revert the
3267 * selection to the original selection.
3269 if ( (lParam
== (LPARAM
)-1) || (!PtInRect( &clientRect
, mousePos
)) )
3270 LISTBOX_MoveCaret( descr
, lphc
->droppedIndex
, FALSE
);
3272 return LISTBOX_HandleLButtonUp( descr
);
3274 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3276 /* for some reason Windows makes it possible to
3277 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3279 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3280 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3281 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3283 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3287 return LISTBOX_HandleKeyDown( descr
, wParam
);
3292 charW
= (WCHAR
)wParam
;
3295 CHAR charA
= (CHAR
)wParam
;
3296 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
3298 return LISTBOX_HandleChar( descr
, charW
);
3301 return LISTBOX_HandleSystemTimer( descr
);
3303 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3306 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3307 wParam
, (LPARAM
)descr
->self
);
3308 TRACE("hbrush = %p\n", hbrush
);
3310 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3313 GetClientRect(descr
->self
, &rect
);
3314 FillRect((HDC
)wParam
, &rect
, hbrush
);
3319 if( lphc
) return 0;
3320 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3321 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3323 if( lphc
&& (lphc
->dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
3332 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3333 WARN("[%p]: unknown msg %04x wp %08x lp %08lx\n",
3334 hwnd
, msg
, wParam
, lParam
);
3337 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3338 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3341 /***********************************************************************
3344 * This is just a wrapper for the real wndproc, it only does window locking
3347 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3349 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3352 /***********************************************************************
3355 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3357 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);
3360 /***********************************************************************
3364 GetListBoxInfo(HWND hwnd
)