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
37 #define WM_SYSTIMER 280
39 UINT STDCALL
SetSystemTimer(HWND
,UINT_PTR
,UINT
,TIMERPROC
);
40 BOOL STDCALL
KillSystemTimer(HWND
,UINT_PTR
);
42 /* End of hack section -------------------------------- */
48 * Probably needs improvement:
52 /* Items array granularity */
53 #define LB_ARRAY_GRANULARITY 16
55 /* Scrolling timeout in ms */
56 #define LB_SCROLL_TIMEOUT 50
58 /* Listbox system timer id */
61 /* flag listbox changed while setredraw false - internal style */
62 #define LBS_DISPLAYCHANGED 0x80000000
67 LPWSTR str
; /* Item text */
68 BOOL selected
; /* Is item selected? */
69 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
70 DWORD data
; /* User data */
73 /* Listbox structure */
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 BOOL caret_on
; /* Is caret on? */
94 BOOL captured
; /* Is mouse captured? */
96 HFONT font
; /* Current font */
97 LCID locale
; /* Current locale for string comparisons */
98 LPHEADCOMBO lphc
; /* ComboLBox */
102 #define IS_OWNERDRAW(descr) \
103 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
105 #define HAS_STRINGS(descr) \
106 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
109 #define IS_MULTISELECT(descr) \
110 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
112 #define SEND_NOTIFICATION(hwnd,descr,code) \
113 (SendMessageW( (descr)->owner, WM_COMMAND, \
114 MAKEWPARAM( GetWindowLongA((hwnd),GWL_ID), (code)), (LPARAM)(hwnd) ))
116 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
118 /* Current timer status */
128 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
130 static LRESULT WINAPI
ComboLBWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
131 static LRESULT WINAPI
ComboLBWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
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
)ComboLBWndProcW
, /* procW */
171 (WNDPROC
)ComboLBWndProcA
, /* procA */
172 sizeof(LB_DESCR
*), /* extra */
173 (LPCWSTR
) IDC_ARROW
, /* cursor */
176 "ComboLBox", /* name */
177 CS_DBLCLKS
| CS_SAVEBITS
, /* style */
178 ComboLBWndProcA
, /* procA */
179 ComboLBWndProcW
, /* 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( HWND hwnd
, 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( hwnd
, SB_HORZ
, &info
, TRUE
);
305 info
.fMask
= SIF_RANGE
;
306 if (descr
->style
& WS_VSCROLL
)
307 SetScrollInfo( hwnd
, 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( hwnd
, 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( hwnd
, SB_HORZ
, &info
, TRUE
);
337 /***********************************************************************
340 * Set the top item of the listbox, scrolling up or down if necessary.
342 static LRESULT
LISTBOX_SetTopItem( HWND hwnd
, 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( hwnd
, 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( hwnd
, 0, diff
, NULL
, NULL
, 0, NULL
,
383 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
387 if (!scroll
) InvalidateRect( hwnd
, NULL
, TRUE
);
388 descr
->top_item
= index
;
389 LISTBOX_UpdateScroll( hwnd
, 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( HWND hwnd
, 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( hwnd
, NULL
, TRUE
);
410 LISTBOX_SetTopItem( hwnd
, 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( HWND hwnd
, LB_DESCR
*descr
)
424 GetClientRect( hwnd
, &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( hwnd
, &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 hwnd
, descr
->height
, descr
->height
- remaining
);
451 SetWindowPos( hwnd
, 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", hwnd
, descr
->width
, descr
->height
);
458 LISTBOX_UpdatePage( hwnd
, descr
);
459 LISTBOX_UpdateScroll( hwnd
, descr
);
461 /* Invalidate the focused item so it will be repainted correctly */
462 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
464 InvalidateRect( hwnd
, &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
)) return -1;
479 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
480 if (descr
->style
& LBS_MULTICOLUMN
)
482 INT col
= (index
/ descr
->page_size
) -
483 (descr
->top_item
/ descr
->page_size
);
484 rect
->left
+= col
* descr
->column_width
;
485 rect
->right
= rect
->left
+ descr
->column_width
;
486 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
487 rect
->bottom
= rect
->top
+ descr
->item_height
;
489 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
492 rect
->right
+= descr
->horz_pos
;
493 if ((index
>= 0) && (index
< descr
->nb_items
))
495 if (index
< descr
->top_item
)
497 for (i
= descr
->top_item
-1; i
>= index
; i
--)
498 rect
->top
-= descr
->items
[i
].height
;
502 for (i
= descr
->top_item
; i
< index
; i
++)
503 rect
->top
+= descr
->items
[i
].height
;
505 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
511 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
512 rect
->bottom
= rect
->top
+ descr
->item_height
;
513 rect
->right
+= descr
->horz_pos
;
516 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
517 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
521 /***********************************************************************
522 * LISTBOX_GetItemFromPoint
524 * Return the item nearest from point (x,y) (in client coordinates).
526 static INT
LISTBOX_GetItemFromPoint( LB_DESCR
*descr
, INT x
, INT y
)
528 INT index
= descr
->top_item
;
530 if (!descr
->nb_items
) return -1; /* No items */
531 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
536 while (index
< descr
->nb_items
)
538 if ((pos
+= descr
->items
[index
].height
) > y
) break;
547 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
551 else if (descr
->style
& LBS_MULTICOLUMN
)
553 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
554 if (y
>= 0) index
+= y
/ descr
->item_height
;
555 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
556 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
560 index
+= (y
/ descr
->item_height
);
562 if (index
< 0) return 0;
563 if (index
>= descr
->nb_items
) return -1;
568 /***********************************************************************
573 static void LISTBOX_PaintItem( HWND hwnd
, LB_DESCR
*descr
, HDC hdc
,
574 const RECT
*rect
, INT index
, UINT action
, BOOL ignoreFocus
)
576 LB_ITEMDATA
*item
= NULL
;
577 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
579 if (IS_OWNERDRAW(descr
))
584 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
588 if (action
== ODA_FOCUS
)
589 DrawFocusRect( hdc
, rect
);
591 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
595 /* some programs mess with the clipping region when
596 drawing the item, *and* restore the previous region
597 after they are done, so a region has better to exist
598 else everything ends clipped */
599 GetClientRect(hwnd
, &r
);
600 hrgn
= CreateRectRgnIndirect(&r
);
601 SelectClipRgn( hdc
, hrgn
);
602 DeleteObject( hrgn
);
604 dis
.CtlType
= ODT_LISTBOX
;
607 dis
.itemAction
= action
;
611 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
612 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
614 (descr
->in_focus
)) dis
.itemState
|= ODS_FOCUS
;
615 if (!IsWindowEnabled(hwnd
)) dis
.itemState
|= ODS_DISABLED
;
616 dis
.itemData
= item
? item
->data
: 0;
618 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%ld,%ld-%ld,%ld\n",
619 hwnd
, index
, item
? debugstr_w(item
->str
) : "", action
,
620 dis
.itemState
, rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
621 SendMessageW(descr
->owner
, WM_DRAWITEM
, id
, (LPARAM
)&dis
);
625 COLORREF oldText
= 0, oldBk
= 0;
627 if (action
== ODA_FOCUS
)
629 DrawFocusRect( hdc
, rect
);
632 if (item
&& item
->selected
)
634 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
635 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
638 TRACE("[%p]: painting %d (%s) action=%02x rect=%ld,%ld-%ld,%ld\n",
639 hwnd
, index
, item
? debugstr_w(item
->str
) : "", action
,
640 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
642 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
643 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
644 else if (!(descr
->style
& LBS_USETABSTOPS
))
645 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
646 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
647 strlenW(item
->str
), NULL
);
650 /* Output empty string to paint background in the full width. */
651 ExtTextOutW( hdc
, rect
->left
+ 1, rect
->top
,
652 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
653 TabbedTextOutW( hdc
, rect
->left
+ 1 , rect
->top
,
654 item
->str
, strlenW(item
->str
),
655 descr
->nb_tabs
, descr
->tabs
, 0);
657 if (item
&& item
->selected
)
659 SetBkColor( hdc
, oldBk
);
660 SetTextColor( hdc
, oldText
);
662 if (!ignoreFocus
&& (descr
->focus_item
== index
) &&
664 (descr
->in_focus
)) DrawFocusRect( hdc
, rect
);
669 /***********************************************************************
672 * Change the redraw flag.
674 static void LISTBOX_SetRedraw( HWND hwnd
, LB_DESCR
*descr
, BOOL on
)
678 if (!(descr
->style
& LBS_NOREDRAW
)) return;
679 descr
->style
&= ~LBS_NOREDRAW
;
680 if (descr
->style
& LBS_DISPLAYCHANGED
)
681 { /* page was changed while setredraw false, refresh automatically */
682 InvalidateRect(hwnd
, NULL
, TRUE
);
683 if ((descr
->top_item
+ descr
->page_size
) > descr
->nb_items
)
684 { /* reset top of page if less than number of items/page */
685 descr
->top_item
= descr
->nb_items
- descr
->page_size
;
686 if (descr
->top_item
< 0) descr
->top_item
= 0;
688 descr
->style
&= ~LBS_DISPLAYCHANGED
;
690 LISTBOX_UpdateScroll( hwnd
, descr
);
692 else descr
->style
|= LBS_NOREDRAW
;
696 /***********************************************************************
697 * LISTBOX_RepaintItem
699 * Repaint a single item synchronously.
701 static void LISTBOX_RepaintItem( HWND hwnd
, LB_DESCR
*descr
, INT index
,
707 HBRUSH hbrush
, oldBrush
= 0;
709 /* Do not repaint the item if the item is not visible */
710 if (!IsWindowVisible(hwnd
)) return;
711 if (descr
->style
& LBS_NOREDRAW
)
713 descr
->style
|= LBS_DISPLAYCHANGED
;
716 if (LISTBOX_GetItemRect( descr
, index
, &rect
) != 1) return;
717 if (!(hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
))) return;
718 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
719 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
720 (WPARAM
)hdc
, (LPARAM
)hwnd
);
721 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
722 if (!IsWindowEnabled(hwnd
))
723 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
724 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
725 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, index
, action
, FALSE
);
726 if (oldFont
) SelectObject( hdc
, oldFont
);
727 if (oldBrush
) SelectObject( hdc
, oldBrush
);
728 ReleaseDC( hwnd
, hdc
);
732 /***********************************************************************
733 * LISTBOX_InitStorage
735 static LRESULT
LISTBOX_InitStorage( HWND hwnd
, LB_DESCR
*descr
, INT nb_items
)
739 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
740 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
742 nb_items
+= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
743 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
744 nb_items
* sizeof(LB_ITEMDATA
));
747 item
= HeapAlloc( GetProcessHeap(), 0,
748 nb_items
* sizeof(LB_ITEMDATA
));
753 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
761 /***********************************************************************
762 * LISTBOX_SetTabStops
764 static BOOL
LISTBOX_SetTabStops( HWND hwnd
, LB_DESCR
*descr
, INT count
,
765 LPINT tabs
, BOOL short_ints
)
767 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
768 if (descr
->tabs
) HeapFree( GetProcessHeap(), 0, descr
->tabs
);
769 if (!(descr
->nb_tabs
= count
))
774 /* FIXME: count = 1 */
775 if (!(descr
->tabs
= (INT
*)HeapAlloc( GetProcessHeap(), 0,
776 descr
->nb_tabs
* sizeof(INT
) )))
782 LPINT16 p
= (LPINT16
)tabs
;
784 TRACE("[%p]: settabstops ", hwnd
);
785 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
786 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
787 if (TRACE_ON(listbox
)) TRACE("%hd ", descr
->tabs
[i
]);
789 if (TRACE_ON(listbox
)) TRACE("\n");
791 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
793 memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
795 /* FIXME: repaint the window? */
800 /***********************************************************************
803 static LRESULT
LISTBOX_GetText( LB_DESCR
*descr
, INT index
, LPARAM lParam
, BOOL unicode
)
805 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
806 if (HAS_STRINGS(descr
))
809 return strlenW(descr
->items
[index
].str
);
811 TRACE("index %d (0x%04x) %s\n", index
, index
, debugstr_w(descr
->items
[index
].str
));
815 LPWSTR buffer
= (LPWSTR
)lParam
;
816 strcpyW( buffer
, descr
->items
[index
].str
);
817 return strlenW(buffer
);
821 LPSTR buffer
= (LPSTR
)lParam
;
822 return WideCharToMultiByte(CP_ACP
, 0, descr
->items
[index
].str
, -1, buffer
, 0x7FFFFFFF, NULL
, NULL
) - 1;
826 *((LPDWORD
)lParam
)=*(LPDWORD
)(&descr
->items
[index
].data
);
827 return sizeof(DWORD
);
832 /***********************************************************************
833 * LISTBOX_FindStringPos
835 * Find the nearest string located before a given string in sort order.
836 * If 'exact' is TRUE, return an error if we don't get an exact match.
838 static INT
LISTBOX_FindStringPos( HWND hwnd
, LB_DESCR
*descr
, LPCWSTR str
,
841 INT index
, min
, max
, res
= -1;
843 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
845 max
= descr
->nb_items
;
848 index
= (min
+ max
) / 2;
849 if (HAS_STRINGS(descr
))
850 res
= lstrcmpiW( str
, descr
->items
[index
].str
);
853 COMPAREITEMSTRUCT cis
;
854 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
856 cis
.CtlType
= ODT_LISTBOX
;
859 /* note that some application (MetaStock) expects the second item
860 * to be in the listbox */
862 cis
.itemData1
= (DWORD
)str
;
864 cis
.itemData2
= descr
->items
[index
].data
;
865 cis
.dwLocaleId
= descr
->locale
;
866 res
= SendMessageW( descr
->owner
, WM_COMPAREITEM
, id
, (LPARAM
)&cis
);
868 if (!res
) return index
;
869 if (res
< 0) max
= index
;
870 else min
= index
+ 1;
872 return exact
? -1 : max
;
876 /***********************************************************************
877 * LISTBOX_FindFileStrPos
879 * Find the nearest string located before a given string in directory
880 * sort order (i.e. first files, then directories, then drives).
882 static INT
LISTBOX_FindFileStrPos( HWND hwnd
, LB_DESCR
*descr
, LPCWSTR str
)
884 INT min
, max
, res
= -1;
886 if (!HAS_STRINGS(descr
))
887 return LISTBOX_FindStringPos( hwnd
, descr
, str
, FALSE
);
889 max
= descr
->nb_items
;
892 INT index
= (min
+ max
) / 2;
893 LPCWSTR p
= descr
->items
[index
].str
;
894 if (*p
== '[') /* drive or directory */
896 if (*str
!= '[') res
= -1;
897 else if (p
[1] == '-') /* drive */
899 if (str
[1] == '-') res
= str
[2] - p
[2];
904 if (str
[1] == '-') res
= 1;
905 else res
= lstrcmpiW( str
, p
);
910 if (*str
== '[') res
= 1;
911 else res
= lstrcmpiW( str
, p
);
913 if (!res
) return index
;
914 if (res
< 0) max
= index
;
915 else min
= index
+ 1;
921 /***********************************************************************
924 * Find the item beginning with a given string.
926 static INT
LISTBOX_FindString( HWND hwnd
, LB_DESCR
*descr
, INT start
,
927 LPCWSTR str
, BOOL exact
)
932 if (start
>= descr
->nb_items
) start
= -1;
933 item
= descr
->items
+ start
+ 1;
934 if (HAS_STRINGS(descr
))
936 if (!str
|| ! str
[0] ) return LB_ERR
;
939 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
940 if (!lstrcmpiW( str
, item
->str
)) return i
;
941 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
942 if (!lstrcmpiW( str
, item
->str
)) return i
;
946 /* Special case for drives and directories: ignore prefix */
947 #define CHECK_DRIVE(item) \
948 if ((item)->str[0] == '[') \
950 if (!strncmpiW( str, (item)->str+1, len )) return i; \
951 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
955 INT len
= strlenW(str
);
956 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
958 if (!strncmpiW( str
, item
->str
, len
)) return i
;
961 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
963 if (!strncmpiW( str
, item
->str
, len
)) return i
;
971 if (exact
&& (descr
->style
& LBS_SORT
))
972 /* If sorted, use a WM_COMPAREITEM binary search */
973 return LISTBOX_FindStringPos( hwnd
, descr
, str
, TRUE
);
975 /* Otherwise use a linear search */
976 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
977 if (item
->data
== (DWORD
)str
) return i
;
978 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
979 if (item
->data
== (DWORD
)str
) return i
;
985 /***********************************************************************
986 * LISTBOX_GetSelCount
988 static LRESULT
LISTBOX_GetSelCount( LB_DESCR
*descr
)
991 LB_ITEMDATA
*item
= descr
->items
;
993 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
994 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
995 if (item
->selected
) count
++;
1001 /***********************************************************************
1002 * LISTBOX_GetSelItems16
1004 static LRESULT
LISTBOX_GetSelItems16( LB_DESCR
*descr
, INT16 max
, LPINT16 array
)
1007 LB_ITEMDATA
*item
= descr
->items
;
1009 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1010 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
1011 if (item
->selected
) array
[count
++] = (INT16
)i
;
1017 /***********************************************************************
1018 * LISTBOX_GetSelItems
1020 static LRESULT
LISTBOX_GetSelItems( LB_DESCR
*descr
, INT max
, LPINT array
)
1023 LB_ITEMDATA
*item
= descr
->items
;
1025 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1026 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
1027 if (item
->selected
) array
[count
++] = i
;
1032 /***********************************************************************
1035 static LRESULT
LISTBOX_Paint( HWND hwnd
, LB_DESCR
*descr
, HDC hdc
)
1037 INT i
, col_pos
= descr
->page_size
- 1;
1039 RECT focusRect
= {-1, -1, -1, -1};
1041 HBRUSH hbrush
, oldBrush
= 0;
1043 if (descr
->style
& LBS_NOREDRAW
) return 0;
1045 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
1046 if (descr
->style
& LBS_MULTICOLUMN
)
1047 rect
.right
= rect
.left
+ descr
->column_width
;
1048 else if (descr
->horz_pos
)
1050 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
1051 rect
.right
+= descr
->horz_pos
;
1054 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
1055 hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
1056 (WPARAM
)hdc
, (LPARAM
)hwnd
);
1057 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
1058 if (!IsWindowEnabled(hwnd
)) SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1060 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
1063 /* Special case for empty listbox: paint focus rect */
1064 rect
.bottom
= rect
.top
+ descr
->item_height
;
1065 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, descr
->focus_item
,
1067 rect
.top
= rect
.bottom
;
1070 /* Paint all the item, regarding the selection
1071 Focus state will be painted after */
1073 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
1075 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
1076 rect
.bottom
= rect
.top
+ descr
->item_height
;
1078 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
1080 if (i
== descr
->focus_item
)
1082 /* keep the focus rect, to paint the focus item after */
1083 focusRect
.left
= rect
.left
;
1084 focusRect
.right
= rect
.right
;
1085 focusRect
.top
= rect
.top
;
1086 focusRect
.bottom
= rect
.bottom
;
1088 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
, TRUE
);
1089 rect
.top
= rect
.bottom
;
1091 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
1093 if (!IS_OWNERDRAW(descr
))
1095 /* Clear the bottom of the column */
1096 if (rect
.top
< descr
->height
)
1098 rect
.bottom
= descr
->height
;
1099 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1100 &rect
, NULL
, 0, NULL
);
1104 /* Go to the next column */
1105 rect
.left
+= descr
->column_width
;
1106 rect
.right
+= descr
->column_width
;
1108 col_pos
= descr
->page_size
- 1;
1113 if (rect
.top
>= descr
->height
) break;
1117 /* Paint the focus item now */
1118 if (focusRect
.top
!= focusRect
.bottom
&& descr
->caret_on
)
1119 LISTBOX_PaintItem( hwnd
, descr
, hdc
, &focusRect
, descr
->focus_item
, ODA_FOCUS
, FALSE
);
1121 if (!IS_OWNERDRAW(descr
))
1123 /* Clear the remainder of the client area */
1124 if (rect
.top
< descr
->height
)
1126 rect
.bottom
= descr
->height
;
1127 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1128 &rect
, NULL
, 0, NULL
);
1130 if (rect
.right
< descr
->width
)
1132 rect
.left
= rect
.right
;
1133 rect
.right
= descr
->width
;
1135 rect
.bottom
= descr
->height
;
1136 ExtTextOutW( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
1137 &rect
, NULL
, 0, NULL
);
1140 if (oldFont
) SelectObject( hdc
, oldFont
);
1141 if (oldBrush
) SelectObject( hdc
, oldBrush
);
1146 /***********************************************************************
1147 * LISTBOX_InvalidateItems
1149 * Invalidate all items from a given item. If the specified item is not
1150 * visible, nothing happens.
1152 static void LISTBOX_InvalidateItems( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1156 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1158 if (descr
->style
& LBS_NOREDRAW
)
1160 descr
->style
|= LBS_DISPLAYCHANGED
;
1163 rect
.bottom
= descr
->height
;
1164 InvalidateRect( hwnd
, &rect
, TRUE
);
1165 if (descr
->style
& LBS_MULTICOLUMN
)
1167 /* Repaint the other columns */
1168 rect
.left
= rect
.right
;
1169 rect
.right
= descr
->width
;
1171 InvalidateRect( hwnd
, &rect
, TRUE
);
1176 static void LISTBOX_InvalidateItemRect( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1180 if (LISTBOX_GetItemRect( descr
, index
, &rect
) == 1)
1181 InvalidateRect( hwnd
, &rect
, TRUE
);
1184 /***********************************************************************
1185 * LISTBOX_GetItemHeight
1187 static LRESULT
LISTBOX_GetItemHeight( LB_DESCR
*descr
, INT index
)
1189 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1191 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1192 return descr
->items
[index
].height
;
1194 else return descr
->item_height
;
1198 /***********************************************************************
1199 * LISTBOX_SetItemHeight
1201 static LRESULT
LISTBOX_SetItemHeight( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1202 INT height
, BOOL repaint
)
1204 if (!height
) height
= 1;
1206 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1208 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1209 TRACE("[%p]: item %d height = %d\n", hwnd
, index
, height
);
1210 descr
->items
[index
].height
= height
;
1211 LISTBOX_UpdateScroll( hwnd
, descr
);
1213 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1215 else if (height
!= descr
->item_height
)
1217 TRACE("[%p]: new height = %d\n", hwnd
, height
);
1218 descr
->item_height
= height
;
1219 LISTBOX_UpdatePage( hwnd
, descr
);
1220 LISTBOX_UpdateScroll( hwnd
, descr
);
1222 InvalidateRect( hwnd
, 0, TRUE
);
1228 /***********************************************************************
1229 * LISTBOX_SetHorizontalPos
1231 static void LISTBOX_SetHorizontalPos( HWND hwnd
, LB_DESCR
*descr
, INT pos
)
1235 if (pos
> descr
->horz_extent
- descr
->width
)
1236 pos
= descr
->horz_extent
- descr
->width
;
1237 if (pos
< 0) pos
= 0;
1238 if (!(diff
= descr
->horz_pos
- pos
)) return;
1239 TRACE("[%p]: new horz pos = %d\n", hwnd
, pos
);
1240 descr
->horz_pos
= pos
;
1241 LISTBOX_UpdateScroll( hwnd
, descr
);
1242 if (abs(diff
) < descr
->width
)
1245 /* Invalidate the focused item so it will be repainted correctly */
1246 if (LISTBOX_GetItemRect( descr
, descr
->focus_item
, &rect
) == 1)
1247 InvalidateRect( hwnd
, &rect
, TRUE
);
1248 ScrollWindowEx( hwnd
, diff
, 0, NULL
, NULL
, 0, NULL
,
1249 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1252 InvalidateRect( hwnd
, NULL
, TRUE
);
1256 /***********************************************************************
1257 * LISTBOX_SetHorizontalExtent
1259 static LRESULT
LISTBOX_SetHorizontalExtent( HWND hwnd
, LB_DESCR
*descr
,
1262 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1264 if (extent
<= 0) extent
= 1;
1265 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1266 TRACE("[%p]: new horz extent = %d\n", hwnd
, extent
);
1267 descr
->horz_extent
= extent
;
1268 if (descr
->horz_pos
> extent
- descr
->width
)
1269 LISTBOX_SetHorizontalPos( hwnd
, descr
, extent
- descr
->width
);
1271 LISTBOX_UpdateScroll( hwnd
, descr
);
1276 /***********************************************************************
1277 * LISTBOX_SetColumnWidth
1279 static LRESULT
LISTBOX_SetColumnWidth( HWND hwnd
, LB_DESCR
*descr
, INT width
)
1281 if (width
== descr
->column_width
) return LB_OKAY
;
1282 TRACE("[%p]: new column width = %d\n", hwnd
, width
);
1283 descr
->column_width
= width
;
1284 LISTBOX_UpdatePage( hwnd
, descr
);
1289 /***********************************************************************
1292 * Returns the item height.
1294 static INT
LISTBOX_SetFont( HWND hwnd
, LB_DESCR
*descr
, HFONT font
)
1302 if (!(hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
)))
1304 ERR("unable to get DC.\n" );
1307 if (font
) oldFont
= SelectObject( hdc
, font
);
1308 GetTextMetricsW( hdc
, &tm
);
1309 if (oldFont
) SelectObject( hdc
, oldFont
);
1310 ReleaseDC( hwnd
, hdc
);
1311 if (!IS_OWNERDRAW(descr
))
1312 LISTBOX_SetItemHeight( hwnd
, descr
, 0, tm
.tmHeight
, FALSE
);
1313 return tm
.tmHeight
;
1317 /***********************************************************************
1318 * LISTBOX_MakeItemVisible
1320 * Make sure that a given item is partially or fully visible.
1322 static void LISTBOX_MakeItemVisible( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1327 if (index
<= descr
->top_item
) top
= index
;
1328 else if (descr
->style
& LBS_MULTICOLUMN
)
1330 INT cols
= descr
->width
;
1331 if (!fully
) cols
+= descr
->column_width
- 1;
1332 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1334 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1335 top
= index
- descr
->page_size
* (cols
- 1);
1337 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1339 INT height
= fully
? descr
->items
[index
].height
: 1;
1340 for (top
= index
; top
> descr
->top_item
; top
--)
1341 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1345 if (index
< descr
->top_item
+ descr
->page_size
) return;
1346 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1347 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1348 top
= index
- descr
->page_size
+ 1;
1350 LISTBOX_SetTopItem( hwnd
, descr
, top
, TRUE
);
1353 /***********************************************************************
1354 * LISTBOX_SetCaretIndex
1357 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1360 static LRESULT
LISTBOX_SetCaretIndex( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1361 BOOL fully_visible
)
1363 INT oldfocus
= descr
->focus_item
;
1365 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1366 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1367 if (index
== oldfocus
) return LB_OKAY
;
1368 descr
->focus_item
= index
;
1369 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1370 LISTBOX_RepaintItem( hwnd
, descr
, oldfocus
, ODA_FOCUS
);
1372 LISTBOX_MakeItemVisible( hwnd
, descr
, index
, fully_visible
);
1373 if (descr
->caret_on
&& (descr
->in_focus
))
1374 LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_FOCUS
);
1380 /***********************************************************************
1381 * LISTBOX_SelectItemRange
1383 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1385 static LRESULT
LISTBOX_SelectItemRange( HWND hwnd
, LB_DESCR
*descr
, INT first
,
1390 /* A few sanity checks */
1392 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1393 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1394 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1395 if (last
== -1) last
= descr
->nb_items
- 1;
1396 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1397 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1398 /* selected_item reflects last selected/unselected item on multiple sel */
1399 descr
->selected_item
= last
;
1401 if (on
) /* Turn selection on */
1403 for (i
= first
; i
<= last
; i
++)
1405 if (descr
->items
[i
].selected
) continue;
1406 descr
->items
[i
].selected
= TRUE
;
1407 LISTBOX_InvalidateItemRect(hwnd
, descr
, i
);
1409 LISTBOX_SetCaretIndex( hwnd
, descr
, last
, TRUE
);
1411 else /* Turn selection off */
1413 for (i
= first
; i
<= last
; i
++)
1415 if (!descr
->items
[i
].selected
) continue;
1416 descr
->items
[i
].selected
= FALSE
;
1417 LISTBOX_InvalidateItemRect(hwnd
, descr
, i
);
1423 /***********************************************************************
1424 * LISTBOX_SetSelection
1426 static LRESULT
LISTBOX_SetSelection( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1427 BOOL on
, BOOL send_notify
)
1429 TRACE( "index=%d notify=%s\n", index
, send_notify
? "YES" : "NO" );
1431 if (descr
->style
& LBS_NOSEL
) return LB_ERR
;
1432 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1433 if (descr
->style
& LBS_MULTIPLESEL
)
1435 if (index
== -1) /* Select all items */
1436 return LISTBOX_SelectItemRange( hwnd
, descr
, 0, -1, on
);
1437 else /* Only one item */
1438 return LISTBOX_SelectItemRange( hwnd
, descr
, index
, index
, on
);
1442 INT oldsel
= descr
->selected_item
;
1443 if (index
== oldsel
) return LB_OKAY
;
1444 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1445 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1446 descr
->selected_item
= index
;
1447 if (oldsel
!= -1) LISTBOX_RepaintItem( hwnd
, descr
, oldsel
, ODA_SELECT
);
1448 if (index
!= -1) LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_SELECT
);
1449 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( hwnd
, descr
,
1450 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1452 if( descr
->lphc
) /* set selection change flag for parent combo */
1453 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1459 /***********************************************************************
1462 * Change the caret position and extend the selection to the new caret.
1464 static void LISTBOX_MoveCaret( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1465 BOOL fully_visible
)
1467 INT oldfocus
= descr
->focus_item
;
1469 if ((index
< 0) || (index
>= descr
->nb_items
))
1472 /* Important, repaint needs to be done in this order if
1473 you want to mimic Windows behavior:
1474 1. Remove the focus and paint the item
1475 2. Remove the selection and paint the item(s)
1476 3. Set the selection and repaint the item(s)
1477 4. Set the focus to 'index' and repaint the item */
1479 /* 1. remove the focus and repaint the item */
1480 descr
->focus_item
= -1;
1481 if ((oldfocus
!= -1) && descr
->caret_on
&& (descr
->in_focus
))
1482 LISTBOX_RepaintItem( hwnd
, descr
, oldfocus
, ODA_FOCUS
);
1484 /* 2. then turn off the previous selection */
1485 /* 3. repaint the new selected item */
1486 if (descr
->style
& LBS_EXTENDEDSEL
)
1488 if (descr
->anchor_item
!= -1)
1490 INT first
= min( index
, descr
->anchor_item
);
1491 INT last
= max( index
, descr
->anchor_item
);
1493 LISTBOX_SelectItemRange( hwnd
, descr
, 0, first
- 1, FALSE
);
1494 LISTBOX_SelectItemRange( hwnd
, descr
, last
+ 1, -1, FALSE
);
1495 LISTBOX_SelectItemRange( hwnd
, descr
, first
, last
, TRUE
);
1498 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1500 /* Set selection to new caret item */
1501 LISTBOX_SetSelection( hwnd
, descr
, index
, TRUE
, FALSE
);
1504 /* 4. repaint the new item with the focus */
1505 descr
->focus_item
= index
;
1506 LISTBOX_MakeItemVisible( hwnd
, descr
, index
, fully_visible
);
1507 if (descr
->caret_on
&& (descr
->in_focus
))
1508 LISTBOX_RepaintItem( hwnd
, descr
, index
, ODA_FOCUS
);
1512 /***********************************************************************
1513 * LISTBOX_InsertItem
1515 static LRESULT
LISTBOX_InsertItem( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1516 LPWSTR str
, DWORD data
)
1520 INT oldfocus
= descr
->focus_item
;
1522 if (index
== -1) index
= descr
->nb_items
;
1523 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1524 if (!descr
->items
) max_items
= 0;
1525 else max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(*item
);
1526 if (descr
->nb_items
== max_items
)
1528 /* We need to grow the array */
1529 max_items
+= LB_ARRAY_GRANULARITY
;
1531 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1532 max_items
* sizeof(LB_ITEMDATA
) );
1534 item
= HeapAlloc( GetProcessHeap(), 0,
1535 max_items
* sizeof(LB_ITEMDATA
) );
1538 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
1541 descr
->items
= item
;
1544 /* Insert the item structure */
1546 item
= &descr
->items
[index
];
1547 if (index
< descr
->nb_items
)
1548 RtlMoveMemory( item
+ 1, item
,
1549 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1553 item
->selected
= FALSE
;
1556 /* Get item height */
1558 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1560 MEASUREITEMSTRUCT mis
;
1561 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
1563 mis
.CtlType
= ODT_LISTBOX
;
1566 mis
.itemData
= descr
->items
[index
].data
;
1567 mis
.itemHeight
= descr
->item_height
;
1568 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1569 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1570 TRACE("[%p]: measure item %d (%s) = %d\n",
1571 hwnd
, index
, str
? debugstr_w(str
) : "", item
->height
);
1574 /* Repaint the items */
1576 LISTBOX_UpdateScroll( hwnd
, descr
);
1577 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1579 /* Move selection and focused item */
1580 /* If listbox was empty, set focus to the first item */
1581 if (descr
->nb_items
== 1)
1582 LISTBOX_SetCaretIndex( hwnd
, descr
, 0, FALSE
);
1583 /* single select don't change selection index in win31 */
1584 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1586 descr
->selected_item
++;
1587 LISTBOX_SetSelection( hwnd
, descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1591 if (index
<= descr
->selected_item
)
1593 descr
->selected_item
++;
1594 descr
->focus_item
= oldfocus
; /* focus not changed */
1601 /***********************************************************************
1602 * LISTBOX_InsertString
1604 static LRESULT
LISTBOX_InsertString( HWND hwnd
, LB_DESCR
*descr
, INT index
,
1607 LPWSTR new_str
= NULL
;
1611 if (HAS_STRINGS(descr
))
1613 static const WCHAR empty_stringW
[] = { 0 };
1614 if (!str
) str
= empty_stringW
;
1615 if (!(new_str
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) )))
1617 SEND_NOTIFICATION( hwnd
, descr
, LBN_ERRSPACE
);
1620 strcpyW(new_str
, str
);
1622 else data
= (DWORD
)str
;
1624 if (index
== -1) index
= descr
->nb_items
;
1625 if ((ret
= LISTBOX_InsertItem( hwnd
, descr
, index
, new_str
, data
)) != 0)
1627 if (new_str
) HeapFree( GetProcessHeap(), 0, new_str
);
1631 TRACE("[%p]: added item %d %s\n",
1632 hwnd
, index
, HAS_STRINGS(descr
) ? debugstr_w(new_str
) : "" );
1637 /***********************************************************************
1638 * LISTBOX_DeleteItem
1640 * Delete the content of an item. 'index' must be a valid index.
1642 static void LISTBOX_DeleteItem( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1644 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1645 * while Win95 sends it for all items with user data.
1646 * It's probably better to send it too often than not
1647 * often enough, so this is what we do here.
1649 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1651 DELETEITEMSTRUCT dis
;
1652 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
1654 dis
.CtlType
= ODT_LISTBOX
;
1657 dis
.hwndItem
= hwnd
;
1658 dis
.itemData
= descr
->items
[index
].data
;
1659 SendMessageW( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1661 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1662 HeapFree( GetProcessHeap(), 0, descr
->items
[index
].str
);
1666 /***********************************************************************
1667 * LISTBOX_RemoveItem
1669 * Remove an item from the listbox and delete its content.
1671 static LRESULT
LISTBOX_RemoveItem( HWND hwnd
, LB_DESCR
*descr
, INT index
)
1676 if ((index
== -1) && (descr
->nb_items
> 0)) index
= descr
->nb_items
- 1;
1677 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1679 /* We need to invalidate the original rect instead of the updated one. */
1680 LISTBOX_InvalidateItems( hwnd
, descr
, index
);
1682 LISTBOX_DeleteItem( hwnd
, descr
, index
);
1684 /* Remove the item */
1686 item
= &descr
->items
[index
];
1687 if (index
< descr
->nb_items
-1)
1688 RtlMoveMemory( item
, item
+ 1,
1689 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1691 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1693 /* Shrink the item array if possible */
1695 max_items
= HeapSize( GetProcessHeap(), 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1696 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1698 max_items
-= LB_ARRAY_GRANULARITY
;
1699 item
= HeapReAlloc( GetProcessHeap(), 0, descr
->items
,
1700 max_items
* sizeof(LB_ITEMDATA
) );
1701 if (item
) descr
->items
= item
;
1703 /* Repaint the items */
1705 LISTBOX_UpdateScroll( hwnd
, descr
);
1706 /* if we removed the scrollbar, reset the top of the list
1707 (correct for owner-drawn ???) */
1708 if (descr
->nb_items
== descr
->page_size
)
1709 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1711 /* Move selection and focused item */
1712 if (!IS_MULTISELECT(descr
))
1714 if (index
== descr
->selected_item
)
1715 descr
->selected_item
= -1;
1716 else if (index
< descr
->selected_item
)
1718 descr
->selected_item
--;
1719 if (ISWIN31
) /* win 31 do not change the selected item number */
1720 LISTBOX_SetSelection( hwnd
, descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1724 if (descr
->focus_item
>= descr
->nb_items
)
1726 descr
->focus_item
= descr
->nb_items
- 1;
1727 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1733 /***********************************************************************
1734 * LISTBOX_ResetContent
1736 static void LISTBOX_ResetContent( HWND hwnd
, LB_DESCR
*descr
)
1740 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( hwnd
, descr
, i
);
1741 if (descr
->items
) HeapFree( GetProcessHeap(), 0, descr
->items
);
1742 descr
->nb_items
= 0;
1743 descr
->top_item
= 0;
1744 descr
->selected_item
= -1;
1745 descr
->focus_item
= 0;
1746 descr
->anchor_item
= -1;
1747 descr
->items
= NULL
;
1751 /***********************************************************************
1754 static LRESULT
LISTBOX_SetCount( HWND hwnd
, LB_DESCR
*descr
, INT count
)
1758 if (HAS_STRINGS(descr
)) return LB_ERR
;
1759 /* FIXME: this is far from optimal... */
1760 if (count
> descr
->nb_items
)
1762 while (count
> descr
->nb_items
)
1763 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, -1, 0 )) < 0)
1766 else if (count
< descr
->nb_items
)
1768 while (count
< descr
->nb_items
)
1769 if ((ret
= LISTBOX_RemoveItem( hwnd
, descr
, -1 )) < 0)
1776 /***********************************************************************
1779 static LRESULT
LISTBOX_Directory( HWND hwnd
, LB_DESCR
*descr
, UINT attrib
,
1780 LPCWSTR filespec
, BOOL long_names
)
1783 LRESULT ret
= LB_OKAY
;
1784 WIN32_FIND_DATAW entry
;
1787 /* don't scan directory if we just want drives exclusively */
1788 if (attrib
!= (DDL_DRIVES
| DDL_EXCLUSIVE
)) {
1789 /* scan directory */
1790 if ((handle
= FindFirstFileW(filespec
, &entry
)) == INVALID_HANDLE_VALUE
)
1792 int le
= GetLastError();
1793 if ((le
!= ERROR_NO_MORE_FILES
) && (le
!= ERROR_FILE_NOT_FOUND
)) return LB_ERR
;
1800 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1802 static const WCHAR bracketW
[] = { ']',0 };
1803 static const WCHAR dotW
[] = { '.',0 };
1804 if (!(attrib
& DDL_DIRECTORY
) ||
1805 !strcmpW( entry
.cFileName
, dotW
)) continue;
1807 if (!long_names
&& entry
.cAlternateFileName
[0])
1808 strcpyW( buffer
+ 1, entry
.cAlternateFileName
);
1810 strcpyW( buffer
+ 1, entry
.cFileName
);
1811 strcatW(buffer
, bracketW
);
1813 else /* not a directory */
1815 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1816 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1818 if ((attrib
& DDL_EXCLUSIVE
) &&
1819 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1822 if (!long_names
&& entry
.cAlternateFileName
[0])
1823 strcpyW( buffer
, entry
.cAlternateFileName
);
1825 strcpyW( buffer
, entry
.cFileName
);
1827 if (!long_names
) CharLowerW( buffer
);
1828 pos
= LISTBOX_FindFileStrPos( hwnd
, descr
, buffer
);
1829 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, pos
, buffer
)) < 0)
1831 } while (FindNextFileW( handle
, &entry
));
1832 FindClose( handle
);
1837 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1839 WCHAR buffer
[] = {'[','-','a','-',']',0};
1840 WCHAR root
[] = {'A',':','\\',0};
1842 for (drive
= 0; drive
< 26; drive
++, buffer
[2]++, root
[0]++)
1844 if (GetDriveTypeW(root
) <= DRIVE_NO_ROOT_DIR
) continue;
1845 if ((ret
= LISTBOX_InsertString( hwnd
, descr
, -1, buffer
)) < 0)
1853 /***********************************************************************
1854 * LISTBOX_HandleVScroll
1856 static LRESULT
LISTBOX_HandleVScroll( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1860 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1861 switch(LOWORD(wParam
))
1864 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
- 1, TRUE
);
1867 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+ 1, TRUE
);
1870 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
-
1871 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1874 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+
1875 LISTBOX_GetCurrentPageSize( descr
), TRUE
);
1877 case SB_THUMBPOSITION
:
1878 LISTBOX_SetTopItem( hwnd
, descr
, HIWORD(wParam
), TRUE
);
1881 info
.cbSize
= sizeof(info
);
1882 info
.fMask
= SIF_TRACKPOS
;
1883 GetScrollInfo( hwnd
, SB_VERT
, &info
);
1884 LISTBOX_SetTopItem( hwnd
, descr
, info
.nTrackPos
, TRUE
);
1887 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1890 LISTBOX_SetTopItem( hwnd
, descr
, descr
->nb_items
, TRUE
);
1897 /***********************************************************************
1898 * LISTBOX_HandleHScroll
1900 static LRESULT
LISTBOX_HandleHScroll( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1905 if (descr
->style
& LBS_MULTICOLUMN
)
1907 switch(LOWORD(wParam
))
1910 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
-descr
->page_size
,
1914 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+descr
->page_size
,
1918 page
= descr
->width
/ descr
->column_width
;
1919 if (page
< 1) page
= 1;
1920 LISTBOX_SetTopItem( hwnd
, descr
,
1921 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1924 page
= descr
->width
/ descr
->column_width
;
1925 if (page
< 1) page
= 1;
1926 LISTBOX_SetTopItem( hwnd
, descr
,
1927 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1929 case SB_THUMBPOSITION
:
1930 LISTBOX_SetTopItem( hwnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1934 info
.cbSize
= sizeof(info
);
1935 info
.fMask
= SIF_TRACKPOS
;
1936 GetScrollInfo( hwnd
, SB_VERT
, &info
);
1937 LISTBOX_SetTopItem( hwnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1941 LISTBOX_SetTopItem( hwnd
, descr
, 0, TRUE
);
1944 LISTBOX_SetTopItem( hwnd
, descr
, descr
->nb_items
, TRUE
);
1948 else if (descr
->horz_extent
)
1950 switch(LOWORD(wParam
))
1953 LISTBOX_SetHorizontalPos( hwnd
, descr
, descr
->horz_pos
- 1 );
1956 LISTBOX_SetHorizontalPos( hwnd
, descr
, descr
->horz_pos
+ 1 );
1959 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1960 descr
->horz_pos
- descr
->width
);
1963 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1964 descr
->horz_pos
+ descr
->width
);
1966 case SB_THUMBPOSITION
:
1967 LISTBOX_SetHorizontalPos( hwnd
, descr
, HIWORD(wParam
) );
1970 info
.cbSize
= sizeof(info
);
1971 info
.fMask
= SIF_TRACKPOS
;
1972 GetScrollInfo( hwnd
, SB_HORZ
, &info
);
1973 LISTBOX_SetHorizontalPos( hwnd
, descr
, info
.nTrackPos
);
1976 LISTBOX_SetHorizontalPos( hwnd
, descr
, 0 );
1979 LISTBOX_SetHorizontalPos( hwnd
, descr
,
1980 descr
->horz_extent
- descr
->width
);
1987 static LRESULT
LISTBOX_HandleMouseWheel(HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
1989 short gcWheelDelta
= 0;
1990 UINT pulScrollLines
= 3;
1992 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1994 gcWheelDelta
-= (short) HIWORD(wParam
);
1996 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
1998 int cLineScroll
= (int) min((UINT
) descr
->page_size
, pulScrollLines
);
1999 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
2000 LISTBOX_SetTopItem( hwnd
, descr
, descr
->top_item
+ cLineScroll
, TRUE
);
2005 /***********************************************************************
2006 * LISTBOX_HandleLButtonDown
2008 static LRESULT
LISTBOX_HandleLButtonDown( HWND hwnd
, LB_DESCR
*descr
,
2009 WPARAM wParam
, INT x
, INT y
)
2011 INT index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2012 TRACE("[%p]: lbuttondown %d,%d item %d\n", hwnd
, x
, y
, index
);
2013 if (!descr
->caret_on
&& (descr
->in_focus
)) return 0;
2015 if (!descr
->in_focus
)
2017 if( !descr
->lphc
) SetFocus( hwnd
);
2018 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
: descr
->lphc
->self
);
2021 if (index
== -1) return 0;
2023 if (descr
->style
& LBS_EXTENDEDSEL
)
2025 /* we should perhaps make sure that all items are deselected
2026 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2027 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
2028 LISTBOX_SetSelection( hwnd, descr, -1, FALSE, FALSE);
2031 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
2032 if (wParam
& MK_CONTROL
)
2034 LISTBOX_SetCaretIndex( hwnd
, descr
, index
, FALSE
);
2035 LISTBOX_SetSelection( hwnd
, descr
, index
,
2036 !descr
->items
[index
].selected
,
2037 (descr
->style
& LBS_NOTIFY
) != 0);
2039 else LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
2043 LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
2044 LISTBOX_SetSelection( hwnd
, descr
, index
,
2045 (!(descr
->style
& LBS_MULTIPLESEL
) ||
2046 !descr
->items
[index
].selected
),
2047 (descr
->style
& LBS_NOTIFY
) != 0 );
2050 descr
->captured
= TRUE
;
2055 if (descr
->style
& LBS_NOTIFY
)
2056 SendMessageW( descr
->owner
, WM_LBTRACKPOINT
, index
,
2057 MAKELPARAM( x
, y
) );
2058 if (GetWindowLongA( hwnd
, GWL_EXSTYLE
) & WS_EX_DRAGDETECT
)
2065 if (DragDetect( hwnd
, pt
))
2066 SendMessageW( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
2073 /*************************************************************************
2074 * LISTBOX_HandleLButtonDownCombo [Internal]
2076 * Process LButtonDown message for the ComboListBox
2079 * pWnd [I] The windows internal structure
2080 * pDescr [I] The ListBox internal structure
2081 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2082 * x [I] X Mouse Coordinate
2083 * y [I] Y Mouse Coordinate
2086 * 0 since we are processing the WM_LBUTTONDOWN Message
2089 * This function is only to be used when a ListBox is a ComboListBox
2092 static LRESULT
LISTBOX_HandleLButtonDownCombo( HWND hwnd
, LB_DESCR
*pDescr
,
2093 UINT msg
, WPARAM wParam
, INT x
, INT y
)
2095 RECT clientRect
, screenRect
;
2101 GetClientRect(hwnd
, &clientRect
);
2103 if(PtInRect(&clientRect
, mousePos
))
2105 /* MousePos is in client, resume normal processing */
2106 if (msg
== WM_LBUTTONDOWN
)
2108 pDescr
->lphc
->droppedIndex
= pDescr
->nb_items
? pDescr
->selected_item
: -1;
2109 return LISTBOX_HandleLButtonDown( hwnd
, pDescr
, wParam
, x
, y
);
2111 else if (pDescr
->style
& LBS_NOTIFY
)
2112 SEND_NOTIFICATION( hwnd
, pDescr
, LBN_DBLCLK
);
2117 POINT screenMousePos
;
2118 HWND hWndOldCapture
;
2120 /* Check the Non-Client Area */
2121 screenMousePos
= mousePos
;
2122 hWndOldCapture
= GetCapture();
2124 GetWindowRect(hwnd
, &screenRect
);
2125 ClientToScreen(hwnd
, &screenMousePos
);
2127 if(!PtInRect(&screenRect
, screenMousePos
))
2129 LISTBOX_SetCaretIndex( hwnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
);
2130 LISTBOX_SetSelection( hwnd
, pDescr
, pDescr
->lphc
->droppedIndex
, FALSE
, FALSE
);
2131 COMBO_FlipListbox( pDescr
->lphc
, FALSE
, FALSE
);
2136 /* Check to see the NC is a scrollbar */
2138 LONG style
= GetWindowLongA( hwnd
, GWL_STYLE
);
2139 /* Check Vertical scroll bar */
2140 if (style
& WS_VSCROLL
)
2142 clientRect
.right
+= GetSystemMetrics(SM_CXVSCROLL
);
2143 if (PtInRect( &clientRect
, mousePos
))
2145 nHitTestType
= HTVSCROLL
;
2148 /* Check horizontal scroll bar */
2149 if (style
& WS_HSCROLL
)
2151 clientRect
.bottom
+= GetSystemMetrics(SM_CYHSCROLL
);
2152 if (PtInRect( &clientRect
, mousePos
))
2154 nHitTestType
= HTHSCROLL
;
2157 /* Windows sends this message when a scrollbar is clicked
2160 if(nHitTestType
!= 0)
2162 SendMessageW(hwnd
, WM_NCLBUTTONDOWN
, nHitTestType
,
2163 MAKELONG(screenMousePos
.x
, screenMousePos
.y
));
2165 /* Resume the Capture after scrolling is complete
2167 if(hWndOldCapture
!= 0)
2169 SetCapture(hWndOldCapture
);
2176 /***********************************************************************
2177 * LISTBOX_HandleLButtonUp
2179 static LRESULT
LISTBOX_HandleLButtonUp( HWND hwnd
, LB_DESCR
*descr
)
2181 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2182 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2183 LISTBOX_Timer
= LB_TIMER_NONE
;
2184 if (descr
->captured
)
2186 descr
->captured
= FALSE
;
2187 if (GetCapture() == hwnd
) ReleaseCapture();
2188 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2189 SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2195 /***********************************************************************
2196 * LISTBOX_HandleTimer
2198 * Handle scrolling upon a timer event.
2199 * Return TRUE if scrolling should continue.
2201 static LRESULT
LISTBOX_HandleTimer( HWND hwnd
, LB_DESCR
*descr
,
2202 INT index
, TIMER_DIRECTION dir
)
2207 if (descr
->top_item
) index
= descr
->top_item
- 1;
2211 if (descr
->top_item
) index
-= descr
->page_size
;
2214 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( descr
);
2215 if (index
== descr
->focus_item
) index
++;
2216 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
2218 case LB_TIMER_RIGHT
:
2219 if (index
+ descr
->page_size
< descr
->nb_items
)
2220 index
+= descr
->page_size
;
2225 if (index
== descr
->focus_item
) return FALSE
;
2226 LISTBOX_MoveCaret( hwnd
, descr
, index
, FALSE
);
2231 /***********************************************************************
2232 * LISTBOX_HandleSystemTimer
2234 * WM_SYSTIMER handler.
2236 static LRESULT
LISTBOX_HandleSystemTimer( HWND hwnd
, LB_DESCR
*descr
)
2238 if (!LISTBOX_HandleTimer( hwnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
2240 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2241 LISTBOX_Timer
= LB_TIMER_NONE
;
2247 /***********************************************************************
2248 * LISTBOX_HandleMouseMove
2250 * WM_MOUSEMOVE handler.
2252 static void LISTBOX_HandleMouseMove( HWND hwnd
, LB_DESCR
*descr
,
2256 TIMER_DIRECTION dir
= LB_TIMER_NONE
;
2258 if (!descr
->captured
) return;
2260 if (descr
->style
& LBS_MULTICOLUMN
)
2263 else if (y
>= descr
->item_height
* descr
->page_size
)
2264 y
= descr
->item_height
* descr
->page_size
- 1;
2268 dir
= LB_TIMER_LEFT
;
2271 else if (x
>= descr
->width
)
2273 dir
= LB_TIMER_RIGHT
;
2274 x
= descr
->width
- 1;
2279 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
2280 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
2283 index
= LISTBOX_GetItemFromPoint( descr
, x
, y
);
2284 if (index
== -1) index
= descr
->focus_item
;
2285 if (!LISTBOX_HandleTimer( hwnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
2287 /* Start/stop the system timer */
2289 if (dir
!= LB_TIMER_NONE
)
2290 SetSystemTimer( hwnd
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
2291 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
2292 KillSystemTimer( hwnd
, LB_TIMER_ID
);
2293 LISTBOX_Timer
= dir
;
2297 /***********************************************************************
2298 * LISTBOX_HandleKeyDown
2300 static LRESULT
LISTBOX_HandleKeyDown( HWND hwnd
, LB_DESCR
*descr
, WPARAM wParam
)
2303 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
2304 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
2305 bForceSelection
= FALSE
; /* only for single select list */
2307 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2309 caret
= SendMessageW( descr
->owner
, WM_VKEYTOITEM
,
2310 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2312 if (caret
== -2) return 0;
2314 if (caret
== -1) switch(wParam
)
2317 if (descr
->style
& LBS_MULTICOLUMN
)
2319 bForceSelection
= FALSE
;
2320 if (descr
->focus_item
>= descr
->page_size
)
2321 caret
= descr
->focus_item
- descr
->page_size
;
2326 caret
= descr
->focus_item
- 1;
2327 if (caret
< 0) caret
= 0;
2330 if (descr
->style
& LBS_MULTICOLUMN
)
2332 bForceSelection
= FALSE
;
2333 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
2334 caret
= descr
->focus_item
+ descr
->page_size
;
2339 caret
= descr
->focus_item
+ 1;
2340 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2344 if (descr
->style
& LBS_MULTICOLUMN
)
2346 INT page
= descr
->width
/ descr
->column_width
;
2347 if (page
< 1) page
= 1;
2348 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
2350 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(descr
) + 1;
2351 if (caret
< 0) caret
= 0;
2354 if (descr
->style
& LBS_MULTICOLUMN
)
2356 INT page
= descr
->width
/ descr
->column_width
;
2357 if (page
< 1) page
= 1;
2358 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
2360 else caret
= descr
->focus_item
+ LISTBOX_GetCurrentPageSize(descr
) - 1;
2361 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2367 caret
= descr
->nb_items
- 1;
2370 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2371 else if (descr
->style
& LBS_MULTIPLESEL
)
2373 LISTBOX_SetSelection( hwnd
, descr
, descr
->focus_item
,
2374 !descr
->items
[descr
->focus_item
].selected
,
2375 (descr
->style
& LBS_NOTIFY
) != 0 );
2379 bForceSelection
= FALSE
;
2381 if (bForceSelection
) /* focused item is used instead of key */
2382 caret
= descr
->focus_item
;
2385 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
2386 !(GetKeyState( VK_SHIFT
) & 0x8000))
2387 descr
->anchor_item
= caret
;
2388 LISTBOX_MoveCaret( hwnd
, descr
, caret
, TRUE
);
2389 LISTBOX_SetSelection( hwnd
, descr
, caret
, TRUE
, FALSE
);
2390 if (descr
->style
& LBS_NOTIFY
)
2394 /* make sure that combo parent doesn't hide us */
2395 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2397 if (descr
->nb_items
) SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2404 /***********************************************************************
2405 * LISTBOX_HandleChar
2407 static LRESULT
LISTBOX_HandleChar( HWND hwnd
, LB_DESCR
*descr
, WCHAR charW
)
2415 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2417 caret
= SendMessageW( descr
->owner
, WM_CHARTOITEM
,
2418 MAKEWPARAM(charW
, descr
->focus_item
),
2420 if (caret
== -2) return 0;
2423 caret
= LISTBOX_FindString( hwnd
, descr
, descr
->focus_item
, str
, FALSE
);
2426 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2427 LISTBOX_SetSelection( hwnd
, descr
, caret
, TRUE
, FALSE
);
2428 LISTBOX_MoveCaret( hwnd
, descr
, caret
, TRUE
);
2429 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2430 SEND_NOTIFICATION( hwnd
, descr
, LBN_SELCHANGE
);
2436 /***********************************************************************
2439 static BOOL
LISTBOX_Create( HWND hwnd
, LPHEADCOMBO lphc
)
2442 MEASUREITEMSTRUCT mis
;
2445 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2448 GetClientRect( hwnd
, &rect
);
2449 descr
->owner
= GetParent( hwnd
);
2450 descr
->style
= GetWindowLongA( hwnd
, GWL_STYLE
);
2451 descr
->width
= rect
.right
- rect
.left
;
2452 descr
->height
= rect
.bottom
- rect
.top
;
2453 descr
->items
= NULL
;
2454 descr
->nb_items
= 0;
2455 descr
->top_item
= 0;
2456 descr
->selected_item
= -1;
2457 descr
->focus_item
= 0;
2458 descr
->anchor_item
= -1;
2459 descr
->item_height
= 1;
2460 descr
->page_size
= 1;
2461 descr
->column_width
= 150;
2462 descr
->horz_extent
= (descr
->style
& WS_HSCROLL
) ? 1 : 0;
2463 descr
->horz_pos
= 0;
2466 descr
->caret_on
= lphc
? FALSE
: TRUE
;
2467 if (descr
->style
& LBS_NOSEL
) descr
->caret_on
= FALSE
;
2468 descr
->in_focus
= FALSE
;
2469 descr
->captured
= FALSE
;
2471 descr
->locale
= 0; /* FIXME */
2475 if (is_old_app(hwnd
) && ( descr
->style
& ( WS_VSCROLL
| WS_HSCROLL
) ) )
2477 /* Win95 document "List Box Differences" from MSDN:
2478 If a list box in a version 3.x application has either the
2479 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2480 horizontal and vertical scroll bars.
2482 descr
->style
|= WS_VSCROLL
| WS_HSCROLL
;
2488 TRACE_(combo
)("[%p]: resetting owner %p -> %p\n", hwnd
, descr
->owner
, lphc
->self
);
2489 descr
->owner
= lphc
->self
;
2492 SetWindowLongA( hwnd
, 0, (LONG
)descr
);
2494 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2496 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2497 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2498 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2499 descr
->item_height
= LISTBOX_SetFont( hwnd
, descr
, 0 );
2501 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2503 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2505 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2506 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2510 UINT id
= GetWindowLongA( hwnd
, GWL_ID
);
2511 mis
.CtlType
= ODT_LISTBOX
;
2516 mis
.itemHeight
= descr
->item_height
;
2517 SendMessageW( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2518 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2522 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr
->owner
, descr
->style
, descr
->width
, descr
->height
);
2527 /***********************************************************************
2530 static BOOL
LISTBOX_Destroy( HWND hwnd
, LB_DESCR
*descr
)
2532 LISTBOX_ResetContent( hwnd
, descr
);
2533 SetWindowLongA( hwnd
, 0, 0 );
2534 HeapFree( GetProcessHeap(), 0, descr
);
2539 /***********************************************************************
2540 * ListBoxWndProc_common
2542 static LRESULT WINAPI
ListBoxWndProc_common( HWND hwnd
, UINT msg
,
2543 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
2548 if (!(descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 )))
2550 if (msg
== WM_CREATE
)
2552 if (!LISTBOX_Create( hwnd
, NULL
))
2554 TRACE("creating wnd=%p descr=%lx\n", hwnd
, GetWindowLongA( hwnd
, 0 ) );
2557 /* Ignore all other messages before we get a WM_CREATE */
2558 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
2559 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2562 //TRACE("[%p]: msg %s wp %08x lp %08lx\n",
2563 // hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
2567 case LB_RESETCONTENT16
:
2569 case LB_RESETCONTENT
:
2570 LISTBOX_ResetContent( hwnd
, descr
);
2571 LISTBOX_UpdateScroll( hwnd
, descr
);
2572 InvalidateRect( hwnd
, NULL
, TRUE
);
2576 case LB_ADDSTRING16
:
2577 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2584 if(unicode
|| !HAS_STRINGS(descr
))
2585 textW
= (LPWSTR
)lParam
;
2588 LPSTR textA
= (LPSTR
)lParam
;
2589 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2590 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2591 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2593 wParam
= LISTBOX_FindStringPos( hwnd
, descr
, textW
, FALSE
);
2594 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2595 if (!unicode
&& HAS_STRINGS(descr
))
2596 HeapFree(GetProcessHeap(), 0, textW
);
2601 case LB_INSERTSTRING16
:
2602 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2603 wParam
= (INT
)(INT16
)wParam
;
2606 case LB_INSERTSTRING
:
2610 if(unicode
|| !HAS_STRINGS(descr
))
2611 textW
= (LPWSTR
)lParam
;
2614 LPSTR textA
= (LPSTR
)lParam
;
2615 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2616 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2617 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2619 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2620 if(!unicode
&& HAS_STRINGS(descr
))
2621 HeapFree(GetProcessHeap(), 0, textW
);
2627 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2634 if(unicode
|| !HAS_STRINGS(descr
))
2635 textW
= (LPWSTR
)lParam
;
2638 LPSTR textA
= (LPSTR
)lParam
;
2639 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2640 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2641 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2643 wParam
= LISTBOX_FindFileStrPos( hwnd
, descr
, textW
);
2644 ret
= LISTBOX_InsertString( hwnd
, descr
, wParam
, textW
);
2645 if(!unicode
&& HAS_STRINGS(descr
))
2646 HeapFree(GetProcessHeap(), 0, textW
);
2651 case LB_DELETESTRING16
:
2653 case LB_DELETESTRING
:
2654 if (LISTBOX_RemoveItem( hwnd
, descr
, wParam
) != LB_ERR
)
2655 return descr
->nb_items
;
2660 case LB_GETITEMDATA16
:
2662 case LB_GETITEMDATA
:
2663 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2665 return descr
->items
[wParam
].data
;
2668 case LB_SETITEMDATA16
:
2670 case LB_SETITEMDATA
:
2671 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2673 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2680 return descr
->nb_items
;
2684 lParam
= (LPARAM
)MapSL(lParam
);
2688 return LISTBOX_GetText( descr
, wParam
, lParam
, unicode
);
2691 case LB_GETTEXTLEN16
:
2695 if ((INT
)wParam
>= descr
->nb_items
|| (INT
)wParam
< 0)
2697 if (!HAS_STRINGS(descr
)) return sizeof(DWORD
);
2698 if (unicode
) return strlenW( descr
->items
[wParam
].str
);
2699 return WideCharToMultiByte( CP_ACP
, 0, descr
->items
[wParam
].str
,
2700 strlenW(descr
->items
[wParam
].str
), NULL
, 0, NULL
, NULL
);
2703 case LB_GETCURSEL16
:
2706 if (descr
->nb_items
==0)
2708 if (!IS_MULTISELECT(descr
))
2709 return descr
->selected_item
;
2711 if (descr
->selected_item
!=-1)
2712 return descr
->selected_item
;
2714 return descr
->focus_item
;
2715 /* otherwise, if the user tries to move the selection with the */
2716 /* arrow keys, we will give the application something to choke on */
2718 case LB_GETTOPINDEX16
:
2720 case LB_GETTOPINDEX
:
2721 return descr
->top_item
;
2724 case LB_GETITEMHEIGHT16
:
2726 case LB_GETITEMHEIGHT
:
2727 return LISTBOX_GetItemHeight( descr
, wParam
);
2730 case LB_SETITEMHEIGHT16
:
2731 lParam
= LOWORD(lParam
);
2734 case LB_SETITEMHEIGHT
:
2735 return LISTBOX_SetItemHeight( hwnd
, descr
, wParam
, lParam
, TRUE
);
2737 case LB_ITEMFROMPOINT
:
2742 pt
.x
= LOWORD(lParam
);
2743 pt
.y
= HIWORD(lParam
);
2746 rect
.right
= descr
->width
;
2747 rect
.bottom
= descr
->height
;
2749 return MAKELONG( LISTBOX_GetItemFromPoint(descr
, pt
.x
, pt
.y
),
2750 !PtInRect( &rect
, pt
) );
2754 case LB_SETCARETINDEX16
:
2756 case LB_SETCARETINDEX
:
2757 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2758 if (LISTBOX_SetCaretIndex( hwnd
, descr
, wParam
, !lParam
) == LB_ERR
)
2766 case LB_GETCARETINDEX16
:
2768 case LB_GETCARETINDEX
:
2769 return descr
->focus_item
;
2772 case LB_SETTOPINDEX16
:
2774 case LB_SETTOPINDEX
:
2775 return LISTBOX_SetTopItem( hwnd
, descr
, wParam
, TRUE
);
2778 case LB_SETCOLUMNWIDTH16
:
2780 case LB_SETCOLUMNWIDTH
:
2781 return LISTBOX_SetColumnWidth( hwnd
, descr
, wParam
);
2784 case LB_GETITEMRECT16
:
2787 ret
= LISTBOX_GetItemRect( descr
, (INT16
)wParam
, &rect
);
2788 CONV_RECT32TO16( &rect
, MapSL(lParam
) );
2793 case LB_GETITEMRECT
:
2794 return LISTBOX_GetItemRect( descr
, wParam
, (RECT
*)lParam
);
2797 case LB_FINDSTRING16
:
2798 wParam
= (INT
)(INT16
)wParam
;
2799 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2806 if(unicode
|| !HAS_STRINGS(descr
))
2807 textW
= (LPWSTR
)lParam
;
2810 LPSTR textA
= (LPSTR
)lParam
;
2811 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2812 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2813 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2815 ret
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, FALSE
);
2816 if(!unicode
&& HAS_STRINGS(descr
))
2817 HeapFree(GetProcessHeap(), 0, textW
);
2822 case LB_FINDSTRINGEXACT16
:
2823 wParam
= (INT
)(INT16
)wParam
;
2824 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2827 case LB_FINDSTRINGEXACT
:
2831 if(unicode
|| !HAS_STRINGS(descr
))
2832 textW
= (LPWSTR
)lParam
;
2835 LPSTR textA
= (LPSTR
)lParam
;
2836 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2837 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2838 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2840 ret
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, TRUE
);
2841 if(!unicode
&& HAS_STRINGS(descr
))
2842 HeapFree(GetProcessHeap(), 0, textW
);
2847 case LB_SELECTSTRING16
:
2848 wParam
= (INT
)(INT16
)wParam
;
2849 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)MapSL(lParam
);
2852 case LB_SELECTSTRING
:
2857 if(HAS_STRINGS(descr
))
2858 TRACE("LB_SELECTSTRING: %s\n", unicode
? debugstr_w((LPWSTR
)lParam
) :
2859 debugstr_a((LPSTR
)lParam
));
2860 if(unicode
|| !HAS_STRINGS(descr
))
2861 textW
= (LPWSTR
)lParam
;
2864 LPSTR textA
= (LPSTR
)lParam
;
2865 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2866 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2867 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2869 index
= LISTBOX_FindString( hwnd
, descr
, wParam
, textW
, FALSE
);
2870 if(!unicode
&& HAS_STRINGS(descr
))
2871 HeapFree(GetProcessHeap(), 0, textW
);
2872 if (index
!= LB_ERR
)
2874 LISTBOX_MoveCaret( hwnd
, descr
, index
, TRUE
);
2875 LISTBOX_SetSelection( hwnd
, descr
, index
, TRUE
, FALSE
);
2882 wParam
= (INT
)(INT16
)wParam
;
2886 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2888 return descr
->items
[wParam
].selected
;
2892 lParam
= (INT
)(INT16
)lParam
;
2896 return LISTBOX_SetSelection( hwnd
, descr
, lParam
, wParam
, FALSE
);
2899 case LB_SETCURSEL16
:
2900 wParam
= (INT
)(INT16
)wParam
;
2904 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2905 LISTBOX_SetCaretIndex( hwnd
, descr
, wParam
, TRUE
);
2906 return LISTBOX_SetSelection( hwnd
, descr
, wParam
, TRUE
, FALSE
);
2909 case LB_GETSELCOUNT16
:
2911 case LB_GETSELCOUNT
:
2912 return LISTBOX_GetSelCount( descr
);
2915 case LB_GETSELITEMS16
:
2916 return LISTBOX_GetSelItems16( descr
, wParam
, (LPINT16
)MapSL(lParam
) );
2919 case LB_GETSELITEMS
:
2920 return LISTBOX_GetSelItems( descr
, wParam
, (LPINT
)lParam
);
2923 case LB_SELITEMRANGE16
:
2925 case LB_SELITEMRANGE
:
2926 if (LOWORD(lParam
) <= HIWORD(lParam
))
2927 return LISTBOX_SelectItemRange( hwnd
, descr
, LOWORD(lParam
),
2928 HIWORD(lParam
), wParam
);
2930 return LISTBOX_SelectItemRange( hwnd
, descr
, HIWORD(lParam
),
2931 LOWORD(lParam
), wParam
);
2934 case LB_SELITEMRANGEEX16
:
2936 case LB_SELITEMRANGEEX
:
2937 if ((INT
)lParam
>= (INT
)wParam
)
2938 return LISTBOX_SelectItemRange( hwnd
, descr
, wParam
, lParam
, TRUE
);
2940 return LISTBOX_SelectItemRange( hwnd
, descr
, lParam
, wParam
, FALSE
);
2943 case LB_GETHORIZONTALEXTENT16
:
2945 case LB_GETHORIZONTALEXTENT
:
2946 return descr
->horz_extent
;
2949 case LB_SETHORIZONTALEXTENT16
:
2951 case LB_SETHORIZONTALEXTENT
:
2952 return LISTBOX_SetHorizontalExtent( hwnd
, descr
, wParam
);
2955 case LB_GETANCHORINDEX16
:
2957 case LB_GETANCHORINDEX
:
2958 return descr
->anchor_item
;
2961 case LB_SETANCHORINDEX16
:
2962 wParam
= (INT
)(INT16
)wParam
;
2965 case LB_SETANCHORINDEX
:
2966 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2968 descr
->anchor_item
= (INT
)wParam
;
2973 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2974 * be set automatically (this is different in Win32) */
2975 if (wParam
& DDL_DRIVES
) wParam
|= DDL_EXCLUSIVE
;
2976 lParam
= (LPARAM
)MapSL(lParam
);
2984 textW
= (LPWSTR
)lParam
;
2987 LPSTR textA
= (LPSTR
)lParam
;
2988 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
2989 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
2990 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
2992 ret
= LISTBOX_Directory( hwnd
, descr
, wParam
, textW
, msg
== LB_DIR
);
2994 HeapFree(GetProcessHeap(), 0, textW
);
2999 return descr
->locale
;
3002 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
3005 case LB_INITSTORAGE
:
3006 return LISTBOX_InitStorage( hwnd
, descr
, wParam
);
3009 return LISTBOX_SetCount( hwnd
, descr
, (INT
)wParam
);
3012 case LB_SETTABSTOPS16
:
3013 return LISTBOX_SetTabStops( hwnd
, descr
, (INT
)(INT16
)wParam
, MapSL(lParam
), TRUE
);
3016 case LB_SETTABSTOPS
:
3017 return LISTBOX_SetTabStops( hwnd
, descr
, wParam
, (LPINT
)lParam
, FALSE
);
3023 if (descr
->caret_on
)
3025 descr
->caret_on
= TRUE
;
3026 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3027 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
3034 if (!descr
->caret_on
)
3036 descr
->caret_on
= FALSE
;
3037 if ((descr
->focus_item
!= -1) && (descr
->in_focus
))
3038 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
3042 return LISTBOX_Destroy( hwnd
, descr
);
3045 InvalidateRect( hwnd
, NULL
, TRUE
);
3049 LISTBOX_SetRedraw( hwnd
, descr
, wParam
!= 0 );
3053 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
3058 HDC hdc
= ( wParam
) ? ((HDC
)wParam
) : BeginPaint( hwnd
, &ps
);
3059 ret
= LISTBOX_Paint( hwnd
, descr
, hdc
);
3060 if( !wParam
) EndPaint( hwnd
, &ps
);
3064 LISTBOX_UpdateSize( hwnd
, descr
);
3067 return (LRESULT
)descr
->font
;
3069 LISTBOX_SetFont( hwnd
, descr
, (HFONT
)wParam
);
3070 if (lParam
) InvalidateRect( hwnd
, 0, TRUE
);
3073 descr
->in_focus
= TRUE
;
3074 descr
->caret_on
= TRUE
;
3075 if (descr
->focus_item
!= -1)
3076 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
3077 SEND_NOTIFICATION( hwnd
, descr
, LBN_SETFOCUS
);
3080 descr
->in_focus
= FALSE
;
3081 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
3082 LISTBOX_RepaintItem( hwnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
3083 SEND_NOTIFICATION( hwnd
, descr
, LBN_KILLFOCUS
);
3086 return LISTBOX_HandleHScroll( hwnd
, descr
, wParam
);
3088 return LISTBOX_HandleVScroll( hwnd
, descr
, wParam
);
3090 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
3091 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
3092 return LISTBOX_HandleMouseWheel( hwnd
, descr
, wParam
);
3093 case WM_LBUTTONDOWN
:
3094 return LISTBOX_HandleLButtonDown( hwnd
, descr
, wParam
,
3095 (INT16
)LOWORD(lParam
),
3096 (INT16
)HIWORD(lParam
) );
3097 case WM_LBUTTONDBLCLK
:
3098 if (descr
->style
& LBS_NOTIFY
)
3099 SEND_NOTIFICATION( hwnd
, descr
, LBN_DBLCLK
);
3102 if (GetCapture() == hwnd
)
3103 LISTBOX_HandleMouseMove( hwnd
, descr
, (INT16
)LOWORD(lParam
),
3104 (INT16
)HIWORD(lParam
) );
3107 return LISTBOX_HandleLButtonUp( hwnd
, descr
);
3109 return LISTBOX_HandleKeyDown( hwnd
, descr
, wParam
);
3114 charW
= (WCHAR
)wParam
;
3117 CHAR charA
= (CHAR
)wParam
;
3118 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
3120 return LISTBOX_HandleChar( hwnd
, descr
, charW
);
3123 return LISTBOX_HandleSystemTimer( hwnd
, descr
);
3125 if ((IS_OWNERDRAW(descr
)) && !(descr
->style
& LBS_DISPLAYCHANGED
))
3128 HBRUSH hbrush
= (HBRUSH
)SendMessageW( descr
->owner
, WM_CTLCOLORLISTBOX
,
3129 wParam
, (LPARAM
)hwnd
);
3130 TRACE("hbrush = %p\n", hbrush
);
3132 hbrush
= GetSysColorBrush(COLOR_WINDOW
);
3135 GetClientRect(hwnd
, &rect
);
3136 FillRect((HDC
)wParam
, &rect
, hbrush
);
3142 return unicode
? SendMessageW( descr
->owner
, msg
, wParam
, lParam
) :
3143 SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
3147 if ((msg
>= WM_USER
) && (msg
< 0xc000))
3148 WARN("[%p]: unknown msg %04x wp %08x lp %08lx\n",
3149 hwnd
, msg
, wParam
, lParam
);
3150 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3151 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3156 /***********************************************************************
3159 * This is just a wrapper for the real wndproc, it only does window locking
3162 static LRESULT WINAPI
ListBoxWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3164 if (!IsWindow(hwnd
)) return 0;
3165 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3168 /***********************************************************************
3171 static LRESULT WINAPI
ListBoxWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3173 if (!IsWindow(hwnd
)) return 0;
3174 return ListBoxWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);
3177 /***********************************************************************
3178 * ComboLBWndProc_common
3180 * The real combo listbox wndproc
3182 static LRESULT WINAPI
ComboLBWndProc_common( HWND hwnd
, UINT msg
,
3183 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
3189 if (!(descr
= (LB_DESCR
*)GetWindowLongA( hwnd
, 0 )))
3191 if (msg
== WM_CREATE
)
3193 CREATESTRUCTA
*lpcs
= (CREATESTRUCTA
*)lParam
;
3194 TRACE_(combo
)("\tpassed parent handle = %p\n",lpcs
->lpCreateParams
);
3195 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
3196 return LISTBOX_Create( hwnd
, lphc
);
3198 /* Ignore all other messages before we get a WM_CREATE */
3199 return unicode
? DefWindowProcW( hwnd
, msg
, wParam
, lParam
) :
3200 DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
3203 //TRACE_(combo)("[%p]: msg %s wp %08x lp %08lx\n",
3204 // hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
3206 if ((lphc
= descr
->lphc
) != NULL
)
3211 if ( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) )
3217 mousePos
.x
= (INT16
)LOWORD(lParam
);
3218 mousePos
.y
= (INT16
)HIWORD(lParam
);
3221 * If we are in a dropdown combobox, we simulate that
3222 * the mouse is captured to show the tracking of the item.
3224 GetClientRect(hwnd
, &clientRect
);
3226 if (PtInRect( &clientRect
, mousePos
))
3228 captured
= descr
->captured
;
3229 descr
->captured
= TRUE
;
3231 LISTBOX_HandleMouseMove( hwnd
, descr
,
3232 mousePos
.x
, mousePos
.y
);
3234 descr
->captured
= captured
;
3239 LISTBOX_HandleMouseMove( hwnd
, descr
,
3240 mousePos
.x
, mousePos
.y
);
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) ||
3270 (!PtInRect( &clientRect
, mousePos
)) )
3272 LISTBOX_MoveCaret( hwnd
, descr
, lphc
->droppedIndex
, FALSE
);
3275 return LISTBOX_HandleLButtonUp( hwnd
, descr
);
3276 case WM_LBUTTONDBLCLK
:
3277 case WM_LBUTTONDOWN
:
3278 return LISTBOX_HandleLButtonDownCombo(hwnd
, descr
, msg
, wParam
,
3279 (INT16
)LOWORD(lParam
),
3280 (INT16
)HIWORD(lParam
) );
3284 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3286 /* for some reason(?) Windows makes it possible to
3287 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3289 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
3290 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
3291 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
3293 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
3297 return LISTBOX_HandleKeyDown( hwnd
, descr
, wParam
);
3300 case LB_SETCURSEL16
:
3303 lRet
= unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3304 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3305 lRet
=(lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
3308 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
3314 /* default handling: call listbox wnd proc */
3315 lRet
= unicode
? ListBoxWndProcW( hwnd
, msg
, wParam
, lParam
) :
3316 ListBoxWndProcA( hwnd
, msg
, wParam
, lParam
);
3318 TRACE_(combo
)("\t default on msg [%04x]\n", (UINT16
)msg
);
3323 /***********************************************************************
3326 * NOTE: in Windows, winproc address of the ComboLBox is the same
3327 * as that of the Listbox.
3329 LRESULT WINAPI
ComboLBWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3331 if (!IsWindow(hwnd
)) return 0;
3332 return ComboLBWndProc_common( hwnd
, msg
, wParam
, lParam
, FALSE
);
3335 /***********************************************************************
3338 LRESULT WINAPI
ComboLBWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3340 if (!IsWindow(hwnd
)) return 0;
3341 return ComboLBWndProc_common( hwnd
, msg
, wParam
, lParam
, TRUE
);
3344 /***********************************************************************
3348 GetListBoxInfo(HWND hwnd
)