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
22 * This code was audited for completeness against the documented features
23 * of Comctl32.dll version 6.0 on Oct. 9, 2004, by Dimitrie O. Paun.
25 * Unless otherwise noted, we believe this code to be complete, as per
26 * the specification mentioned above.
27 * If you discover missing features, or bugs, please note them below.
33 * - LB_SETLOCALE: some FIXMEs remain
34 * - LBS_USETABSTOPS: some FIXMEs remain
44 #include "wine/winuser16.h"
45 #include "wine/winbase16.h"
47 #include "wine/unicode.h"
53 #include "wine/debug.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(listbox);
58 /* Items array granularity */
59 #define LB_ARRAY_GRANULARITY 16
61 /* Scrolling timeout in ms */
62 #define LB_SCROLL_TIMEOUT 50
64 /* Listbox system timer id */
67 /* flag listbox changed while setredraw false - internal style */
68 #define LBS_DISPLAYCHANGED 0x80000000
73 LPWSTR str; /* Item text */
74 BOOL selected; /* Is item selected? */
75 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
76 DWORD data; /* User data */
79 /* Listbox structure */
82 HWND owner; /* Owner window to send notifications to */
83 UINT style; /* Window style */
84 INT width; /* Window width */
85 INT height; /* Window height */
86 LB_ITEMDATA *items; /* Array of items */
87 INT nb_items; /* Number of items */
88 INT top_item; /* Top visible item */
89 INT selected_item; /* Selected item */
90 INT focus_item; /* Item that has the focus */
91 INT anchor_item; /* Anchor item for extended selection */
92 INT item_height; /* Default item height */
93 INT page_size; /* Items per listbox page */
94 INT column_width; /* Column width for multi-column listboxes */
95 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
96 INT horz_pos; /* Horizontal position */
97 INT nb_tabs; /* Number of tabs in array */
98 INT *tabs; /* Array of tabs */
99 BOOL caret_on; /* Is caret on? */
100 BOOL captured; /* Is mouse captured? */
102 HFONT font; /* Current font */
103 LCID locale; /* Current locale for string comparisons */
104 LPHEADCOMBO lphc; /* ComboLBox */
108 #define IS_OWNERDRAW(descr) \
109 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
111 #define HAS_STRINGS(descr) \
112 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
115 #define IS_MULTISELECT(descr) \
116 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
117 !((descr)->style & LBS_NOSEL))
119 #define SEND_NOTIFICATION(hwnd,descr,code) \
120 (SendMessageW( (descr)->owner, WM_COMMAND, \
121 MAKEWPARAM( GetWindowLongPtrW((hwnd),GWLP_ID), (code)), (LPARAM)(hwnd) ))
123 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
125 /* Current timer status */
135 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
137 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
138 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
140 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect );
142 /*********************************************************************
143 * listbox class descriptor
145 const struct builtin_class_descr LISTBOX_builtin_class =
147 "ListBox", /* name */
148 CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
149 ListBoxWndProcA, /* procA */
150 ListBoxWndProcW, /* procW */
151 sizeof(LB_DESCR *), /* extra */
152 IDC_ARROW, /* cursor */
157 /*********************************************************************
158 * combolbox class descriptor
160 const struct builtin_class_descr COMBOLBOX_builtin_class =
162 "ComboLBox", /* name */
163 CS_DBLCLKS | CS_SAVEBITS, /* style */
164 ListBoxWndProcA, /* procA */
165 ListBoxWndProcW, /* procW */
166 sizeof(LB_DESCR *), /* extra */
167 IDC_ARROW, /* cursor */
172 /* check whether app is a Win 3.1 app */
173 inline static BOOL is_old_app( HWND hwnd )
175 return (GetExpWinVer16( GetWindowLongPtrW(hwnd,GWLP_HINSTANCE) ) & 0xFF00 ) == 0x0300;
179 /***********************************************************************
182 void LISTBOX_Dump( HWND hwnd )
186 LB_DESCR *descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 );
188 TRACE( "Listbox:\n" );
189 TRACE( "hwnd=%p descr=%08x items=%d top=%d\n",
190 hwnd, (UINT)descr, descr->nb_items, descr->top_item );
191 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
193 TRACE( "%4d: %-40s %d %08lx %3d\n",
194 i, debugstr_w(item->str), item->selected, item->data, item->height );
199 /***********************************************************************
200 * LISTBOX_GetCurrentPageSize
202 * Return the current page size
204 static INT LISTBOX_GetCurrentPageSize( LB_DESCR *descr )
207 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
208 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
210 if ((height += descr->items[i].height) > descr->height) break;
212 if (i == descr->top_item) return 1;
213 else return i - descr->top_item;
217 /***********************************************************************
218 * LISTBOX_GetMaxTopIndex
220 * Return the maximum possible index for the top of the listbox.
222 static INT LISTBOX_GetMaxTopIndex( LB_DESCR *descr )
226 if (descr->style & LBS_OWNERDRAWVARIABLE)
228 page = descr->height;
229 for (max = descr->nb_items - 1; max >= 0; max--)
230 if ((page -= descr->items[max].height) < 0) break;
231 if (max < descr->nb_items - 1) max++;
233 else if (descr->style & LBS_MULTICOLUMN)
235 if ((page = descr->width / descr->column_width) < 1) page = 1;
236 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
237 max = (max - page) * descr->page_size;
241 max = descr->nb_items - descr->page_size;
243 if (max < 0) max = 0;
248 /***********************************************************************
249 * LISTBOX_UpdateScroll
251 * Update the scrollbars. Should be called whenever the content
252 * of the listbox changes.
254 static void LISTBOX_UpdateScroll( HWND hwnd, LB_DESCR *descr )
258 /* Check the listbox scroll bar flags individually before we call
259 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
260 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
261 scroll bar when we do not need one.
262 if (!(descr->style & WS_VSCROLL)) return;
265 /* It is important that we check descr->style, and not wnd->dwStyle,
266 for WS_VSCROLL, as the former is exactly the one passed in
267 argument to CreateWindow.
268 In Windows (and from now on in Wine :) a listbox created
269 with such a style (no WS_SCROLL) does not update
270 the scrollbar with listbox-related data, thus letting
271 the programmer use it for his/her own purposes. */
273 if (descr->style & LBS_NOREDRAW) return;
274 info.cbSize = sizeof(info);
276 if (descr->style & LBS_MULTICOLUMN)
279 info.nMax = (descr->nb_items - 1) / descr->page_size;
280 info.nPos = descr->top_item / descr->page_size;
281 info.nPage = descr->width / descr->column_width;
282 if (info.nPage < 1) info.nPage = 1;
283 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
284 if (descr->style & LBS_DISABLENOSCROLL)
285 info.fMask |= SIF_DISABLENOSCROLL;
286 if (descr->style & WS_HSCROLL)
287 SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
289 info.fMask = SIF_RANGE;
290 if (descr->style & WS_VSCROLL)
291 SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
296 info.nMax = descr->nb_items - 1;
297 info.nPos = descr->top_item;
298 info.nPage = LISTBOX_GetCurrentPageSize( descr );
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_VSCROLL)
303 SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
305 if (descr->horz_extent)
308 info.nMax = descr->horz_extent - 1;
309 info.nPos = descr->horz_pos;
310 info.nPage = descr->width;
311 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
312 if (descr->style & LBS_DISABLENOSCROLL)
313 info.fMask |= SIF_DISABLENOSCROLL;
314 if (descr->style & WS_HSCROLL)
315 SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
321 /***********************************************************************
324 * Set the top item of the listbox, scrolling up or down if necessary.
326 static LRESULT LISTBOX_SetTopItem( HWND hwnd, LB_DESCR *descr, INT index,
329 INT max = LISTBOX_GetMaxTopIndex( descr );
330 if (index > max) index = max;
331 if (index < 0) index = 0;
332 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
333 if (descr->top_item == index) return LB_OKAY;
334 if (descr->style & LBS_MULTICOLUMN)
336 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
337 if (scroll && (abs(diff) < descr->width))
338 ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
339 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
347 if (descr->style & LBS_OWNERDRAWVARIABLE)
351 if (index > descr->top_item)
353 for (i = index - 1; i >= descr->top_item; i--)
354 diff -= descr->items[i].height;
358 for (i = index; i < descr->top_item; i++)
359 diff += descr->items[i].height;
363 diff = (descr->top_item - index) * descr->item_height;
365 if (abs(diff) < descr->height)
366 ScrollWindowEx( hwnd, 0, diff, NULL, NULL, 0, NULL,
367 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
371 if (!scroll) InvalidateRect( hwnd, NULL, TRUE );
372 descr->top_item = index;
373 LISTBOX_UpdateScroll( hwnd, descr );
378 /***********************************************************************
381 * Update the page size. Should be called when the size of
382 * the client area or the item height changes.
384 static void LISTBOX_UpdatePage( HWND hwnd, LB_DESCR *descr )
388 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
390 if (page_size == descr->page_size) return;
391 descr->page_size = page_size;
392 if (descr->style & LBS_MULTICOLUMN)
393 InvalidateRect( hwnd, NULL, TRUE );
394 LISTBOX_SetTopItem( hwnd, descr, descr->top_item, FALSE );
398 /***********************************************************************
401 * Update the size of the listbox. Should be called when the size of
402 * the client area changes.
404 static void LISTBOX_UpdateSize( HWND hwnd, LB_DESCR *descr )
408 GetClientRect( hwnd, &rect );
409 descr->width = rect.right - rect.left;
410 descr->height = rect.bottom - rect.top;
411 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
416 GetWindowRect( hwnd, &rect );
417 if(descr->item_height != 0)
418 remaining = descr->height % descr->item_height;
421 if ((descr->height > descr->item_height) && remaining)
423 if (is_old_app(hwnd))
424 { /* give a margin for error to 16 bits programs - if we need
425 less than the height of the nonclient area, round to the
426 *next* number of items */
427 int ncheight = rect.bottom - rect.top - descr->height;
428 if ((descr->item_height - remaining) <= ncheight)
429 remaining = remaining - descr->item_height;
431 TRACE("[%p]: changing height %d -> %d\n",
432 hwnd, descr->height, descr->height - remaining );
433 SetWindowPos( hwnd, 0, 0, 0, rect.right - rect.left,
434 rect.bottom - rect.top - remaining,
435 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
439 TRACE("[%p]: new size = %d,%d\n", hwnd, descr->width, descr->height );
440 LISTBOX_UpdatePage( hwnd, descr );
441 LISTBOX_UpdateScroll( hwnd, descr );
443 /* Invalidate the focused item so it will be repainted correctly */
444 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
446 InvalidateRect( hwnd, &rect, FALSE );
451 /***********************************************************************
452 * LISTBOX_GetItemRect
454 * Get the rectangle enclosing an item, in listbox client coordinates.
455 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
457 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect )
459 /* Index <= 0 is legal even on empty listboxes */
460 if (index && (index >= descr->nb_items)) return -1;
461 SetRect( rect, 0, 0, descr->width, descr->height );
462 if (descr->style & LBS_MULTICOLUMN)
464 INT col = (index / descr->page_size) -
465 (descr->top_item / descr->page_size);
466 rect->left += col * descr->column_width;
467 rect->right = rect->left + descr->column_width;
468 rect->top += (index % descr->page_size) * descr->item_height;
469 rect->bottom = rect->top + descr->item_height;
471 else if (descr->style & LBS_OWNERDRAWVARIABLE)
474 rect->right += descr->horz_pos;
475 if ((index >= 0) && (index < descr->nb_items))
477 if (index < descr->top_item)
479 for (i = descr->top_item-1; i >= index; i--)
480 rect->top -= descr->items[i].height;
484 for (i = descr->top_item; i < index; i++)
485 rect->top += descr->items[i].height;
487 rect->bottom = rect->top + descr->items[index].height;
493 rect->top += (index - descr->top_item) * descr->item_height;
494 rect->bottom = rect->top + descr->item_height;
495 rect->right += descr->horz_pos;
498 return ((rect->left < descr->width) && (rect->right > 0) &&
499 (rect->top < descr->height) && (rect->bottom > 0));
503 /***********************************************************************
504 * LISTBOX_GetItemFromPoint
506 * Return the item nearest from point (x,y) (in client coordinates).
508 static INT LISTBOX_GetItemFromPoint( LB_DESCR *descr, INT x, INT y )
510 INT index = descr->top_item;
512 if (!descr->nb_items) return -1; /* No items */
513 if (descr->style & LBS_OWNERDRAWVARIABLE)
518 while (index < descr->nb_items)
520 if ((pos += descr->items[index].height) > y) break;
529 if ((pos -= descr->items[index].height) <= y) break;
533 else if (descr->style & LBS_MULTICOLUMN)
535 if (y >= descr->item_height * descr->page_size) return -1;
536 if (y >= 0) index += y / descr->item_height;
537 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
538 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
542 index += (y / descr->item_height);
544 if (index < 0) return 0;
545 if (index >= descr->nb_items) return -1;
550 /***********************************************************************
555 static void LISTBOX_PaintItem( HWND hwnd, LB_DESCR *descr, HDC hdc,
556 const RECT *rect, INT index, UINT action, BOOL ignoreFocus )
558 LB_ITEMDATA *item = NULL;
559 if (index < descr->nb_items) item = &descr->items[index];
561 if (IS_OWNERDRAW(descr))
566 UINT id = (UINT)GetWindowLongPtrW( hwnd, GWLP_ID );
570 if (action == ODA_FOCUS)
571 DrawFocusRect( hdc, rect );
573 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
577 /* some programs mess with the clipping region when
578 drawing the item, *and* restore the previous region
579 after they are done, so a region has better to exist
580 else everything ends clipped */
581 GetClientRect(hwnd, &r);
582 hrgn = CreateRectRgnIndirect(&r);
583 SelectClipRgn( hdc, hrgn);
584 DeleteObject( hrgn );
586 dis.CtlType = ODT_LISTBOX;
589 dis.itemAction = action;
593 if (item && item->selected) dis.itemState |= ODS_SELECTED;
594 if (!ignoreFocus && (descr->focus_item == index) &&
596 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
597 if (!IsWindowEnabled(hwnd)) dis.itemState |= ODS_DISABLED;
598 dis.itemData = item ? item->data : 0;
600 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%ld,%ld-%ld,%ld\n",
601 hwnd, index, item ? debugstr_w(item->str) : "", action,
602 dis.itemState, rect->left, rect->top, rect->right, rect->bottom );
603 SendMessageW(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
607 COLORREF oldText = 0, oldBk = 0;
609 if (action == ODA_FOCUS)
611 DrawFocusRect( hdc, rect );
614 if (item && item->selected)
616 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
617 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
620 TRACE("[%p]: painting %d (%s) action=%02x rect=%ld,%ld-%ld,%ld\n",
621 hwnd, index, item ? debugstr_w(item->str) : "", action,
622 rect->left, rect->top, rect->right, rect->bottom );
624 ExtTextOutW( hdc, rect->left + 1, rect->top,
625 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
626 else if (!(descr->style & LBS_USETABSTOPS))
627 ExtTextOutW( hdc, rect->left + 1, rect->top,
628 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
629 strlenW(item->str), NULL );
632 /* Output empty string to paint background in the full width. */
633 ExtTextOutW( hdc, rect->left + 1, rect->top,
634 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
635 TabbedTextOutW( hdc, rect->left + 1 , rect->top,
636 item->str, strlenW(item->str),
637 descr->nb_tabs, descr->tabs, 0);
639 if (item && item->selected)
641 SetBkColor( hdc, oldBk );
642 SetTextColor( hdc, oldText );
644 if (!ignoreFocus && (descr->focus_item == index) &&
646 (descr->in_focus)) DrawFocusRect( hdc, rect );
651 /***********************************************************************
654 * Change the redraw flag.
656 static void LISTBOX_SetRedraw( HWND hwnd, LB_DESCR *descr, BOOL on )
660 if (!(descr->style & LBS_NOREDRAW)) return;
661 descr->style &= ~LBS_NOREDRAW;
662 if (descr->style & LBS_DISPLAYCHANGED)
663 { /* page was changed while setredraw false, refresh automatically */
664 InvalidateRect(hwnd, NULL, TRUE);
665 if ((descr->top_item + descr->page_size) > descr->nb_items)
666 { /* reset top of page if less than number of items/page */
667 descr->top_item = descr->nb_items - descr->page_size;
668 if (descr->top_item < 0) descr->top_item = 0;
670 descr->style &= ~LBS_DISPLAYCHANGED;
672 LISTBOX_UpdateScroll( hwnd, descr );
674 else descr->style |= LBS_NOREDRAW;
678 /***********************************************************************
679 * LISTBOX_RepaintItem
681 * Repaint a single item synchronously.
683 static void LISTBOX_RepaintItem( HWND hwnd, LB_DESCR *descr, INT index,
689 HBRUSH hbrush, oldBrush = 0;
691 /* Do not repaint the item if the item is not visible */
692 if (!IsWindowVisible(hwnd)) return;
693 if (descr->style & LBS_NOREDRAW)
695 descr->style |= LBS_DISPLAYCHANGED;
698 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
699 if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE ))) return;
700 if (descr->font) oldFont = SelectObject( hdc, descr->font );
701 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
702 (WPARAM)hdc, (LPARAM)hwnd );
703 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
704 if (!IsWindowEnabled(hwnd))
705 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
706 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
707 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, index, action, FALSE );
708 if (oldFont) SelectObject( hdc, oldFont );
709 if (oldBrush) SelectObject( hdc, oldBrush );
710 ReleaseDC( hwnd, hdc );
714 /***********************************************************************
715 * LISTBOX_InitStorage
717 static LRESULT LISTBOX_InitStorage( HWND hwnd, LB_DESCR *descr, INT nb_items )
721 nb_items += LB_ARRAY_GRANULARITY - 1;
722 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
724 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
725 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
726 nb_items * sizeof(LB_ITEMDATA));
729 item = HeapAlloc( GetProcessHeap(), 0,
730 nb_items * sizeof(LB_ITEMDATA));
735 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
743 /***********************************************************************
744 * LISTBOX_SetTabStops
746 static BOOL LISTBOX_SetTabStops( HWND hwnd, LB_DESCR *descr, INT count,
747 LPINT tabs, BOOL short_ints )
749 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
750 if (descr->tabs) HeapFree( GetProcessHeap(), 0, descr->tabs );
751 if (!(descr->nb_tabs = count))
756 /* FIXME: count = 1 */
757 if (!(descr->tabs = (INT *)HeapAlloc( GetProcessHeap(), 0,
758 descr->nb_tabs * sizeof(INT) )))
763 LPINT16 p = (LPINT16)tabs;
765 TRACE("[%p]: settabstops ", hwnd );
766 for (i = 0; i < descr->nb_tabs; i++) {
767 descr->tabs[i] = *p++<<1; /* FIXME */
768 if (TRACE_ON(listbox)) TRACE("%hd ", descr->tabs[i]);
770 if (TRACE_ON(listbox)) TRACE("\n");
772 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
773 /* FIXME: repaint the window? */
778 /***********************************************************************
781 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPARAM lParam, BOOL unicode )
783 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
784 if (HAS_STRINGS(descr))
788 DWORD len = strlenW(descr->items[index].str);
791 return WideCharToMultiByte( CP_ACP, 0, descr->items[index].str, len,
792 NULL, 0, NULL, NULL );
795 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
799 LPWSTR buffer = (LPWSTR)lParam;
800 strcpyW( buffer, descr->items[index].str );
801 return strlenW(buffer);
805 LPSTR buffer = (LPSTR)lParam;
806 return WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, buffer, 0x7FFFFFFF, NULL, NULL) - 1;
810 *((LPDWORD)lParam)=*(LPDWORD)(&descr->items[index].data);
811 return sizeof(DWORD);
816 /***********************************************************************
817 * LISTBOX_FindStringPos
819 * Find the nearest string located before a given string in sort order.
820 * If 'exact' is TRUE, return an error if we don't get an exact match.
822 static INT LISTBOX_FindStringPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str,
825 INT index, min, max, res = -1;
827 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
829 max = descr->nb_items;
832 index = (min + max) / 2;
833 if (HAS_STRINGS(descr))
834 res = lstrcmpiW( str, descr->items[index].str);
837 COMPAREITEMSTRUCT cis;
838 UINT id = (UINT)GetWindowLongPtrW( hwnd, GWLP_ID );
840 cis.CtlType = ODT_LISTBOX;
843 /* note that some application (MetaStock) expects the second item
844 * to be in the listbox */
846 cis.itemData1 = (DWORD)str;
848 cis.itemData2 = descr->items[index].data;
849 cis.dwLocaleId = descr->locale;
850 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
852 if (!res) return index;
853 if (res < 0) max = index;
854 else min = index + 1;
856 return exact ? -1 : max;
860 /***********************************************************************
861 * LISTBOX_FindFileStrPos
863 * Find the nearest string located before a given string in directory
864 * sort order (i.e. first files, then directories, then drives).
866 static INT LISTBOX_FindFileStrPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str )
868 INT min, max, res = -1;
870 if (!HAS_STRINGS(descr))
871 return LISTBOX_FindStringPos( hwnd, descr, str, FALSE );
873 max = descr->nb_items;
876 INT index = (min + max) / 2;
877 LPCWSTR p = descr->items[index].str;
878 if (*p == '[') /* drive or directory */
880 if (*str != '[') res = -1;
881 else if (p[1] == '-') /* drive */
883 if (str[1] == '-') res = str[2] - p[2];
888 if (str[1] == '-') res = 1;
889 else res = lstrcmpiW( str, p );
894 if (*str == '[') res = 1;
895 else res = lstrcmpiW( str, p );
897 if (!res) return index;
898 if (res < 0) max = index;
899 else min = index + 1;
905 /***********************************************************************
908 * Find the item beginning with a given string.
910 static INT LISTBOX_FindString( HWND hwnd, LB_DESCR *descr, INT start,
911 LPCWSTR str, BOOL exact )
916 if (start >= descr->nb_items) start = -1;
917 item = descr->items + start + 1;
918 if (HAS_STRINGS(descr))
920 if (!str || ! str[0] ) return LB_ERR;
923 for (i = start + 1; i < descr->nb_items; i++, item++)
924 if (!lstrcmpiW( str, item->str )) return i;
925 for (i = 0, item = descr->items; i <= start; i++, item++)
926 if (!lstrcmpiW( str, item->str )) return i;
930 /* Special case for drives and directories: ignore prefix */
931 #define CHECK_DRIVE(item) \
932 if ((item)->str[0] == '[') \
934 if (!strncmpiW( str, (item)->str+1, len )) return i; \
935 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
939 INT len = strlenW(str);
940 for (i = start + 1; i < descr->nb_items; i++, item++)
942 if (!strncmpiW( str, item->str, len )) return i;
945 for (i = 0, item = descr->items; i <= start; i++, item++)
947 if (!strncmpiW( str, item->str, len )) return i;
955 if (exact && (descr->style & LBS_SORT))
956 /* If sorted, use a WM_COMPAREITEM binary search */
957 return LISTBOX_FindStringPos( hwnd, descr, str, TRUE );
959 /* Otherwise use a linear search */
960 for (i = start + 1; i < descr->nb_items; i++, item++)
961 if (item->data == (DWORD)str) return i;
962 for (i = 0, item = descr->items; i <= start; i++, item++)
963 if (item->data == (DWORD)str) return i;
969 /***********************************************************************
970 * LISTBOX_GetSelCount
972 static LRESULT LISTBOX_GetSelCount( LB_DESCR *descr )
975 LB_ITEMDATA *item = descr->items;
977 if (!(descr->style & LBS_MULTIPLESEL) ||
978 (descr->style & LBS_NOSEL))
980 for (i = count = 0; i < descr->nb_items; i++, item++)
981 if (item->selected) count++;
986 /***********************************************************************
987 * LISTBOX_GetSelItems16
989 static LRESULT LISTBOX_GetSelItems16( LB_DESCR *descr, INT16 max, LPINT16 array )
992 LB_ITEMDATA *item = descr->items;
994 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
995 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
996 if (item->selected) array[count++] = (INT16)i;
1001 /***********************************************************************
1002 * LISTBOX_GetSelItems
1004 static LRESULT LISTBOX_GetSelItems( LB_DESCR *descr, INT max, LPINT 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++] = i;
1016 /***********************************************************************
1019 static LRESULT LISTBOX_Paint( HWND hwnd, LB_DESCR *descr, HDC hdc )
1021 INT i, col_pos = descr->page_size - 1;
1023 RECT focusRect = {-1, -1, -1, -1};
1025 HBRUSH hbrush, oldBrush = 0;
1027 if (descr->style & LBS_NOREDRAW) return 0;
1029 SetRect( &rect, 0, 0, descr->width, descr->height );
1030 if (descr->style & LBS_MULTICOLUMN)
1031 rect.right = rect.left + descr->column_width;
1032 else if (descr->horz_pos)
1034 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
1035 rect.right += descr->horz_pos;
1038 if (descr->font) oldFont = SelectObject( hdc, descr->font );
1039 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1040 (WPARAM)hdc, (LPARAM)hwnd );
1041 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1042 if (!IsWindowEnabled(hwnd)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1044 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1047 /* Special case for empty listbox: paint focus rect */
1048 rect.bottom = rect.top + descr->item_height;
1049 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1050 &rect, NULL, 0, NULL );
1051 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, descr->focus_item,
1053 rect.top = rect.bottom;
1056 /* Paint all the item, regarding the selection
1057 Focus state will be painted after */
1059 for (i = descr->top_item; i < descr->nb_items; i++)
1061 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1062 rect.bottom = rect.top + descr->item_height;
1064 rect.bottom = rect.top + descr->items[i].height;
1066 if (i == descr->focus_item)
1068 /* keep the focus rect, to paint the focus item after */
1069 focusRect.left = rect.left;
1070 focusRect.right = rect.right;
1071 focusRect.top = rect.top;
1072 focusRect.bottom = rect.bottom;
1074 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1075 rect.top = rect.bottom;
1077 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1079 if (!IS_OWNERDRAW(descr))
1081 /* Clear the bottom of the column */
1082 if (rect.top < descr->height)
1084 rect.bottom = descr->height;
1085 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1086 &rect, NULL, 0, NULL );
1090 /* Go to the next column */
1091 rect.left += descr->column_width;
1092 rect.right += descr->column_width;
1094 col_pos = descr->page_size - 1;
1099 if (rect.top >= descr->height) break;
1103 /* Paint the focus item now */
1104 if (focusRect.top != focusRect.bottom &&
1105 descr->caret_on && descr->in_focus)
1106 LISTBOX_PaintItem( hwnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1108 if (!IS_OWNERDRAW(descr))
1110 /* Clear the remainder of the client area */
1111 if (rect.top < descr->height)
1113 rect.bottom = descr->height;
1114 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1115 &rect, NULL, 0, NULL );
1117 if (rect.right < descr->width)
1119 rect.left = rect.right;
1120 rect.right = descr->width;
1122 rect.bottom = descr->height;
1123 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1124 &rect, NULL, 0, NULL );
1127 if (oldFont) SelectObject( hdc, oldFont );
1128 if (oldBrush) SelectObject( hdc, oldBrush );
1133 /***********************************************************************
1134 * LISTBOX_InvalidateItems
1136 * Invalidate all items from a given item. If the specified item is not
1137 * visible, nothing happens.
1139 static void LISTBOX_InvalidateItems( HWND hwnd, LB_DESCR *descr, INT index )
1143 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1145 if (descr->style & LBS_NOREDRAW)
1147 descr->style |= LBS_DISPLAYCHANGED;
1150 rect.bottom = descr->height;
1151 InvalidateRect( hwnd, &rect, TRUE );
1152 if (descr->style & LBS_MULTICOLUMN)
1154 /* Repaint the other columns */
1155 rect.left = rect.right;
1156 rect.right = descr->width;
1158 InvalidateRect( hwnd, &rect, TRUE );
1163 static void LISTBOX_InvalidateItemRect( HWND hwnd, LB_DESCR *descr, INT index )
1167 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1168 InvalidateRect( hwnd, &rect, TRUE );
1171 /***********************************************************************
1172 * LISTBOX_GetItemHeight
1174 static LRESULT LISTBOX_GetItemHeight( LB_DESCR *descr, INT index )
1176 if (descr->style & LBS_OWNERDRAWVARIABLE)
1178 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1179 return descr->items[index].height;
1181 else return descr->item_height;
1185 /***********************************************************************
1186 * LISTBOX_SetItemHeight
1188 static LRESULT LISTBOX_SetItemHeight( HWND hwnd, LB_DESCR *descr, INT index,
1189 INT height, BOOL repaint )
1191 if (!height) height = 1;
1193 if (descr->style & LBS_OWNERDRAWVARIABLE)
1195 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1196 TRACE("[%p]: item %d height = %d\n", hwnd, index, height );
1197 descr->items[index].height = height;
1198 LISTBOX_UpdateScroll( hwnd, descr );
1200 LISTBOX_InvalidateItems( hwnd, descr, index );
1202 else if (height != descr->item_height)
1204 TRACE("[%p]: new height = %d\n", hwnd, height );
1205 descr->item_height = height;
1206 LISTBOX_UpdatePage( hwnd, descr );
1207 LISTBOX_UpdateScroll( hwnd, descr );
1209 InvalidateRect( hwnd, 0, TRUE );
1215 /***********************************************************************
1216 * LISTBOX_SetHorizontalPos
1218 static void LISTBOX_SetHorizontalPos( HWND hwnd, LB_DESCR *descr, INT pos )
1222 if (pos > descr->horz_extent - descr->width)
1223 pos = descr->horz_extent - descr->width;
1224 if (pos < 0) pos = 0;
1225 if (!(diff = descr->horz_pos - pos)) return;
1226 TRACE("[%p]: new horz pos = %d\n", hwnd, pos );
1227 descr->horz_pos = pos;
1228 LISTBOX_UpdateScroll( hwnd, descr );
1229 if (abs(diff) < descr->width)
1232 /* Invalidate the focused item so it will be repainted correctly */
1233 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
1234 InvalidateRect( hwnd, &rect, TRUE );
1235 ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
1236 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1239 InvalidateRect( hwnd, NULL, TRUE );
1243 /***********************************************************************
1244 * LISTBOX_SetHorizontalExtent
1246 static LRESULT LISTBOX_SetHorizontalExtent( HWND hwnd, LB_DESCR *descr,
1249 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1251 if (extent <= 0) extent = 1;
1252 if (extent == descr->horz_extent) return LB_OKAY;
1253 TRACE("[%p]: new horz extent = %d\n", hwnd, extent );
1254 descr->horz_extent = extent;
1255 if (descr->horz_pos > extent - descr->width)
1256 LISTBOX_SetHorizontalPos( hwnd, descr, extent - descr->width );
1258 LISTBOX_UpdateScroll( hwnd, descr );
1263 /***********************************************************************
1264 * LISTBOX_SetColumnWidth
1266 static LRESULT LISTBOX_SetColumnWidth( HWND hwnd, LB_DESCR *descr, INT width)
1268 if (width == descr->column_width) return LB_OKAY;
1269 TRACE("[%p]: new column width = %d\n", hwnd, width );
1270 descr->column_width = width;
1271 LISTBOX_UpdatePage( hwnd, descr );
1276 /***********************************************************************
1279 * Returns the item height.
1281 static INT LISTBOX_SetFont( HWND hwnd, LB_DESCR *descr, HFONT font )
1289 if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE )))
1291 ERR("unable to get DC.\n" );
1294 if (font) oldFont = SelectObject( hdc, font );
1295 GetTextMetricsW( hdc, &tm );
1296 if (oldFont) SelectObject( hdc, oldFont );
1297 ReleaseDC( hwnd, hdc );
1298 if (!IS_OWNERDRAW(descr))
1299 LISTBOX_SetItemHeight( hwnd, descr, 0, tm.tmHeight, FALSE );
1300 return tm.tmHeight ;
1304 /***********************************************************************
1305 * LISTBOX_MakeItemVisible
1307 * Make sure that a given item is partially or fully visible.
1309 static void LISTBOX_MakeItemVisible( HWND hwnd, LB_DESCR *descr, INT index,
1314 if (index <= descr->top_item) top = index;
1315 else if (descr->style & LBS_MULTICOLUMN)
1317 INT cols = descr->width;
1318 if (!fully) cols += descr->column_width - 1;
1319 if (cols >= descr->column_width) cols /= descr->column_width;
1321 if (index < descr->top_item + (descr->page_size * cols)) return;
1322 top = index - descr->page_size * (cols - 1);
1324 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1326 INT height = fully ? descr->items[index].height : 1;
1327 for (top = index; top > descr->top_item; top--)
1328 if ((height += descr->items[top-1].height) > descr->height) break;
1332 if (index < descr->top_item + descr->page_size) return;
1333 if (!fully && (index == descr->top_item + descr->page_size) &&
1334 (descr->height > (descr->page_size * descr->item_height))) return;
1335 top = index - descr->page_size + 1;
1337 LISTBOX_SetTopItem( hwnd, descr, top, TRUE );
1340 /***********************************************************************
1341 * LISTBOX_SetCaretIndex
1344 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1347 static LRESULT LISTBOX_SetCaretIndex( HWND hwnd, LB_DESCR *descr, INT index,
1348 BOOL fully_visible )
1350 INT oldfocus = descr->focus_item;
1352 if (descr->style & LBS_NOSEL) return LB_ERR;
1353 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1354 if (index == oldfocus) return LB_OKAY;
1355 descr->focus_item = index;
1356 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1357 LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
1359 LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
1360 if (descr->caret_on && (descr->in_focus))
1361 LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
1367 /***********************************************************************
1368 * LISTBOX_SelectItemRange
1370 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1372 static LRESULT LISTBOX_SelectItemRange( HWND hwnd, LB_DESCR *descr, INT first,
1377 /* A few sanity checks */
1379 if (descr->style & LBS_NOSEL) return LB_ERR;
1380 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1381 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1382 if (last == -1) last = descr->nb_items - 1;
1383 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1384 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1385 /* selected_item reflects last selected/unselected item on multiple sel */
1386 descr->selected_item = last;
1388 if (on) /* Turn selection on */
1390 for (i = first; i <= last; i++)
1392 if (descr->items[i].selected) continue;
1393 descr->items[i].selected = TRUE;
1394 LISTBOX_InvalidateItemRect(hwnd, descr, i);
1396 LISTBOX_SetCaretIndex( hwnd, descr, last, TRUE );
1398 else /* Turn selection off */
1400 for (i = first; i <= last; i++)
1402 if (!descr->items[i].selected) continue;
1403 descr->items[i].selected = FALSE;
1404 LISTBOX_InvalidateItemRect(hwnd, descr, i);
1410 /***********************************************************************
1411 * LISTBOX_SetSelection
1413 static LRESULT LISTBOX_SetSelection( HWND hwnd, LB_DESCR *descr, INT index,
1414 BOOL on, BOOL send_notify )
1416 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1418 if (descr->style & LBS_NOSEL)
1420 descr->selected_item = index;
1423 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1424 if (descr->style & LBS_MULTIPLESEL)
1426 if (index == -1) /* Select all items */
1427 return LISTBOX_SelectItemRange( hwnd, descr, 0, -1, on );
1428 else /* Only one item */
1429 return LISTBOX_SelectItemRange( hwnd, descr, index, index, on );
1433 INT oldsel = descr->selected_item;
1434 if (index == oldsel) return LB_OKAY;
1435 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1436 if (index != -1) descr->items[index].selected = TRUE;
1437 descr->selected_item = index;
1438 if (oldsel != -1) LISTBOX_RepaintItem( hwnd, descr, oldsel, ODA_SELECT );
1439 if (index != -1) LISTBOX_RepaintItem( hwnd, descr, index, ODA_SELECT );
1440 if (send_notify && descr->nb_items) SEND_NOTIFICATION( hwnd, descr,
1441 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1443 if( descr->lphc ) /* set selection change flag for parent combo */
1444 descr->lphc->wState |= CBF_SELCHANGE;
1450 /***********************************************************************
1453 * Change the caret position and extend the selection to the new caret.
1455 static void LISTBOX_MoveCaret( HWND hwnd, LB_DESCR *descr, INT index,
1456 BOOL fully_visible )
1458 INT oldfocus = descr->focus_item;
1460 if ((index < 0) || (index >= descr->nb_items))
1463 /* Important, repaint needs to be done in this order if
1464 you want to mimic Windows behavior:
1465 1. Remove the focus and paint the item
1466 2. Remove the selection and paint the item(s)
1467 3. Set the selection and repaint the item(s)
1468 4. Set the focus to 'index' and repaint the item */
1470 /* 1. remove the focus and repaint the item */
1471 descr->focus_item = -1;
1472 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1473 LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
1475 /* 2. then turn off the previous selection */
1476 /* 3. repaint the new selected item */
1477 if (descr->style & LBS_EXTENDEDSEL)
1479 if (descr->anchor_item != -1)
1481 INT first = min( index, descr->anchor_item );
1482 INT last = max( index, descr->anchor_item );
1484 LISTBOX_SelectItemRange( hwnd, descr, 0, first - 1, FALSE );
1485 LISTBOX_SelectItemRange( hwnd, descr, last + 1, -1, FALSE );
1486 LISTBOX_SelectItemRange( hwnd, descr, first, last, TRUE );
1489 else if (!(descr->style & LBS_MULTIPLESEL))
1491 /* Set selection to new caret item */
1492 LISTBOX_SetSelection( hwnd, descr, index, TRUE, FALSE );
1495 /* 4. repaint the new item with the focus */
1496 descr->focus_item = index;
1497 LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
1498 if (descr->caret_on && (descr->in_focus))
1499 LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
1503 /***********************************************************************
1504 * LISTBOX_InsertItem
1506 static LRESULT LISTBOX_InsertItem( HWND hwnd, LB_DESCR *descr, INT index,
1507 LPWSTR str, DWORD data )
1511 INT oldfocus = descr->focus_item;
1513 if (index == -1) index = descr->nb_items;
1514 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1515 if (!descr->items) max_items = 0;
1516 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1517 if (descr->nb_items == max_items)
1519 /* We need to grow the array */
1520 max_items += LB_ARRAY_GRANULARITY;
1522 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1523 max_items * sizeof(LB_ITEMDATA) );
1525 item = HeapAlloc( GetProcessHeap(), 0,
1526 max_items * sizeof(LB_ITEMDATA) );
1529 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
1532 descr->items = item;
1535 /* Insert the item structure */
1537 item = &descr->items[index];
1538 if (index < descr->nb_items)
1539 RtlMoveMemory( item + 1, item,
1540 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1544 item->selected = FALSE;
1547 /* Get item height */
1549 if (descr->style & LBS_OWNERDRAWVARIABLE)
1551 MEASUREITEMSTRUCT mis;
1552 UINT id = (UINT)GetWindowLongPtrW( hwnd, GWLP_ID );
1554 mis.CtlType = ODT_LISTBOX;
1557 mis.itemData = descr->items[index].data;
1558 mis.itemHeight = descr->item_height;
1559 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1560 item->height = mis.itemHeight ? mis.itemHeight : 1;
1561 TRACE("[%p]: measure item %d (%s) = %d\n",
1562 hwnd, index, str ? debugstr_w(str) : "", item->height );
1565 /* Repaint the items */
1567 LISTBOX_UpdateScroll( hwnd, descr );
1568 LISTBOX_InvalidateItems( hwnd, descr, index );
1570 /* Move selection and focused item */
1571 /* If listbox was empty, set focus to the first item */
1572 if (descr->nb_items == 1)
1573 LISTBOX_SetCaretIndex( hwnd, descr, 0, FALSE );
1574 /* single select don't change selection index in win31 */
1575 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1577 descr->selected_item++;
1578 LISTBOX_SetSelection( hwnd, descr, descr->selected_item-1, TRUE, FALSE );
1582 if (index <= descr->selected_item)
1584 descr->selected_item++;
1585 descr->focus_item = oldfocus; /* focus not changed */
1592 /***********************************************************************
1593 * LISTBOX_InsertString
1595 static LRESULT LISTBOX_InsertString( HWND hwnd, LB_DESCR *descr, INT index,
1598 LPWSTR new_str = NULL;
1602 if (HAS_STRINGS(descr))
1604 static const WCHAR empty_stringW[] = { 0 };
1605 if (!str) str = empty_stringW;
1606 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1608 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
1611 strcpyW(new_str, str);
1613 else data = (DWORD)str;
1615 if (index == -1) index = descr->nb_items;
1616 if ((ret = LISTBOX_InsertItem( hwnd, descr, index, new_str, data )) != 0)
1618 if (new_str) HeapFree( GetProcessHeap(), 0, new_str );
1622 TRACE("[%p]: added item %d %s\n",
1623 hwnd, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1628 /***********************************************************************
1629 * LISTBOX_DeleteItem
1631 * Delete the content of an item. 'index' must be a valid index.
1633 static void LISTBOX_DeleteItem( HWND hwnd, LB_DESCR *descr, INT index )
1635 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1636 * while Win95 sends it for all items with user data.
1637 * It's probably better to send it too often than not
1638 * often enough, so this is what we do here.
1640 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1642 DELETEITEMSTRUCT dis;
1643 UINT id = (UINT)GetWindowLongPtrW( hwnd, GWLP_ID );
1645 dis.CtlType = ODT_LISTBOX;
1648 dis.hwndItem = hwnd;
1649 dis.itemData = descr->items[index].data;
1650 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1652 if (HAS_STRINGS(descr) && descr->items[index].str)
1653 HeapFree( GetProcessHeap(), 0, descr->items[index].str );
1657 /***********************************************************************
1658 * LISTBOX_RemoveItem
1660 * Remove an item from the listbox and delete its content.
1662 static LRESULT LISTBOX_RemoveItem( HWND hwnd, LB_DESCR *descr, INT index )
1667 if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1668 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1670 /* We need to invalidate the original rect instead of the updated one. */
1671 LISTBOX_InvalidateItems( hwnd, descr, index );
1673 LISTBOX_DeleteItem( hwnd, descr, index );
1675 /* Remove the item */
1677 item = &descr->items[index];
1678 if (index < descr->nb_items-1)
1679 RtlMoveMemory( item, item + 1,
1680 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1682 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1684 /* Shrink the item array if possible */
1686 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1687 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1689 max_items -= LB_ARRAY_GRANULARITY;
1690 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1691 max_items * sizeof(LB_ITEMDATA) );
1692 if (item) descr->items = item;
1694 /* Repaint the items */
1696 LISTBOX_UpdateScroll( hwnd, descr );
1697 /* if we removed the scrollbar, reset the top of the list
1698 (correct for owner-drawn ???) */
1699 if (descr->nb_items == descr->page_size)
1700 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1702 /* Move selection and focused item */
1703 if (!IS_MULTISELECT(descr))
1705 if (index == descr->selected_item)
1706 descr->selected_item = -1;
1707 else if (index < descr->selected_item)
1709 descr->selected_item--;
1710 if (ISWIN31) /* win 31 do not change the selected item number */
1711 LISTBOX_SetSelection( hwnd, descr, descr->selected_item + 1, TRUE, FALSE);
1715 if (descr->focus_item >= descr->nb_items)
1717 descr->focus_item = descr->nb_items - 1;
1718 if (descr->focus_item < 0) descr->focus_item = 0;
1724 /***********************************************************************
1725 * LISTBOX_ResetContent
1727 static void LISTBOX_ResetContent( HWND hwnd, LB_DESCR *descr )
1731 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( hwnd, descr, i );
1732 if (descr->items) HeapFree( GetProcessHeap(), 0, descr->items );
1733 descr->nb_items = 0;
1734 descr->top_item = 0;
1735 descr->selected_item = -1;
1736 descr->focus_item = 0;
1737 descr->anchor_item = -1;
1738 descr->items = NULL;
1742 /***********************************************************************
1745 static LRESULT LISTBOX_SetCount( HWND hwnd, LB_DESCR *descr, INT count )
1749 if (HAS_STRINGS(descr)) return LB_ERR;
1750 /* FIXME: this is far from optimal... */
1751 if (count > descr->nb_items)
1753 while (count > descr->nb_items)
1754 if ((ret = LISTBOX_InsertString( hwnd, descr, -1, 0 )) < 0)
1757 else if (count < descr->nb_items)
1759 while (count < descr->nb_items)
1760 if ((ret = LISTBOX_RemoveItem( hwnd, descr, -1 )) < 0)
1767 /***********************************************************************
1770 static LRESULT LISTBOX_Directory( HWND hwnd, LB_DESCR *descr, UINT attrib,
1771 LPCWSTR filespec, BOOL long_names )
1774 LRESULT ret = LB_OKAY;
1775 WIN32_FIND_DATAW entry;
1778 /* don't scan directory if we just want drives exclusively */
1779 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1780 /* scan directory */
1781 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1783 int le = GetLastError();
1784 if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR;
1791 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1793 static const WCHAR bracketW[] = { ']',0 };
1794 static const WCHAR dotW[] = { '.',0 };
1795 if (!(attrib & DDL_DIRECTORY) ||
1796 !strcmpW( entry.cFileName, dotW )) continue;
1798 if (!long_names && entry.cAlternateFileName[0])
1799 strcpyW( buffer + 1, entry.cAlternateFileName );
1801 strcpyW( buffer + 1, entry.cFileName );
1802 strcatW(buffer, bracketW);
1804 else /* not a directory */
1806 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1807 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1809 if ((attrib & DDL_EXCLUSIVE) &&
1810 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1813 if (!long_names && entry.cAlternateFileName[0])
1814 strcpyW( buffer, entry.cAlternateFileName );
1816 strcpyW( buffer, entry.cFileName );
1818 if (!long_names) CharLowerW( buffer );
1819 pos = LISTBOX_FindFileStrPos( hwnd, descr, buffer );
1820 if ((ret = LISTBOX_InsertString( hwnd, descr, pos, buffer )) < 0)
1822 } while (FindNextFileW( handle, &entry ));
1823 FindClose( handle );
1828 if ((ret >= 0) && (attrib & DDL_DRIVES))
1830 WCHAR buffer[] = {'[','-','a','-',']',0};
1831 WCHAR root[] = {'A',':','\\',0};
1833 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1835 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1836 if ((ret = LISTBOX_InsertString( hwnd, descr, -1, buffer )) < 0)
1844 /***********************************************************************
1845 * LISTBOX_HandleVScroll
1847 static LRESULT LISTBOX_HandleVScroll( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1851 if (descr->style & LBS_MULTICOLUMN) return 0;
1852 switch(LOWORD(wParam))
1855 LISTBOX_SetTopItem( hwnd, descr, descr->top_item - 1, TRUE );
1858 LISTBOX_SetTopItem( hwnd, descr, descr->top_item + 1, TRUE );
1861 LISTBOX_SetTopItem( hwnd, descr, descr->top_item -
1862 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1865 LISTBOX_SetTopItem( hwnd, descr, descr->top_item +
1866 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1868 case SB_THUMBPOSITION:
1869 LISTBOX_SetTopItem( hwnd, descr, HIWORD(wParam), TRUE );
1872 info.cbSize = sizeof(info);
1873 info.fMask = SIF_TRACKPOS;
1874 GetScrollInfo( hwnd, SB_VERT, &info );
1875 LISTBOX_SetTopItem( hwnd, descr, info.nTrackPos, TRUE );
1878 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1881 LISTBOX_SetTopItem( hwnd, descr, descr->nb_items, TRUE );
1888 /***********************************************************************
1889 * LISTBOX_HandleHScroll
1891 static LRESULT LISTBOX_HandleHScroll( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1896 if (descr->style & LBS_MULTICOLUMN)
1898 switch(LOWORD(wParam))
1901 LISTBOX_SetTopItem( hwnd, descr, descr->top_item-descr->page_size,
1905 LISTBOX_SetTopItem( hwnd, descr, descr->top_item+descr->page_size,
1909 page = descr->width / descr->column_width;
1910 if (page < 1) page = 1;
1911 LISTBOX_SetTopItem( hwnd, descr,
1912 descr->top_item - page * descr->page_size, TRUE );
1915 page = descr->width / descr->column_width;
1916 if (page < 1) page = 1;
1917 LISTBOX_SetTopItem( hwnd, descr,
1918 descr->top_item + page * descr->page_size, TRUE );
1920 case SB_THUMBPOSITION:
1921 LISTBOX_SetTopItem( hwnd, descr, HIWORD(wParam)*descr->page_size,
1925 info.cbSize = sizeof(info);
1926 info.fMask = SIF_TRACKPOS;
1927 GetScrollInfo( hwnd, SB_VERT, &info );
1928 LISTBOX_SetTopItem( hwnd, descr, info.nTrackPos*descr->page_size,
1932 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1935 LISTBOX_SetTopItem( hwnd, descr, descr->nb_items, TRUE );
1939 else if (descr->horz_extent)
1941 switch(LOWORD(wParam))
1944 LISTBOX_SetHorizontalPos( hwnd, descr, descr->horz_pos - 1 );
1947 LISTBOX_SetHorizontalPos( hwnd, descr, descr->horz_pos + 1 );
1950 LISTBOX_SetHorizontalPos( hwnd, descr,
1951 descr->horz_pos - descr->width );
1954 LISTBOX_SetHorizontalPos( hwnd, descr,
1955 descr->horz_pos + descr->width );
1957 case SB_THUMBPOSITION:
1958 LISTBOX_SetHorizontalPos( hwnd, descr, HIWORD(wParam) );
1961 info.cbSize = sizeof(info);
1962 info.fMask = SIF_TRACKPOS;
1963 GetScrollInfo( hwnd, SB_HORZ, &info );
1964 LISTBOX_SetHorizontalPos( hwnd, descr, info.nTrackPos );
1967 LISTBOX_SetHorizontalPos( hwnd, descr, 0 );
1970 LISTBOX_SetHorizontalPos( hwnd, descr,
1971 descr->horz_extent - descr->width );
1978 static LRESULT LISTBOX_HandleMouseWheel(HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1980 short gcWheelDelta = 0;
1981 UINT pulScrollLines = 3;
1983 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1985 gcWheelDelta -= (short) HIWORD(wParam);
1987 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1989 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1990 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1991 LISTBOX_SetTopItem( hwnd, descr, descr->top_item + cLineScroll, TRUE );
1996 /***********************************************************************
1997 * LISTBOX_HandleLButtonDown
1999 static LRESULT LISTBOX_HandleLButtonDown( HWND hwnd, LB_DESCR *descr,
2000 WPARAM wParam, INT x, INT y )
2002 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
2003 TRACE("[%p]: lbuttondown %d,%d item %d\n", hwnd, x, y, index );
2004 if (!descr->caret_on && (descr->in_focus)) return 0;
2006 if (!descr->in_focus)
2008 if( !descr->lphc ) SetFocus( hwnd );
2009 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
2012 if (index == -1) return 0;
2014 if (descr->style & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL))
2016 /* we should perhaps make sure that all items are deselected
2017 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2018 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
2019 LISTBOX_SetSelection( hwnd, descr, -1, FALSE, FALSE);
2022 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
2023 if (wParam & MK_CONTROL)
2025 LISTBOX_SetCaretIndex( hwnd, descr, index, FALSE );
2026 LISTBOX_SetSelection( hwnd, descr, index,
2027 !descr->items[index].selected,
2028 (descr->style & LBS_NOTIFY) != 0);
2032 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
2034 if (descr->style & LBS_MULTIPLESEL)
2036 LISTBOX_SetSelection( hwnd, descr, index,
2037 (!(descr->style & LBS_MULTIPLESEL) ||
2038 !descr->items[index].selected),
2039 (descr->style & LBS_NOTIFY) != 0 );
2045 descr->anchor_item = index;
2046 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
2047 LISTBOX_SetSelection( hwnd, descr, index,
2048 TRUE, (descr->style & LBS_NOTIFY) != 0 );
2051 descr->captured = TRUE;
2056 if (descr->style & LBS_NOTIFY )
2057 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
2058 MAKELPARAM( x, y ) );
2059 if (GetWindowLongA( hwnd, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
2066 if (DragDetect( hwnd, pt ))
2067 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
2074 /*************************************************************************
2075 * LISTBOX_HandleLButtonDownCombo [Internal]
2077 * Process LButtonDown message for the ComboListBox
2080 * pWnd [I] The windows internal structure
2081 * pDescr [I] The ListBox internal structure
2082 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2083 * x [I] X Mouse Coordinate
2084 * y [I] Y Mouse Coordinate
2087 * 0 since we are processing the WM_LBUTTONDOWN Message
2090 * This function is only to be used when a ListBox is a ComboListBox
2093 static LRESULT LISTBOX_HandleLButtonDownCombo( HWND hwnd, LB_DESCR *pDescr,
2094 UINT msg, WPARAM wParam, INT x, INT y)
2096 RECT clientRect, screenRect;
2102 GetClientRect(hwnd, &clientRect);
2104 if(PtInRect(&clientRect, mousePos))
2106 /* MousePos is in client, resume normal processing */
2107 if (msg == WM_LBUTTONDOWN)
2109 pDescr->lphc->droppedIndex = pDescr->nb_items ? pDescr->selected_item : -1;
2110 return LISTBOX_HandleLButtonDown( hwnd, pDescr, wParam, x, y);
2112 else if (pDescr->style & LBS_NOTIFY)
2113 SEND_NOTIFICATION( hwnd, pDescr, LBN_DBLCLK );
2118 POINT screenMousePos;
2119 HWND hWndOldCapture;
2121 /* Check the Non-Client Area */
2122 screenMousePos = mousePos;
2123 hWndOldCapture = GetCapture();
2125 GetWindowRect(hwnd, &screenRect);
2126 ClientToScreen(hwnd, &screenMousePos);
2128 if(!PtInRect(&screenRect, screenMousePos))
2130 LISTBOX_SetCaretIndex( hwnd, pDescr, pDescr->lphc->droppedIndex, FALSE );
2131 LISTBOX_SetSelection( hwnd, pDescr, pDescr->lphc->droppedIndex, FALSE, FALSE );
2132 COMBO_FlipListbox( pDescr->lphc, FALSE, FALSE );
2137 /* Check to see the NC is a scrollbar */
2139 LONG style = GetWindowLongA( hwnd, GWL_STYLE );
2140 /* Check Vertical scroll bar */
2141 if (style & WS_VSCROLL)
2143 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2144 if (PtInRect( &clientRect, mousePos ))
2146 nHitTestType = HTVSCROLL;
2149 /* Check horizontal scroll bar */
2150 if (style & WS_HSCROLL)
2152 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2153 if (PtInRect( &clientRect, mousePos ))
2155 nHitTestType = HTHSCROLL;
2158 /* Windows sends this message when a scrollbar is clicked
2161 if(nHitTestType != 0)
2163 SendMessageW(hwnd, WM_NCLBUTTONDOWN, nHitTestType,
2164 MAKELONG(screenMousePos.x, screenMousePos.y));
2166 /* Resume the Capture after scrolling is complete
2168 if(hWndOldCapture != 0)
2170 SetCapture(hWndOldCapture);
2177 /***********************************************************************
2178 * LISTBOX_HandleLButtonUp
2180 static LRESULT LISTBOX_HandleLButtonUp( HWND hwnd, LB_DESCR *descr )
2182 if (LISTBOX_Timer != LB_TIMER_NONE)
2183 KillSystemTimer( hwnd, LB_TIMER_ID );
2184 LISTBOX_Timer = LB_TIMER_NONE;
2185 if (descr->captured)
2187 descr->captured = FALSE;
2188 if (GetCapture() == hwnd) ReleaseCapture();
2189 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2190 SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2196 /***********************************************************************
2197 * LISTBOX_HandleTimer
2199 * Handle scrolling upon a timer event.
2200 * Return TRUE if scrolling should continue.
2202 static LRESULT LISTBOX_HandleTimer( HWND hwnd, LB_DESCR *descr,
2203 INT index, TIMER_DIRECTION dir )
2208 if (descr->top_item) index = descr->top_item - 1;
2212 if (descr->top_item) index -= descr->page_size;
2215 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2216 if (index == descr->focus_item) index++;
2217 if (index >= descr->nb_items) index = descr->nb_items - 1;
2219 case LB_TIMER_RIGHT:
2220 if (index + descr->page_size < descr->nb_items)
2221 index += descr->page_size;
2226 if (index == descr->focus_item) return FALSE;
2227 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
2232 /***********************************************************************
2233 * LISTBOX_HandleSystemTimer
2235 * WM_SYSTIMER handler.
2237 static LRESULT LISTBOX_HandleSystemTimer( HWND hwnd, LB_DESCR *descr )
2239 if (!LISTBOX_HandleTimer( hwnd, descr, descr->focus_item, LISTBOX_Timer ))
2241 KillSystemTimer( hwnd, LB_TIMER_ID );
2242 LISTBOX_Timer = LB_TIMER_NONE;
2248 /***********************************************************************
2249 * LISTBOX_HandleMouseMove
2251 * WM_MOUSEMOVE handler.
2253 static void LISTBOX_HandleMouseMove( HWND hwnd, LB_DESCR *descr,
2257 TIMER_DIRECTION dir = LB_TIMER_NONE;
2259 if (!descr->captured) return;
2261 if (descr->style & LBS_MULTICOLUMN)
2264 else if (y >= descr->item_height * descr->page_size)
2265 y = descr->item_height * descr->page_size - 1;
2269 dir = LB_TIMER_LEFT;
2272 else if (x >= descr->width)
2274 dir = LB_TIMER_RIGHT;
2275 x = descr->width - 1;
2280 if (y < 0) dir = LB_TIMER_UP; /* above */
2281 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2284 index = LISTBOX_GetItemFromPoint( descr, x, y );
2285 if (index == -1) index = descr->focus_item;
2286 if (!LISTBOX_HandleTimer( hwnd, descr, index, dir )) dir = LB_TIMER_NONE;
2288 /* Start/stop the system timer */
2290 if (dir != LB_TIMER_NONE)
2291 SetSystemTimer( hwnd, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2292 else if (LISTBOX_Timer != LB_TIMER_NONE)
2293 KillSystemTimer( hwnd, LB_TIMER_ID );
2294 LISTBOX_Timer = dir;
2298 /***********************************************************************
2299 * LISTBOX_HandleKeyDown
2301 static LRESULT LISTBOX_HandleKeyDown( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
2304 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2305 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2306 bForceSelection = FALSE; /* only for single select list */
2308 if (descr->style & LBS_WANTKEYBOARDINPUT)
2310 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2311 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2313 if (caret == -2) return 0;
2315 if (caret == -1) switch(wParam)
2318 if (descr->style & LBS_MULTICOLUMN)
2320 bForceSelection = FALSE;
2321 if (descr->focus_item >= descr->page_size)
2322 caret = descr->focus_item - descr->page_size;
2327 caret = descr->focus_item - 1;
2328 if (caret < 0) caret = 0;
2331 if (descr->style & LBS_MULTICOLUMN)
2333 bForceSelection = FALSE;
2334 if (descr->focus_item + descr->page_size < descr->nb_items)
2335 caret = descr->focus_item + descr->page_size;
2340 caret = descr->focus_item + 1;
2341 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2345 if (descr->style & LBS_MULTICOLUMN)
2347 INT page = descr->width / descr->column_width;
2348 if (page < 1) page = 1;
2349 caret = descr->focus_item - (page * descr->page_size) + 1;
2351 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2352 if (caret < 0) caret = 0;
2355 if (descr->style & LBS_MULTICOLUMN)
2357 INT page = descr->width / descr->column_width;
2358 if (page < 1) page = 1;
2359 caret = descr->focus_item + (page * descr->page_size) - 1;
2361 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2362 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2368 caret = descr->nb_items - 1;
2371 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2372 else if (descr->style & LBS_MULTIPLESEL)
2374 LISTBOX_SetSelection( hwnd, descr, descr->focus_item,
2375 !descr->items[descr->focus_item].selected,
2376 (descr->style & LBS_NOTIFY) != 0 );
2380 bForceSelection = FALSE;
2382 if (bForceSelection) /* focused item is used instead of key */
2383 caret = descr->focus_item;
2386 if (((descr->style & LBS_EXTENDEDSEL) &&
2387 !(GetKeyState( VK_SHIFT ) & 0x8000)) ||
2388 !IS_MULTISELECT(descr))
2389 descr->anchor_item = caret;
2390 LISTBOX_MoveCaret( hwnd, descr, caret, TRUE );
2392 if (descr->style & LBS_MULTIPLESEL)
2393 descr->selected_item = caret;
2395 LISTBOX_SetSelection( hwnd, descr, caret, TRUE, FALSE);
2396 if (descr->style & LBS_NOTIFY)
2400 /* make sure that combo parent doesn't hide us */
2401 descr->lphc->wState |= CBF_NOROLLUP;
2403 if (descr->nb_items) SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2410 /***********************************************************************
2411 * LISTBOX_HandleChar
2413 static LRESULT LISTBOX_HandleChar( HWND hwnd, LB_DESCR *descr, WCHAR charW )
2421 if (descr->style & LBS_WANTKEYBOARDINPUT)
2423 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2424 MAKEWPARAM(charW, descr->focus_item),
2426 if (caret == -2) return 0;
2429 caret = LISTBOX_FindString( hwnd, descr, descr->focus_item, str, FALSE);
2432 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2433 LISTBOX_SetSelection( hwnd, descr, caret, TRUE, FALSE);
2434 LISTBOX_MoveCaret( hwnd, descr, caret, TRUE );
2435 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2436 SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2442 /***********************************************************************
2445 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2448 MEASUREITEMSTRUCT mis;
2451 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2454 GetClientRect( hwnd, &rect );
2455 descr->owner = GetParent( hwnd );
2456 descr->style = GetWindowLongA( hwnd, GWL_STYLE );
2457 descr->width = rect.right - rect.left;
2458 descr->height = rect.bottom - rect.top;
2459 descr->items = NULL;
2460 descr->nb_items = 0;
2461 descr->top_item = 0;
2462 descr->selected_item = -1;
2463 descr->focus_item = 0;
2464 descr->anchor_item = -1;
2465 descr->item_height = 1;
2466 descr->page_size = 1;
2467 descr->column_width = 150;
2468 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0;
2469 descr->horz_pos = 0;
2472 descr->caret_on = lphc ? FALSE : TRUE;
2473 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2474 descr->in_focus = FALSE;
2475 descr->captured = FALSE;
2477 descr->locale = 0; /* FIXME */
2480 if (is_old_app(hwnd) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2482 /* Win95 document "List Box Differences" from MSDN:
2483 If a list box in a version 3.x application has either the
2484 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2485 horizontal and vertical scroll bars.
2487 descr->style |= WS_VSCROLL | WS_HSCROLL;
2492 TRACE("[%p]: resetting owner %p -> %p\n", hwnd, descr->owner, lphc->self );
2493 descr->owner = lphc->self;
2496 SetWindowLongA( hwnd, 0, (LONG)descr );
2498 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2500 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2501 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2502 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2503 descr->item_height = LISTBOX_SetFont( hwnd, descr, 0 );
2505 if (descr->style & LBS_OWNERDRAWFIXED)
2507 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2509 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2510 descr->item_height = lphc->fixedOwnerDrawHeight;
2514 UINT id = (UINT)GetWindowLongPtrW( hwnd, GWLP_ID );
2515 mis.CtlType = ODT_LISTBOX;
2520 mis.itemHeight = descr->item_height;
2521 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2522 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2526 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2531 /***********************************************************************
2534 static BOOL LISTBOX_Destroy( HWND hwnd, LB_DESCR *descr )
2536 LISTBOX_ResetContent( hwnd, descr );
2537 SetWindowLongA( hwnd, 0, 0 );
2538 HeapFree( GetProcessHeap(), 0, descr );
2543 /***********************************************************************
2544 * ListBoxWndProc_common
2546 static LRESULT WINAPI ListBoxWndProc_common( HWND hwnd, UINT msg,
2547 WPARAM wParam, LPARAM lParam, BOOL unicode )
2551 LPHEADCOMBO lphc = 0;
2553 if (!IsWindow(hwnd)) return 0;
2555 if (!(descr = (LB_DESCR *)GetWindowLongW( hwnd, 0 )))
2557 if (msg == WM_CREATE)
2559 CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam;
2560 if (lpcs->style & LBS_COMBOBOX) lphc = (LPHEADCOMBO)lpcs->lpCreateParams;
2561 if (!LISTBOX_Create( hwnd, lphc )) return -1;
2562 TRACE("creating wnd=%p descr=%lx\n", hwnd, GetWindowLongW( hwnd, 0 ) );
2565 /* Ignore all other messages before we get a WM_CREATE */
2566 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2567 DefWindowProcA( hwnd, msg, wParam, lParam );
2569 if (descr->style & LBS_COMBOBOX) lphc = descr->lphc;
2571 TRACE("[%p]: msg %s wp %08x lp %08lx\n",
2572 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
2576 case LB_RESETCONTENT16:
2577 case LB_RESETCONTENT:
2578 LISTBOX_ResetContent( hwnd, descr );
2579 LISTBOX_UpdateScroll( hwnd, descr );
2580 InvalidateRect( hwnd, NULL, TRUE );
2583 case LB_ADDSTRING16:
2584 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2590 if(unicode || !HAS_STRINGS(descr))
2591 textW = (LPWSTR)lParam;
2594 LPSTR textA = (LPSTR)lParam;
2595 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2596 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2597 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2599 wParam = LISTBOX_FindStringPos( hwnd, descr, textW, FALSE );
2600 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2601 if (!unicode && HAS_STRINGS(descr))
2602 HeapFree(GetProcessHeap(), 0, textW);
2606 case LB_INSERTSTRING16:
2607 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2608 wParam = (INT)(INT16)wParam;
2610 case LB_INSERTSTRING:
2614 if(unicode || !HAS_STRINGS(descr))
2615 textW = (LPWSTR)lParam;
2618 LPSTR textA = (LPSTR)lParam;
2619 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2620 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2621 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2623 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2624 if(!unicode && HAS_STRINGS(descr))
2625 HeapFree(GetProcessHeap(), 0, textW);
2630 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2636 if(unicode || !HAS_STRINGS(descr))
2637 textW = (LPWSTR)lParam;
2640 LPSTR textA = (LPSTR)lParam;
2641 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2642 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2643 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2645 wParam = LISTBOX_FindFileStrPos( hwnd, descr, textW );
2646 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2647 if(!unicode && HAS_STRINGS(descr))
2648 HeapFree(GetProcessHeap(), 0, textW);
2652 case LB_DELETESTRING16:
2653 case LB_DELETESTRING:
2654 if (LISTBOX_RemoveItem( hwnd, descr, wParam) != LB_ERR)
2655 return descr->nb_items;
2659 case LB_GETITEMDATA16:
2660 case LB_GETITEMDATA:
2661 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2663 return descr->items[wParam].data;
2665 case LB_SETITEMDATA16:
2666 case LB_SETITEMDATA:
2667 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2669 descr->items[wParam].data = (DWORD)lParam;
2674 return descr->nb_items;
2677 lParam = (LPARAM)MapSL(lParam);
2680 return LISTBOX_GetText( descr, wParam, lParam, unicode );
2682 case LB_GETTEXTLEN16:
2685 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2687 if (!HAS_STRINGS(descr)) return sizeof(DWORD);
2688 if (unicode) return strlenW( descr->items[wParam].str );
2689 return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
2690 strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
2692 case LB_GETCURSEL16:
2694 if (descr->nb_items == 0)
2696 if (!IS_MULTISELECT(descr))
2697 return descr->selected_item;
2698 if (descr->selected_item != -1)
2699 return descr->selected_item;
2700 return descr->focus_item;
2701 /* otherwise, if the user tries to move the selection with the */
2702 /* arrow keys, we will give the application something to choke on */
2703 case LB_GETTOPINDEX16:
2704 case LB_GETTOPINDEX:
2705 return descr->top_item;
2707 case LB_GETITEMHEIGHT16:
2708 case LB_GETITEMHEIGHT:
2709 return LISTBOX_GetItemHeight( descr, wParam );
2711 case LB_SETITEMHEIGHT16:
2712 lParam = LOWORD(lParam);
2714 case LB_SETITEMHEIGHT:
2715 return LISTBOX_SetItemHeight( hwnd, descr, wParam, lParam, TRUE );
2717 case LB_ITEMFROMPOINT:
2722 pt.x = LOWORD(lParam);
2723 pt.y = HIWORD(lParam);
2726 rect.right = descr->width;
2727 rect.bottom = descr->height;
2729 return MAKELONG( LISTBOX_GetItemFromPoint(descr, pt.x, pt.y),
2730 !PtInRect( &rect, pt ) );
2733 case LB_SETCARETINDEX16:
2734 case LB_SETCARETINDEX:
2735 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2736 if (LISTBOX_SetCaretIndex( hwnd, descr, wParam, !lParam ) == LB_ERR)
2743 case LB_GETCARETINDEX16:
2744 case LB_GETCARETINDEX:
2745 return descr->focus_item;
2747 case LB_SETTOPINDEX16:
2748 case LB_SETTOPINDEX:
2749 return LISTBOX_SetTopItem( hwnd, descr, wParam, TRUE );
2751 case LB_SETCOLUMNWIDTH16:
2752 case LB_SETCOLUMNWIDTH:
2753 return LISTBOX_SetColumnWidth( hwnd, descr, wParam );
2755 case LB_GETITEMRECT16:
2758 RECT16 *r16 = MapSL(lParam);
2759 ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2760 r16->left = rect.left;
2761 r16->top = rect.top;
2762 r16->right = rect.right;
2763 r16->bottom = rect.bottom;
2767 case LB_GETITEMRECT:
2768 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2770 case LB_FINDSTRING16:
2771 wParam = (INT)(INT16)wParam;
2772 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2778 if(unicode || !HAS_STRINGS(descr))
2779 textW = (LPWSTR)lParam;
2782 LPSTR textA = (LPSTR)lParam;
2783 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2784 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2785 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2787 ret = LISTBOX_FindString( hwnd, descr, wParam, textW, FALSE );
2788 if(!unicode && HAS_STRINGS(descr))
2789 HeapFree(GetProcessHeap(), 0, textW);
2793 case LB_FINDSTRINGEXACT16:
2794 wParam = (INT)(INT16)wParam;
2795 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2797 case LB_FINDSTRINGEXACT:
2801 if(unicode || !HAS_STRINGS(descr))
2802 textW = (LPWSTR)lParam;
2805 LPSTR textA = (LPSTR)lParam;
2806 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2807 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2808 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2810 ret = LISTBOX_FindString( hwnd, descr, wParam, textW, TRUE );
2811 if(!unicode && HAS_STRINGS(descr))
2812 HeapFree(GetProcessHeap(), 0, textW);
2816 case LB_SELECTSTRING16:
2817 wParam = (INT)(INT16)wParam;
2818 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2820 case LB_SELECTSTRING:
2825 if(HAS_STRINGS(descr))
2826 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2827 debugstr_a((LPSTR)lParam));
2828 if(unicode || !HAS_STRINGS(descr))
2829 textW = (LPWSTR)lParam;
2832 LPSTR textA = (LPSTR)lParam;
2833 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2834 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2835 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2837 index = LISTBOX_FindString( hwnd, descr, wParam, textW, FALSE );
2838 if(!unicode && HAS_STRINGS(descr))
2839 HeapFree(GetProcessHeap(), 0, textW);
2840 if (index != LB_ERR)
2842 LISTBOX_MoveCaret( hwnd, descr, index, TRUE );
2843 LISTBOX_SetSelection( hwnd, descr, index, TRUE, FALSE );
2849 wParam = (INT)(INT16)wParam;
2852 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2854 return descr->items[wParam].selected;
2857 lParam = (INT)(INT16)lParam;
2860 return LISTBOX_SetSelection( hwnd, descr, lParam, wParam, FALSE );
2862 case LB_SETCURSEL16:
2863 wParam = (INT)(INT16)wParam;
2866 if (IS_MULTISELECT(descr)) return LB_ERR;
2867 LISTBOX_SetCaretIndex( hwnd, descr, wParam, TRUE );
2868 ret = LISTBOX_SetSelection( hwnd, descr, wParam, TRUE, FALSE );
2869 if (lphc && ret != LB_ERR) ret = descr->selected_item;
2872 case LB_GETSELCOUNT16:
2873 case LB_GETSELCOUNT:
2874 return LISTBOX_GetSelCount( descr );
2876 case LB_GETSELITEMS16:
2877 return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2879 case LB_GETSELITEMS:
2880 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2882 case LB_SELITEMRANGE16:
2883 case LB_SELITEMRANGE:
2884 if (LOWORD(lParam) <= HIWORD(lParam))
2885 return LISTBOX_SelectItemRange( hwnd, descr, LOWORD(lParam),
2886 HIWORD(lParam), wParam );
2888 return LISTBOX_SelectItemRange( hwnd, descr, HIWORD(lParam),
2889 LOWORD(lParam), wParam );
2891 case LB_SELITEMRANGEEX16:
2892 case LB_SELITEMRANGEEX:
2893 if ((INT)lParam >= (INT)wParam)
2894 return LISTBOX_SelectItemRange( hwnd, descr, wParam, lParam, TRUE );
2896 return LISTBOX_SelectItemRange( hwnd, descr, lParam, wParam, FALSE);
2898 case LB_GETHORIZONTALEXTENT16:
2899 case LB_GETHORIZONTALEXTENT:
2900 return descr->horz_extent;
2902 case LB_SETHORIZONTALEXTENT16:
2903 case LB_SETHORIZONTALEXTENT:
2904 return LISTBOX_SetHorizontalExtent( hwnd, descr, wParam );
2906 case LB_GETANCHORINDEX16:
2907 case LB_GETANCHORINDEX:
2908 return descr->anchor_item;
2910 case LB_SETANCHORINDEX16:
2911 wParam = (INT)(INT16)wParam;
2913 case LB_SETANCHORINDEX:
2914 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2916 descr->anchor_item = (INT)wParam;
2920 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2921 * be set automatically (this is different in Win32) */
2922 if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2923 lParam = (LPARAM)MapSL(lParam);
2930 textW = (LPWSTR)lParam;
2933 LPSTR textA = (LPSTR)lParam;
2934 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2935 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2936 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2938 ret = LISTBOX_Directory( hwnd, descr, wParam, textW, msg == LB_DIR );
2940 HeapFree(GetProcessHeap(), 0, textW);
2945 return descr->locale;
2948 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2951 case LB_INITSTORAGE:
2952 return LISTBOX_InitStorage( hwnd, descr, wParam );
2955 return LISTBOX_SetCount( hwnd, descr, (INT)wParam );
2957 case LB_SETTABSTOPS16:
2958 return LISTBOX_SetTabStops( hwnd, descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2960 case LB_SETTABSTOPS:
2961 return LISTBOX_SetTabStops( hwnd, descr, wParam, (LPINT)lParam, FALSE );
2965 if (descr->caret_on)
2967 descr->caret_on = TRUE;
2968 if ((descr->focus_item != -1) && (descr->in_focus))
2969 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2974 if (!descr->caret_on)
2976 descr->caret_on = FALSE;
2977 if ((descr->focus_item != -1) && (descr->in_focus))
2978 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2982 return LISTBOX_Destroy( hwnd, descr );
2985 InvalidateRect( hwnd, NULL, TRUE );
2989 LISTBOX_SetRedraw( hwnd, descr, wParam != 0 );
2993 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2998 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( hwnd, &ps );
2999 ret = LISTBOX_Paint( hwnd, descr, hdc );
3000 if( !wParam ) EndPaint( hwnd, &ps );
3004 LISTBOX_UpdateSize( hwnd, descr );
3007 return (LRESULT)descr->font;
3009 LISTBOX_SetFont( hwnd, descr, (HFONT)wParam );
3010 if (lParam) InvalidateRect( hwnd, 0, TRUE );
3013 descr->in_focus = TRUE;
3014 descr->caret_on = TRUE;
3015 if (descr->focus_item != -1)
3016 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
3017 SEND_NOTIFICATION( hwnd, descr, LBN_SETFOCUS );
3020 descr->in_focus = FALSE;
3021 if ((descr->focus_item != -1) && descr->caret_on)
3022 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
3023 SEND_NOTIFICATION( hwnd, descr, LBN_KILLFOCUS );
3026 return LISTBOX_HandleHScroll( hwnd, descr, wParam );
3028 return LISTBOX_HandleVScroll( hwnd, descr, wParam );
3030 if (wParam & (MK_SHIFT | MK_CONTROL))
3031 return DefWindowProcW( hwnd, msg, wParam, lParam );
3032 return LISTBOX_HandleMouseWheel( hwnd, descr, wParam );
3033 case WM_LBUTTONDOWN:
3035 return LISTBOX_HandleLButtonDownCombo(hwnd, descr, msg, wParam,
3036 (INT16)LOWORD(lParam),
3037 (INT16)HIWORD(lParam) );
3038 return LISTBOX_HandleLButtonDown( hwnd, descr, wParam,
3039 (INT16)LOWORD(lParam),
3040 (INT16)HIWORD(lParam) );
3041 case WM_LBUTTONDBLCLK:
3043 return LISTBOX_HandleLButtonDownCombo(hwnd, descr, msg, wParam,
3044 (INT16)LOWORD(lParam),
3045 (INT16)HIWORD(lParam) );
3046 if (descr->style & LBS_NOTIFY)
3047 SEND_NOTIFICATION( hwnd, descr, LBN_DBLCLK );
3050 if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) )
3052 BOOL captured = descr->captured;
3056 mousePos.x = (INT16)LOWORD(lParam);
3057 mousePos.y = (INT16)HIWORD(lParam);
3060 * If we are in a dropdown combobox, we simulate that
3061 * the mouse is captured to show the tracking of the item.
3063 if (GetClientRect(hwnd, &clientRect) && PtInRect( &clientRect, mousePos ))
3064 descr->captured = TRUE;
3066 LISTBOX_HandleMouseMove( hwnd, descr, mousePos.x, mousePos.y);
3068 descr->captured = captured;
3070 else if (GetCapture() == hwnd)
3072 LISTBOX_HandleMouseMove( hwnd, descr, (INT16)LOWORD(lParam),
3073 (INT16)HIWORD(lParam) );
3083 * If the mouse button "up" is not in the listbox,
3084 * we make sure there is no selection by re-selecting the
3085 * item that was selected when the listbox was made visible.
3087 mousePos.x = (INT16)LOWORD(lParam);
3088 mousePos.y = (INT16)HIWORD(lParam);
3090 GetClientRect(hwnd, &clientRect);
3093 * When the user clicks outside the combobox and the focus
3094 * is lost, the owning combobox will send a fake buttonup with
3095 * 0xFFFFFFF as the mouse location, we must also revert the
3096 * selection to the original selection.
3098 if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) )
3099 LISTBOX_MoveCaret( hwnd, descr, lphc->droppedIndex, FALSE );
3101 return LISTBOX_HandleLButtonUp( hwnd, descr );
3103 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3105 /* for some reason Windows makes it possible to
3106 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3108 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3109 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3110 && (wParam == VK_DOWN || wParam == VK_UP)) )
3112 COMBO_FlipListbox( lphc, FALSE, FALSE );
3116 return LISTBOX_HandleKeyDown( hwnd, descr, wParam );
3121 charW = (WCHAR)wParam;
3124 CHAR charA = (CHAR)wParam;
3125 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
3127 return LISTBOX_HandleChar( hwnd, descr, charW );
3130 return LISTBOX_HandleSystemTimer( hwnd, descr );
3132 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
3135 HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
3136 wParam, (LPARAM)hwnd );
3137 TRACE("hbrush = %p\n", hbrush);
3139 hbrush = GetSysColorBrush(COLOR_WINDOW);
3142 GetClientRect(hwnd, &rect);
3143 FillRect((HDC)wParam, &rect, hbrush);
3148 if( lphc ) return 0;
3149 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3150 SendMessageA( descr->owner, msg, wParam, lParam );
3153 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3162 if ((msg >= WM_USER) && (msg < 0xc000))
3163 WARN("[%p]: unknown msg %04x wp %08x lp %08lx\n",
3164 hwnd, msg, wParam, lParam );
3167 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3168 DefWindowProcA( hwnd, msg, wParam, lParam );
3171 /***********************************************************************
3174 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3176 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3179 /***********************************************************************
3182 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3184 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE );