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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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.
42 #include "wine/unicode.h"
43 #include "user_private.h"
45 #include "wine/exception.h"
46 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(listbox);
50 /* Items array granularity */
51 #define LB_ARRAY_GRANULARITY 16
53 /* Scrolling timeout in ms */
54 #define LB_SCROLL_TIMEOUT 50
56 /* Listbox system timer id */
59 /* flag listbox changed while setredraw false - internal style */
60 #define LBS_DISPLAYCHANGED 0x80000000
65 LPWSTR str; /* Item text */
66 BOOL selected; /* Is item selected? */
67 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
68 ULONG_PTR data; /* User data */
71 /* Listbox structure */
74 HWND self; /* Our own window handle */
75 HWND owner; /* Owner window to send notifications to */
76 UINT style; /* Window style */
77 INT width; /* Window width */
78 INT height; /* Window height */
79 LB_ITEMDATA *items; /* Array of items */
80 INT nb_items; /* Number of items */
81 INT top_item; /* Top visible item */
82 INT selected_item; /* Selected item */
83 INT focus_item; /* Item that has the focus */
84 INT anchor_item; /* Anchor item for extended selection */
85 INT item_height; /* Default item height */
86 INT page_size; /* Items per listbox page */
87 INT column_width; /* Column width for multi-column listboxes */
88 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
89 INT horz_pos; /* Horizontal position */
90 INT nb_tabs; /* Number of tabs in array */
91 INT *tabs; /* Array of tabs */
92 INT avg_char_width; /* Average width of characters */
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|LBS_EXTENDEDSEL) && \
111 !((descr)->style & LBS_NOSEL))
113 #define SEND_NOTIFICATION(descr,code) \
114 (SendMessageW( (descr)->owner, WM_COMMAND, \
115 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
117 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
119 /* Current timer status */
129 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
131 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
132 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
134 static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect );
136 /*********************************************************************
137 * listbox class descriptor
139 static const WCHAR listboxW[] = {'L','i','s','t','B','o','x',0};
140 const struct builtin_class_descr LISTBOX_builtin_class =
143 CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
144 ListBoxWndProcA, /* procA */
145 ListBoxWndProcW, /* procW */
146 sizeof(LB_DESCR *), /* extra */
147 IDC_ARROW, /* cursor */
152 /*********************************************************************
153 * combolbox class descriptor
155 static const WCHAR combolboxW[] = {'C','o','m','b','o','L','B','o','x',0};
156 const struct builtin_class_descr COMBOLBOX_builtin_class =
158 combolboxW, /* name */
159 CS_DBLCLKS | CS_SAVEBITS, /* style */
160 ListBoxWndProcA, /* procA */
161 ListBoxWndProcW, /* procW */
162 sizeof(LB_DESCR *), /* extra */
163 IDC_ARROW, /* cursor */
168 /* check whether app is a Win 3.1 app */
169 static inline BOOL is_old_app( LB_DESCR *descr )
171 return (GetExpWinVer16( GetWindowLongPtrW(descr->self, GWLP_HINSTANCE) ) & 0xFF00 ) == 0x0300;
175 /***********************************************************************
176 * LISTBOX_GetCurrentPageSize
178 * Return the current page size
180 static INT LISTBOX_GetCurrentPageSize( const LB_DESCR *descr )
183 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
184 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
186 if ((height += descr->items[i].height) > descr->height) break;
188 if (i == descr->top_item) return 1;
189 else return i - descr->top_item;
193 /***********************************************************************
194 * LISTBOX_GetMaxTopIndex
196 * Return the maximum possible index for the top of the listbox.
198 static INT LISTBOX_GetMaxTopIndex( const LB_DESCR *descr )
202 if (descr->style & LBS_OWNERDRAWVARIABLE)
204 page = descr->height;
205 for (max = descr->nb_items - 1; max >= 0; max--)
206 if ((page -= descr->items[max].height) < 0) break;
207 if (max < descr->nb_items - 1) max++;
209 else if (descr->style & LBS_MULTICOLUMN)
211 if ((page = descr->width / descr->column_width) < 1) page = 1;
212 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
213 max = (max - page) * descr->page_size;
217 max = descr->nb_items - descr->page_size;
219 if (max < 0) max = 0;
224 /***********************************************************************
225 * LISTBOX_UpdateScroll
227 * Update the scrollbars. Should be called whenever the content
228 * of the listbox changes.
230 static void LISTBOX_UpdateScroll( LB_DESCR *descr )
234 /* Check the listbox scroll bar flags individually before we call
235 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
236 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
237 scroll bar when we do not need one.
238 if (!(descr->style & WS_VSCROLL)) return;
241 /* It is important that we check descr->style, and not wnd->dwStyle,
242 for WS_VSCROLL, as the former is exactly the one passed in
243 argument to CreateWindow.
244 In Windows (and from now on in Wine :) a listbox created
245 with such a style (no WS_SCROLL) does not update
246 the scrollbar with listbox-related data, thus letting
247 the programmer use it for his/her own purposes. */
249 if (descr->style & LBS_NOREDRAW) return;
250 info.cbSize = sizeof(info);
252 if (descr->style & LBS_MULTICOLUMN)
255 info.nMax = (descr->nb_items - 1) / descr->page_size;
256 info.nPos = descr->top_item / descr->page_size;
257 info.nPage = descr->width / descr->column_width;
258 if (info.nPage < 1) info.nPage = 1;
259 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
260 if (descr->style & LBS_DISABLENOSCROLL)
261 info.fMask |= SIF_DISABLENOSCROLL;
262 if (descr->style & WS_HSCROLL)
263 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
265 info.fMask = SIF_RANGE;
266 if (descr->style & WS_VSCROLL)
267 SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
272 info.nMax = descr->nb_items - 1;
273 info.nPos = descr->top_item;
274 info.nPage = LISTBOX_GetCurrentPageSize( descr );
275 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
276 if (descr->style & LBS_DISABLENOSCROLL)
277 info.fMask |= SIF_DISABLENOSCROLL;
278 if (descr->style & WS_VSCROLL)
279 SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
281 if (descr->horz_extent)
284 info.nMax = descr->horz_extent - 1;
285 info.nPos = descr->horz_pos;
286 info.nPage = descr->width;
287 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
288 if (descr->style & LBS_DISABLENOSCROLL)
289 info.fMask |= SIF_DISABLENOSCROLL;
290 if (descr->style & WS_HSCROLL)
291 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
297 /***********************************************************************
300 * Set the top item of the listbox, scrolling up or down if necessary.
302 static LRESULT LISTBOX_SetTopItem( LB_DESCR *descr, INT index, BOOL scroll )
304 INT max = LISTBOX_GetMaxTopIndex( descr );
306 TRACE("setting top item %d, scroll %d\n", index, scroll);
308 if (index > max) index = max;
309 if (index < 0) index = 0;
310 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
311 if (descr->top_item == index) return LB_OKAY;
312 if (descr->style & LBS_MULTICOLUMN)
314 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
315 if (scroll && (abs(diff) < descr->width))
316 ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
317 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
325 if (descr->style & LBS_OWNERDRAWVARIABLE)
329 if (index > descr->top_item)
331 for (i = index - 1; i >= descr->top_item; i--)
332 diff -= descr->items[i].height;
336 for (i = index; i < descr->top_item; i++)
337 diff += descr->items[i].height;
341 diff = (descr->top_item - index) * descr->item_height;
343 if (abs(diff) < descr->height)
344 ScrollWindowEx( descr->self, 0, diff, NULL, NULL, 0, NULL,
345 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
349 if (!scroll) InvalidateRect( descr->self, NULL, TRUE );
350 descr->top_item = index;
351 LISTBOX_UpdateScroll( descr );
356 /***********************************************************************
359 * Update the page size. Should be called when the size of
360 * the client area or the item height changes.
362 static void LISTBOX_UpdatePage( LB_DESCR *descr )
366 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
368 if (page_size == descr->page_size) return;
369 descr->page_size = page_size;
370 if (descr->style & LBS_MULTICOLUMN)
371 InvalidateRect( descr->self, NULL, TRUE );
372 LISTBOX_SetTopItem( descr, descr->top_item, FALSE );
376 /***********************************************************************
379 * Update the size of the listbox. Should be called when the size of
380 * the client area changes.
382 static void LISTBOX_UpdateSize( LB_DESCR *descr )
386 GetClientRect( descr->self, &rect );
387 descr->width = rect.right - rect.left;
388 descr->height = rect.bottom - rect.top;
389 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
394 GetWindowRect( descr->self, &rect );
395 if(descr->item_height != 0)
396 remaining = descr->height % descr->item_height;
399 if ((descr->height > descr->item_height) && remaining)
401 if (is_old_app(descr))
402 { /* give a margin for error to 16 bits programs - if we need
403 less than the height of the nonclient area, round to the
404 *next* number of items */
405 int ncheight = rect.bottom - rect.top - descr->height;
406 if ((descr->item_height - remaining) <= ncheight)
407 remaining = remaining - descr->item_height;
409 TRACE("[%p]: changing height %d -> %d\n",
410 descr->self, descr->height, descr->height - remaining );
411 SetWindowPos( descr->self, 0, 0, 0, rect.right - rect.left,
412 rect.bottom - rect.top - remaining,
413 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
417 TRACE("[%p]: new size = %d,%d\n", descr->self, descr->width, descr->height );
418 LISTBOX_UpdatePage( descr );
419 LISTBOX_UpdateScroll( descr );
421 /* Invalidate the focused item so it will be repainted correctly */
422 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
424 InvalidateRect( descr->self, &rect, FALSE );
429 /***********************************************************************
430 * LISTBOX_GetItemRect
432 * Get the rectangle enclosing an item, in listbox client coordinates.
433 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
435 static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect )
437 /* Index <= 0 is legal even on empty listboxes */
438 if (index && (index >= descr->nb_items))
440 memset(rect, 0, sizeof(*rect));
441 SetLastError(ERROR_INVALID_INDEX);
444 SetRect( rect, 0, 0, descr->width, descr->height );
445 if (descr->style & LBS_MULTICOLUMN)
447 INT col = (index / descr->page_size) -
448 (descr->top_item / descr->page_size);
449 rect->left += col * descr->column_width;
450 rect->right = rect->left + descr->column_width;
451 rect->top += (index % descr->page_size) * descr->item_height;
452 rect->bottom = rect->top + descr->item_height;
454 else if (descr->style & LBS_OWNERDRAWVARIABLE)
457 rect->right += descr->horz_pos;
458 if ((index >= 0) && (index < descr->nb_items))
460 if (index < descr->top_item)
462 for (i = descr->top_item-1; i >= index; i--)
463 rect->top -= descr->items[i].height;
467 for (i = descr->top_item; i < index; i++)
468 rect->top += descr->items[i].height;
470 rect->bottom = rect->top + descr->items[index].height;
476 rect->top += (index - descr->top_item) * descr->item_height;
477 rect->bottom = rect->top + descr->item_height;
478 rect->right += descr->horz_pos;
481 TRACE("item %d, rect %s\n", index, wine_dbgstr_rect(rect));
483 return ((rect->left < descr->width) && (rect->right > 0) &&
484 (rect->top < descr->height) && (rect->bottom > 0));
488 /***********************************************************************
489 * LISTBOX_GetItemFromPoint
491 * Return the item nearest from point (x,y) (in client coordinates).
493 static INT LISTBOX_GetItemFromPoint( const LB_DESCR *descr, INT x, INT y )
495 INT index = descr->top_item;
497 if (!descr->nb_items) return -1; /* No items */
498 if (descr->style & LBS_OWNERDRAWVARIABLE)
503 while (index < descr->nb_items)
505 if ((pos += descr->items[index].height) > y) break;
514 if ((pos -= descr->items[index].height) <= y) break;
518 else if (descr->style & LBS_MULTICOLUMN)
520 if (y >= descr->item_height * descr->page_size) return -1;
521 if (y >= 0) index += y / descr->item_height;
522 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
523 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
527 index += (y / descr->item_height);
529 if (index < 0) return 0;
530 if (index >= descr->nb_items) return -1;
535 /***********************************************************************
540 static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect,
541 INT index, UINT action, BOOL ignoreFocus )
543 LB_ITEMDATA *item = NULL;
544 if (index < descr->nb_items) item = &descr->items[index];
546 if (IS_OWNERDRAW(descr))
554 if (action == ODA_FOCUS)
555 DrawFocusRect( hdc, rect );
557 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
561 /* some programs mess with the clipping region when
562 drawing the item, *and* restore the previous region
563 after they are done, so a region has better to exist
564 else everything ends clipped */
565 GetClientRect(descr->self, &r);
566 hrgn = CreateRectRgnIndirect(&r);
567 SelectClipRgn( hdc, hrgn);
568 DeleteObject( hrgn );
570 dis.CtlType = ODT_LISTBOX;
571 dis.CtlID = GetWindowLongPtrW( descr->self, GWLP_ID );
572 dis.hwndItem = descr->self;
573 dis.itemAction = action;
577 if (item->selected) dis.itemState |= ODS_SELECTED;
578 if (!ignoreFocus && (descr->focus_item == index) &&
580 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
581 if (!IsWindowEnabled(descr->self)) dis.itemState |= ODS_DISABLED;
582 dis.itemData = item->data;
584 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
585 descr->self, index, item ? debugstr_w(item->str) : "", action,
586 dis.itemState, wine_dbgstr_rect(rect) );
587 SendMessageW(descr->owner, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
591 COLORREF oldText = 0, oldBk = 0;
593 if (action == ODA_FOCUS)
595 DrawFocusRect( hdc, rect );
598 if (item && item->selected)
600 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
601 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
604 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
605 descr->self, index, item ? debugstr_w(item->str) : "", action,
606 wine_dbgstr_rect(rect) );
608 ExtTextOutW( hdc, rect->left + 1, rect->top,
609 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
610 else if (!(descr->style & LBS_USETABSTOPS))
611 ExtTextOutW( hdc, rect->left + 1, rect->top,
612 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
613 strlenW(item->str), NULL );
616 /* Output empty string to paint background in the full width. */
617 ExtTextOutW( hdc, rect->left + 1, rect->top,
618 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
619 TabbedTextOutW( hdc, rect->left + 1 , rect->top,
620 item->str, strlenW(item->str),
621 descr->nb_tabs, descr->tabs, 0);
623 if (item && item->selected)
625 SetBkColor( hdc, oldBk );
626 SetTextColor( hdc, oldText );
628 if (!ignoreFocus && (descr->focus_item == index) &&
630 (descr->in_focus)) DrawFocusRect( hdc, rect );
635 /***********************************************************************
638 * Change the redraw flag.
640 static void LISTBOX_SetRedraw( LB_DESCR *descr, BOOL on )
644 if (!(descr->style & LBS_NOREDRAW)) return;
645 descr->style &= ~LBS_NOREDRAW;
646 if (descr->style & LBS_DISPLAYCHANGED)
647 { /* page was changed while setredraw false, refresh automatically */
648 InvalidateRect(descr->self, NULL, TRUE);
649 if ((descr->top_item + descr->page_size) > descr->nb_items)
650 { /* reset top of page if less than number of items/page */
651 descr->top_item = descr->nb_items - descr->page_size;
652 if (descr->top_item < 0) descr->top_item = 0;
654 descr->style &= ~LBS_DISPLAYCHANGED;
656 LISTBOX_UpdateScroll( descr );
658 else descr->style |= LBS_NOREDRAW;
662 /***********************************************************************
663 * LISTBOX_RepaintItem
665 * Repaint a single item synchronously.
667 static void LISTBOX_RepaintItem( LB_DESCR *descr, INT index, UINT action )
672 HBRUSH hbrush, oldBrush = 0;
674 /* Do not repaint the item if the item is not visible */
675 if (!IsWindowVisible(descr->self)) return;
676 if (descr->style & LBS_NOREDRAW)
678 descr->style |= LBS_DISPLAYCHANGED;
681 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
682 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
683 if (descr->font) oldFont = SelectObject( hdc, descr->font );
684 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
685 (WPARAM)hdc, (LPARAM)descr->self );
686 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
687 if (!IsWindowEnabled(descr->self))
688 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
689 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
690 LISTBOX_PaintItem( descr, hdc, &rect, index, action, TRUE );
691 if (oldFont) SelectObject( hdc, oldFont );
692 if (oldBrush) SelectObject( hdc, oldBrush );
693 ReleaseDC( descr->self, hdc );
697 /***********************************************************************
698 * LISTBOX_DrawFocusRect
700 static void LISTBOX_DrawFocusRect( LB_DESCR *descr, BOOL on )
706 /* Do not repaint the item if the item is not visible */
707 if (!IsWindowVisible(descr->self)) return;
709 if (descr->focus_item == -1) return;
710 if (!descr->caret_on || !descr->in_focus) return;
712 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) != 1) return;
713 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
714 if (descr->font) oldFont = SelectObject( hdc, descr->font );
715 if (!IsWindowEnabled(descr->self))
716 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
717 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
718 LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, on ? FALSE : TRUE );
719 if (oldFont) SelectObject( hdc, oldFont );
720 ReleaseDC( descr->self, hdc );
724 /***********************************************************************
725 * LISTBOX_InitStorage
727 static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items )
731 nb_items += LB_ARRAY_GRANULARITY - 1;
732 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
734 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
735 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
736 nb_items * sizeof(LB_ITEMDATA));
739 item = HeapAlloc( GetProcessHeap(), 0,
740 nb_items * sizeof(LB_ITEMDATA));
745 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
753 /***********************************************************************
754 * LISTBOX_SetTabStops
756 static BOOL LISTBOX_SetTabStops( LB_DESCR *descr, INT count, LPINT tabs )
760 if (!(descr->style & LBS_USETABSTOPS))
762 SetLastError(ERROR_LB_WITHOUT_TABSTOPS);
766 HeapFree( GetProcessHeap(), 0, descr->tabs );
767 if (!(descr->nb_tabs = count))
772 if (!(descr->tabs = HeapAlloc( GetProcessHeap(), 0,
773 descr->nb_tabs * sizeof(INT) )))
775 memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
777 /* convert into "dialog units"*/
778 for (i = 0; i < descr->nb_tabs; i++)
779 descr->tabs[i] = MulDiv(descr->tabs[i], descr->avg_char_width, 4);
785 /***********************************************************************
788 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPWSTR buffer, BOOL unicode )
792 if ((index < 0) || (index >= descr->nb_items))
794 SetLastError(ERROR_INVALID_INDEX);
797 if (HAS_STRINGS(descr))
801 len = strlenW(descr->items[index].str);
804 return WideCharToMultiByte( CP_ACP, 0, descr->items[index].str, len,
805 NULL, 0, NULL, NULL );
808 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
810 __TRY /* hide a Delphi bug that passes a read-only buffer */
814 strcpyW( buffer, descr->items[index].str );
815 len = strlenW(buffer);
819 len = WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1,
820 (LPSTR)buffer, 0x7FFFFFFF, NULL, NULL) - 1;
825 WARN( "got an invalid buffer (Delphi bug?)\n" );
826 SetLastError( ERROR_INVALID_PARAMETER );
832 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
838 static inline INT LISTBOX_lstrcmpiW( LCID lcid, LPCWSTR str1, LPCWSTR str2 )
840 INT ret = CompareStringW( lcid, NORM_IGNORECASE, str1, -1, str2, -1 );
841 if (ret == CSTR_LESS_THAN)
843 if (ret == CSTR_EQUAL)
845 if (ret == CSTR_GREATER_THAN)
850 /***********************************************************************
851 * LISTBOX_FindStringPos
853 * Find the nearest string located before a given string in sort order.
854 * If 'exact' is TRUE, return an error if we don't get an exact match.
856 static INT LISTBOX_FindStringPos( LB_DESCR *descr, LPCWSTR str, BOOL exact )
858 INT index, min, max, res = -1;
860 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
862 max = descr->nb_items;
865 index = (min + max) / 2;
866 if (HAS_STRINGS(descr))
867 res = LISTBOX_lstrcmpiW( descr->locale, str, descr->items[index].str);
870 COMPAREITEMSTRUCT cis;
871 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
873 cis.CtlType = ODT_LISTBOX;
875 cis.hwndItem = descr->self;
876 /* note that some application (MetaStock) expects the second item
877 * to be in the listbox */
879 cis.itemData1 = (ULONG_PTR)str;
881 cis.itemData2 = descr->items[index].data;
882 cis.dwLocaleId = descr->locale;
883 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
885 if (!res) return index;
886 if (res < 0) max = index;
887 else min = index + 1;
889 return exact ? -1 : max;
893 /***********************************************************************
894 * LISTBOX_FindFileStrPos
896 * Find the nearest string located before a given string in directory
897 * sort order (i.e. first files, then directories, then drives).
899 static INT LISTBOX_FindFileStrPos( LB_DESCR *descr, LPCWSTR str )
901 INT min, max, res = -1;
903 if (!HAS_STRINGS(descr))
904 return LISTBOX_FindStringPos( descr, str, FALSE );
906 max = descr->nb_items;
909 INT index = (min + max) / 2;
910 LPCWSTR p = descr->items[index].str;
911 if (*p == '[') /* drive or directory */
913 if (*str != '[') res = -1;
914 else if (p[1] == '-') /* drive */
916 if (str[1] == '-') res = str[2] - p[2];
921 if (str[1] == '-') res = 1;
922 else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
927 if (*str == '[') res = 1;
928 else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
930 if (!res) return index;
931 if (res < 0) max = index;
932 else min = index + 1;
938 /***********************************************************************
941 * Find the item beginning with a given string.
943 static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exact )
948 if (start >= descr->nb_items) start = -1;
949 item = descr->items + start + 1;
950 if (HAS_STRINGS(descr))
952 if (!str || ! str[0] ) return LB_ERR;
955 for (i = start + 1; i < descr->nb_items; i++, item++)
956 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
957 for (i = 0, item = descr->items; i <= start; i++, item++)
958 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
962 /* Special case for drives and directories: ignore prefix */
963 #define CHECK_DRIVE(item) \
964 if ((item)->str[0] == '[') \
966 if (!strncmpiW( str, (item)->str+1, len )) return i; \
967 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
971 INT len = strlenW(str);
972 for (i = start + 1; i < descr->nb_items; i++, item++)
974 if (!strncmpiW( str, item->str, len )) return i;
977 for (i = 0, item = descr->items; i <= start; i++, item++)
979 if (!strncmpiW( str, item->str, len )) return i;
987 if (exact && (descr->style & LBS_SORT))
988 /* If sorted, use a WM_COMPAREITEM binary search */
989 return LISTBOX_FindStringPos( descr, str, TRUE );
991 /* Otherwise use a linear search */
992 for (i = start + 1; i < descr->nb_items; i++, item++)
993 if (item->data == (ULONG_PTR)str) return i;
994 for (i = 0, item = descr->items; i <= start; i++, item++)
995 if (item->data == (ULONG_PTR)str) return i;
1001 /***********************************************************************
1002 * LISTBOX_GetSelCount
1004 static LRESULT LISTBOX_GetSelCount( const LB_DESCR *descr )
1007 const LB_ITEMDATA *item = descr->items;
1009 if (!(descr->style & LBS_MULTIPLESEL) ||
1010 (descr->style & LBS_NOSEL))
1012 for (i = count = 0; i < descr->nb_items; i++, item++)
1013 if (item->selected) count++;
1018 /***********************************************************************
1019 * LISTBOX_GetSelItems
1021 static LRESULT LISTBOX_GetSelItems( const LB_DESCR *descr, INT max, LPINT array )
1024 const LB_ITEMDATA *item = descr->items;
1026 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1027 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
1028 if (item->selected) array[count++] = i;
1033 /***********************************************************************
1036 static LRESULT LISTBOX_Paint( LB_DESCR *descr, HDC hdc )
1038 INT i, col_pos = descr->page_size - 1;
1040 RECT focusRect = {-1, -1, -1, -1};
1042 HBRUSH hbrush, oldBrush = 0;
1044 if (descr->style & LBS_NOREDRAW) return 0;
1046 SetRect( &rect, 0, 0, descr->width, descr->height );
1047 if (descr->style & LBS_MULTICOLUMN)
1048 rect.right = rect.left + descr->column_width;
1049 else if (descr->horz_pos)
1051 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
1052 rect.right += descr->horz_pos;
1055 if (descr->font) oldFont = SelectObject( hdc, descr->font );
1056 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1057 (WPARAM)hdc, (LPARAM)descr->self );
1058 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1059 if (!IsWindowEnabled(descr->self)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1061 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1064 /* Special case for empty listbox: paint focus rect */
1065 rect.bottom = rect.top + descr->item_height;
1066 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1067 &rect, NULL, 0, NULL );
1068 LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, FALSE );
1069 rect.top = rect.bottom;
1072 /* Paint all the item, regarding the selection
1073 Focus state will be painted after */
1075 for (i = descr->top_item; i < descr->nb_items; i++)
1077 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1078 rect.bottom = rect.top + descr->item_height;
1080 rect.bottom = rect.top + descr->items[i].height;
1082 if (i == descr->focus_item)
1084 /* keep the focus rect, to paint the focus item after */
1085 focusRect.left = rect.left;
1086 focusRect.right = rect.right;
1087 focusRect.top = rect.top;
1088 focusRect.bottom = rect.bottom;
1090 LISTBOX_PaintItem( descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1091 rect.top = rect.bottom;
1093 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1095 if (!IS_OWNERDRAW(descr))
1097 /* Clear the bottom of the column */
1098 if (rect.top < descr->height)
1100 rect.bottom = descr->height;
1101 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1102 &rect, NULL, 0, NULL );
1106 /* Go to the next column */
1107 rect.left += descr->column_width;
1108 rect.right += descr->column_width;
1110 col_pos = descr->page_size - 1;
1115 if (rect.top >= descr->height) break;
1119 /* Paint the focus item now */
1120 if (focusRect.top != focusRect.bottom &&
1121 descr->caret_on && descr->in_focus)
1122 LISTBOX_PaintItem( descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1124 if (!IS_OWNERDRAW(descr))
1126 /* Clear the remainder of the client area */
1127 if (rect.top < descr->height)
1129 rect.bottom = descr->height;
1130 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1131 &rect, NULL, 0, NULL );
1133 if (rect.right < descr->width)
1135 rect.left = rect.right;
1136 rect.right = descr->width;
1138 rect.bottom = descr->height;
1139 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1140 &rect, NULL, 0, NULL );
1143 if (oldFont) SelectObject( hdc, oldFont );
1144 if (oldBrush) SelectObject( hdc, oldBrush );
1149 /***********************************************************************
1150 * LISTBOX_InvalidateItems
1152 * Invalidate all items from a given item. If the specified item is not
1153 * visible, nothing happens.
1155 static void LISTBOX_InvalidateItems( LB_DESCR *descr, INT index )
1159 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1161 if (descr->style & LBS_NOREDRAW)
1163 descr->style |= LBS_DISPLAYCHANGED;
1166 rect.bottom = descr->height;
1167 InvalidateRect( descr->self, &rect, TRUE );
1168 if (descr->style & LBS_MULTICOLUMN)
1170 /* Repaint the other columns */
1171 rect.left = rect.right;
1172 rect.right = descr->width;
1174 InvalidateRect( descr->self, &rect, TRUE );
1179 static void LISTBOX_InvalidateItemRect( LB_DESCR *descr, INT index )
1183 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1184 InvalidateRect( descr->self, &rect, TRUE );
1187 /***********************************************************************
1188 * LISTBOX_GetItemHeight
1190 static LRESULT LISTBOX_GetItemHeight( const LB_DESCR *descr, INT index )
1192 if (descr->style & LBS_OWNERDRAWVARIABLE && descr->nb_items > 0)
1194 if ((index < 0) || (index >= descr->nb_items))
1196 SetLastError(ERROR_INVALID_INDEX);
1199 return descr->items[index].height;
1201 else return descr->item_height;
1205 /***********************************************************************
1206 * LISTBOX_SetItemHeight
1208 static LRESULT LISTBOX_SetItemHeight( LB_DESCR *descr, INT index, INT height, BOOL repaint )
1210 if (height > MAXBYTE)
1213 if (!height) height = 1;
1215 if (descr->style & LBS_OWNERDRAWVARIABLE)
1217 if ((index < 0) || (index >= descr->nb_items))
1219 SetLastError(ERROR_INVALID_INDEX);
1222 TRACE("[%p]: item %d height = %d\n", descr->self, index, height );
1223 descr->items[index].height = height;
1224 LISTBOX_UpdateScroll( descr );
1226 LISTBOX_InvalidateItems( descr, index );
1228 else if (height != descr->item_height)
1230 TRACE("[%p]: new height = %d\n", descr->self, height );
1231 descr->item_height = height;
1232 LISTBOX_UpdatePage( descr );
1233 LISTBOX_UpdateScroll( descr );
1235 InvalidateRect( descr->self, 0, TRUE );
1241 /***********************************************************************
1242 * LISTBOX_SetHorizontalPos
1244 static void LISTBOX_SetHorizontalPos( LB_DESCR *descr, INT pos )
1248 if (pos > descr->horz_extent - descr->width)
1249 pos = descr->horz_extent - descr->width;
1250 if (pos < 0) pos = 0;
1251 if (!(diff = descr->horz_pos - pos)) return;
1252 TRACE("[%p]: new horz pos = %d\n", descr->self, pos );
1253 descr->horz_pos = pos;
1254 LISTBOX_UpdateScroll( descr );
1255 if (abs(diff) < descr->width)
1258 /* Invalidate the focused item so it will be repainted correctly */
1259 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
1260 InvalidateRect( descr->self, &rect, TRUE );
1261 ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
1262 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1265 InvalidateRect( descr->self, NULL, TRUE );
1269 /***********************************************************************
1270 * LISTBOX_SetHorizontalExtent
1272 static LRESULT LISTBOX_SetHorizontalExtent( LB_DESCR *descr, INT extent )
1274 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1276 if (extent <= 0) extent = 1;
1277 if (extent == descr->horz_extent) return LB_OKAY;
1278 TRACE("[%p]: new horz extent = %d\n", descr->self, extent );
1279 descr->horz_extent = extent;
1280 if (descr->horz_pos > extent - descr->width)
1281 LISTBOX_SetHorizontalPos( descr, extent - descr->width );
1283 LISTBOX_UpdateScroll( descr );
1288 /***********************************************************************
1289 * LISTBOX_SetColumnWidth
1291 static LRESULT LISTBOX_SetColumnWidth( LB_DESCR *descr, INT width)
1293 if (width == descr->column_width) return LB_OKAY;
1294 TRACE("[%p]: new column width = %d\n", descr->self, width );
1295 descr->column_width = width;
1296 LISTBOX_UpdatePage( descr );
1301 /***********************************************************************
1304 * Returns the item height.
1306 static INT LISTBOX_SetFont( LB_DESCR *descr, HFONT font )
1310 const char *alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1315 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE )))
1317 ERR("unable to get DC.\n" );
1320 if (font) oldFont = SelectObject( hdc, font );
1321 GetTextExtentPointA( hdc, alphabet, 52, &sz);
1322 if (oldFont) SelectObject( hdc, oldFont );
1323 ReleaseDC( descr->self, hdc );
1325 descr->avg_char_width = (sz.cx / 26 + 1) / 2;
1326 if (!IS_OWNERDRAW(descr))
1327 LISTBOX_SetItemHeight( descr, 0, sz.cy, FALSE );
1332 /***********************************************************************
1333 * LISTBOX_MakeItemVisible
1335 * Make sure that a given item is partially or fully visible.
1337 static void LISTBOX_MakeItemVisible( LB_DESCR *descr, INT index, BOOL fully )
1341 TRACE("current top item %d, index %d, fully %d\n", descr->top_item, index, fully);
1343 if (index <= descr->top_item) top = index;
1344 else if (descr->style & LBS_MULTICOLUMN)
1346 INT cols = descr->width;
1347 if (!fully) cols += descr->column_width - 1;
1348 if (cols >= descr->column_width) cols /= descr->column_width;
1350 if (index < descr->top_item + (descr->page_size * cols)) return;
1351 top = index - descr->page_size * (cols - 1);
1353 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1355 INT height = fully ? descr->items[index].height : 1;
1356 for (top = index; top > descr->top_item; top--)
1357 if ((height += descr->items[top-1].height) > descr->height) break;
1361 if (index < descr->top_item + descr->page_size) return;
1362 if (!fully && (index == descr->top_item + descr->page_size) &&
1363 (descr->height > (descr->page_size * descr->item_height))) return;
1364 top = index - descr->page_size + 1;
1366 LISTBOX_SetTopItem( descr, top, TRUE );
1369 /***********************************************************************
1370 * LISTBOX_SetCaretIndex
1373 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1376 static LRESULT LISTBOX_SetCaretIndex( LB_DESCR *descr, INT index, BOOL fully_visible )
1378 INT oldfocus = descr->focus_item;
1380 TRACE("old focus %d, index %d\n", oldfocus, index);
1382 if (descr->style & LBS_NOSEL) return LB_ERR;
1383 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1384 if (index == oldfocus) return LB_OKAY;
1386 LISTBOX_DrawFocusRect( descr, FALSE );
1387 descr->focus_item = index;
1389 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1390 LISTBOX_DrawFocusRect( descr, TRUE );
1396 /***********************************************************************
1397 * LISTBOX_SelectItemRange
1399 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1401 static LRESULT LISTBOX_SelectItemRange( LB_DESCR *descr, INT first,
1406 /* A few sanity checks */
1408 if (descr->style & LBS_NOSEL) return LB_ERR;
1409 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1411 if (!descr->nb_items) return LB_OKAY;
1413 if (last == -1 || last >= descr->nb_items) last = descr->nb_items - 1;
1414 if (first < 0) first = 0;
1415 if (last < first) return LB_OKAY;
1417 if (on) /* Turn selection on */
1419 for (i = first; i <= last; i++)
1421 if (descr->items[i].selected) continue;
1422 descr->items[i].selected = TRUE;
1423 LISTBOX_InvalidateItemRect(descr, i);
1426 else /* Turn selection off */
1428 for (i = first; i <= last; i++)
1430 if (!descr->items[i].selected) continue;
1431 descr->items[i].selected = FALSE;
1432 LISTBOX_InvalidateItemRect(descr, i);
1438 /***********************************************************************
1439 * LISTBOX_SetSelection
1441 static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index,
1442 BOOL on, BOOL send_notify )
1444 TRACE( "cur_sel=%d index=%d notify=%s\n",
1445 descr->selected_item, index, send_notify ? "YES" : "NO" );
1447 if (descr->style & LBS_NOSEL)
1449 descr->selected_item = index;
1452 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1453 if (descr->style & LBS_MULTIPLESEL)
1455 if (index == -1) /* Select all items */
1456 return LISTBOX_SelectItemRange( descr, 0, descr->nb_items, on );
1457 else /* Only one item */
1458 return LISTBOX_SelectItemRange( descr, index, index, on );
1462 INT oldsel = descr->selected_item;
1463 if (index == oldsel) return LB_OKAY;
1464 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1465 if (index != -1) descr->items[index].selected = TRUE;
1466 if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT );
1467 descr->selected_item = index;
1468 if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT );
1469 if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr,
1470 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1472 if( descr->lphc ) /* set selection change flag for parent combo */
1473 descr->lphc->wState |= CBF_SELCHANGE;
1479 /***********************************************************************
1482 * Change the caret position and extend the selection to the new caret.
1484 static void LISTBOX_MoveCaret( LB_DESCR *descr, INT index, BOOL fully_visible )
1486 TRACE("old focus %d, index %d\n", descr->focus_item, index);
1488 if ((index < 0) || (index >= descr->nb_items))
1491 /* Important, repaint needs to be done in this order if
1492 you want to mimic Windows behavior:
1493 1. Remove the focus and paint the item
1494 2. Remove the selection and paint the item(s)
1495 3. Set the selection and repaint the item(s)
1496 4. Set the focus to 'index' and repaint the item */
1498 /* 1. remove the focus and repaint the item */
1499 LISTBOX_DrawFocusRect( descr, FALSE );
1501 /* 2. then turn off the previous selection */
1502 /* 3. repaint the new selected item */
1503 if (descr->style & LBS_EXTENDEDSEL)
1505 if (descr->anchor_item != -1)
1507 INT first = min( index, descr->anchor_item );
1508 INT last = max( index, descr->anchor_item );
1510 LISTBOX_SelectItemRange( descr, 0, first - 1, FALSE );
1511 LISTBOX_SelectItemRange( descr, last + 1, -1, FALSE );
1512 LISTBOX_SelectItemRange( descr, first, last, TRUE );
1515 else if (!(descr->style & LBS_MULTIPLESEL))
1517 /* Set selection to new caret item */
1518 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
1521 /* 4. repaint the new item with the focus */
1522 descr->focus_item = index;
1523 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1524 LISTBOX_DrawFocusRect( descr, TRUE );
1528 /***********************************************************************
1529 * LISTBOX_InsertItem
1531 static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index,
1532 LPWSTR str, ULONG_PTR data )
1536 INT oldfocus = descr->focus_item;
1538 if (index == -1) index = descr->nb_items;
1539 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1540 if (!descr->items) max_items = 0;
1541 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1542 if (descr->nb_items == max_items)
1544 /* We need to grow the array */
1545 max_items += LB_ARRAY_GRANULARITY;
1547 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1548 max_items * sizeof(LB_ITEMDATA) );
1550 item = HeapAlloc( GetProcessHeap(), 0,
1551 max_items * sizeof(LB_ITEMDATA) );
1554 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1557 descr->items = item;
1560 /* Insert the item structure */
1562 item = &descr->items[index];
1563 if (index < descr->nb_items)
1564 RtlMoveMemory( item + 1, item,
1565 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1569 item->selected = FALSE;
1572 /* Get item height */
1574 if (descr->style & LBS_OWNERDRAWVARIABLE)
1576 MEASUREITEMSTRUCT mis;
1577 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1579 mis.CtlType = ODT_LISTBOX;
1582 mis.itemData = descr->items[index].data;
1583 mis.itemHeight = descr->item_height;
1584 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1585 item->height = mis.itemHeight ? mis.itemHeight : 1;
1586 TRACE("[%p]: measure item %d (%s) = %d\n",
1587 descr->self, index, str ? debugstr_w(str) : "", item->height );
1590 /* Repaint the items */
1592 LISTBOX_UpdateScroll( descr );
1593 LISTBOX_InvalidateItems( descr, index );
1595 /* Move selection and focused item */
1596 /* If listbox was empty, set focus to the first item */
1597 if (descr->nb_items == 1)
1598 LISTBOX_SetCaretIndex( descr, 0, FALSE );
1599 /* single select don't change selection index in win31 */
1600 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1602 descr->selected_item++;
1603 LISTBOX_SetSelection( descr, descr->selected_item-1, TRUE, FALSE );
1607 if (index <= descr->selected_item)
1609 descr->selected_item++;
1610 descr->focus_item = oldfocus; /* focus not changed */
1617 /***********************************************************************
1618 * LISTBOX_InsertString
1620 static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index, LPCWSTR str )
1622 LPWSTR new_str = NULL;
1626 if (HAS_STRINGS(descr))
1628 static const WCHAR empty_stringW[] = { 0 };
1629 if (!str) str = empty_stringW;
1630 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1632 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1635 strcpyW(new_str, str);
1637 else data = (ULONG_PTR)str;
1639 if (index == -1) index = descr->nb_items;
1640 if ((ret = LISTBOX_InsertItem( descr, index, new_str, data )) != 0)
1642 HeapFree( GetProcessHeap(), 0, new_str );
1646 TRACE("[%p]: added item %d %s\n",
1647 descr->self, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1652 /***********************************************************************
1653 * LISTBOX_DeleteItem
1655 * Delete the content of an item. 'index' must be a valid index.
1657 static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index )
1659 /* save the item data before it gets freed by LB_RESETCONTENT */
1660 ULONG_PTR item_data = descr->items[index].data;
1661 LPWSTR item_str = descr->items[index].str;
1663 if (!descr->nb_items)
1664 SendMessageW( descr->self, LB_RESETCONTENT, 0, 0 );
1666 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1667 * while Win95 sends it for all items with user data.
1668 * It's probably better to send it too often than not
1669 * often enough, so this is what we do here.
1671 if (IS_OWNERDRAW(descr) || item_data)
1673 DELETEITEMSTRUCT dis;
1674 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1676 dis.CtlType = ODT_LISTBOX;
1679 dis.hwndItem = descr->self;
1680 dis.itemData = item_data;
1681 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1683 if (HAS_STRINGS(descr))
1684 HeapFree( GetProcessHeap(), 0, item_str );
1688 /***********************************************************************
1689 * LISTBOX_RemoveItem
1691 * Remove an item from the listbox and delete its content.
1693 static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index )
1698 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1700 /* We need to invalidate the original rect instead of the updated one. */
1701 LISTBOX_InvalidateItems( descr, index );
1704 LISTBOX_DeleteItem( descr, index );
1706 if (!descr->nb_items) return LB_OKAY;
1708 /* Remove the item */
1710 item = &descr->items[index];
1711 if (index < descr->nb_items)
1712 RtlMoveMemory( item, item + 1,
1713 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1714 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1716 /* Shrink the item array if possible */
1718 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1719 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1721 max_items -= LB_ARRAY_GRANULARITY;
1722 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1723 max_items * sizeof(LB_ITEMDATA) );
1724 if (item) descr->items = item;
1726 /* Repaint the items */
1728 LISTBOX_UpdateScroll( descr );
1729 /* if we removed the scrollbar, reset the top of the list
1730 (correct for owner-drawn ???) */
1731 if (descr->nb_items == descr->page_size)
1732 LISTBOX_SetTopItem( descr, 0, TRUE );
1734 /* Move selection and focused item */
1735 if (!IS_MULTISELECT(descr))
1737 if (index == descr->selected_item)
1738 descr->selected_item = -1;
1739 else if (index < descr->selected_item)
1741 descr->selected_item--;
1742 if (ISWIN31) /* win 31 do not change the selected item number */
1743 LISTBOX_SetSelection( descr, descr->selected_item + 1, TRUE, FALSE);
1747 if (descr->focus_item >= descr->nb_items)
1749 descr->focus_item = descr->nb_items - 1;
1750 if (descr->focus_item < 0) descr->focus_item = 0;
1756 /***********************************************************************
1757 * LISTBOX_ResetContent
1759 static void LISTBOX_ResetContent( LB_DESCR *descr )
1763 for(i = descr->nb_items - 1; i>=0; i--) LISTBOX_DeleteItem( descr, i);
1764 HeapFree( GetProcessHeap(), 0, descr->items );
1765 descr->nb_items = 0;
1766 descr->top_item = 0;
1767 descr->selected_item = -1;
1768 descr->focus_item = 0;
1769 descr->anchor_item = -1;
1770 descr->items = NULL;
1774 /***********************************************************************
1777 static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count )
1781 if (HAS_STRINGS(descr))
1783 SetLastError(ERROR_SETCOUNT_ON_BAD_LB);
1787 /* FIXME: this is far from optimal... */
1788 if (count > descr->nb_items)
1790 while (count > descr->nb_items)
1791 if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0)
1794 else if (count < descr->nb_items)
1796 while (count < descr->nb_items)
1797 if ((ret = LISTBOX_RemoveItem( descr, (descr->nb_items - 1) )) < 0)
1804 /***********************************************************************
1807 static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib,
1808 LPCWSTR filespec, BOOL long_names )
1811 LRESULT ret = LB_OKAY;
1812 WIN32_FIND_DATAW entry;
1814 LRESULT maxinsert = LB_ERR;
1816 /* don't scan directory if we just want drives exclusively */
1817 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1818 /* scan directory */
1819 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1821 int le = GetLastError();
1822 if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR;
1829 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1831 static const WCHAR bracketW[] = { ']',0 };
1832 static const WCHAR dotW[] = { '.',0 };
1833 if (!(attrib & DDL_DIRECTORY) ||
1834 !strcmpW( entry.cFileName, dotW )) continue;
1836 if (!long_names && entry.cAlternateFileName[0])
1837 strcpyW( buffer + 1, entry.cAlternateFileName );
1839 strcpyW( buffer + 1, entry.cFileName );
1840 strcatW(buffer, bracketW);
1842 else /* not a directory */
1844 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1845 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1847 if ((attrib & DDL_EXCLUSIVE) &&
1848 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1851 if (!long_names && entry.cAlternateFileName[0])
1852 strcpyW( buffer, entry.cAlternateFileName );
1854 strcpyW( buffer, entry.cFileName );
1856 if (!long_names) CharLowerW( buffer );
1857 pos = LISTBOX_FindFileStrPos( descr, buffer );
1858 if ((ret = LISTBOX_InsertString( descr, pos, buffer )) < 0)
1860 if (ret <= maxinsert) maxinsert++; else maxinsert = ret;
1861 } while (FindNextFileW( handle, &entry ));
1862 FindClose( handle );
1870 if (attrib & DDL_DRIVES)
1872 WCHAR buffer[] = {'[','-','a','-',']',0};
1873 WCHAR root[] = {'A',':','\\',0};
1875 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1877 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1878 if ((ret = LISTBOX_InsertString( descr, -1, buffer )) < 0)
1887 /***********************************************************************
1888 * LISTBOX_HandleVScroll
1890 static LRESULT LISTBOX_HandleVScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1894 if (descr->style & LBS_MULTICOLUMN) return 0;
1898 LISTBOX_SetTopItem( descr, descr->top_item - 1, TRUE );
1901 LISTBOX_SetTopItem( descr, descr->top_item + 1, TRUE );
1904 LISTBOX_SetTopItem( descr, descr->top_item -
1905 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1908 LISTBOX_SetTopItem( descr, descr->top_item +
1909 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1911 case SB_THUMBPOSITION:
1912 LISTBOX_SetTopItem( descr, pos, TRUE );
1915 info.cbSize = sizeof(info);
1916 info.fMask = SIF_TRACKPOS;
1917 GetScrollInfo( descr->self, SB_VERT, &info );
1918 LISTBOX_SetTopItem( descr, info.nTrackPos, TRUE );
1921 LISTBOX_SetTopItem( descr, 0, TRUE );
1924 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1931 /***********************************************************************
1932 * LISTBOX_HandleHScroll
1934 static LRESULT LISTBOX_HandleHScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1939 if (descr->style & LBS_MULTICOLUMN)
1944 LISTBOX_SetTopItem( descr, descr->top_item-descr->page_size,
1948 LISTBOX_SetTopItem( descr, descr->top_item+descr->page_size,
1952 page = descr->width / descr->column_width;
1953 if (page < 1) page = 1;
1954 LISTBOX_SetTopItem( descr,
1955 descr->top_item - page * descr->page_size, TRUE );
1958 page = descr->width / descr->column_width;
1959 if (page < 1) page = 1;
1960 LISTBOX_SetTopItem( descr,
1961 descr->top_item + page * descr->page_size, TRUE );
1963 case SB_THUMBPOSITION:
1964 LISTBOX_SetTopItem( descr, pos*descr->page_size, TRUE );
1967 info.cbSize = sizeof(info);
1968 info.fMask = SIF_TRACKPOS;
1969 GetScrollInfo( descr->self, SB_VERT, &info );
1970 LISTBOX_SetTopItem( descr, info.nTrackPos*descr->page_size,
1974 LISTBOX_SetTopItem( descr, 0, TRUE );
1977 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1981 else if (descr->horz_extent)
1986 LISTBOX_SetHorizontalPos( descr, descr->horz_pos - 1 );
1989 LISTBOX_SetHorizontalPos( descr, descr->horz_pos + 1 );
1992 LISTBOX_SetHorizontalPos( descr,
1993 descr->horz_pos - descr->width );
1996 LISTBOX_SetHorizontalPos( descr,
1997 descr->horz_pos + descr->width );
1999 case SB_THUMBPOSITION:
2000 LISTBOX_SetHorizontalPos( descr, pos );
2003 info.cbSize = sizeof(info);
2004 info.fMask = SIF_TRACKPOS;
2005 GetScrollInfo( descr->self, SB_HORZ, &info );
2006 LISTBOX_SetHorizontalPos( descr, info.nTrackPos );
2009 LISTBOX_SetHorizontalPos( descr, 0 );
2012 LISTBOX_SetHorizontalPos( descr,
2013 descr->horz_extent - descr->width );
2020 static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta )
2022 short gcWheelDelta = 0;
2023 UINT pulScrollLines = 3;
2025 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
2027 gcWheelDelta -= delta;
2029 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
2031 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
2032 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
2033 LISTBOX_SetTopItem( descr, descr->top_item + cLineScroll, TRUE );
2038 /***********************************************************************
2039 * LISTBOX_HandleLButtonDown
2041 static LRESULT LISTBOX_HandleLButtonDown( LB_DESCR *descr, DWORD keys, INT x, INT y )
2043 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
2045 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2046 descr->self, x, y, index, descr->focus_item);
2048 if (!descr->caret_on && (descr->in_focus)) return 0;
2050 if (!descr->in_focus)
2052 if( !descr->lphc ) SetFocus( descr->self );
2053 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
2056 if (index == -1) return 0;
2060 if (descr->style & LBS_NOTIFY )
2061 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
2062 MAKELPARAM( x, y ) );
2065 descr->captured = TRUE;
2066 SetCapture( descr->self );
2068 if (descr->style & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL))
2070 /* we should perhaps make sure that all items are deselected
2071 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2072 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2073 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2076 if (!(keys & MK_SHIFT)) descr->anchor_item = index;
2077 if (keys & MK_CONTROL)
2079 LISTBOX_SetCaretIndex( descr, index, FALSE );
2080 LISTBOX_SetSelection( descr, index,
2081 !descr->items[index].selected,
2082 (descr->style & LBS_NOTIFY) != 0);
2086 LISTBOX_MoveCaret( descr, index, FALSE );
2088 if (descr->style & LBS_EXTENDEDSEL)
2090 LISTBOX_SetSelection( descr, index,
2091 descr->items[index].selected,
2092 (descr->style & LBS_NOTIFY) != 0 );
2096 LISTBOX_SetSelection( descr, index,
2097 !descr->items[index].selected,
2098 (descr->style & LBS_NOTIFY) != 0 );
2104 descr->anchor_item = index;
2105 LISTBOX_MoveCaret( descr, index, FALSE );
2106 LISTBOX_SetSelection( descr, index,
2107 TRUE, (descr->style & LBS_NOTIFY) != 0 );
2112 if (GetWindowLongW( descr->self, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
2119 if (DragDetect( descr->self, pt ))
2120 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
2127 /*************************************************************************
2128 * LISTBOX_HandleLButtonDownCombo [Internal]
2130 * Process LButtonDown message for the ComboListBox
2133 * pWnd [I] The windows internal structure
2134 * pDescr [I] The ListBox internal structure
2135 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2136 * x [I] X Mouse Coordinate
2137 * y [I] Y Mouse Coordinate
2140 * 0 since we are processing the WM_LBUTTONDOWN Message
2143 * This function is only to be used when a ListBox is a ComboListBox
2146 static LRESULT LISTBOX_HandleLButtonDownCombo( LB_DESCR *descr, UINT msg, DWORD keys, INT x, INT y)
2148 RECT clientRect, screenRect;
2154 GetClientRect(descr->self, &clientRect);
2156 if(PtInRect(&clientRect, mousePos))
2158 /* MousePos is in client, resume normal processing */
2159 if (msg == WM_LBUTTONDOWN)
2161 descr->lphc->droppedIndex = descr->nb_items ? descr->selected_item : -1;
2162 return LISTBOX_HandleLButtonDown( descr, keys, x, y);
2164 else if (descr->style & LBS_NOTIFY)
2165 SEND_NOTIFICATION( descr, LBN_DBLCLK );
2169 POINT screenMousePos;
2170 HWND hWndOldCapture;
2172 /* Check the Non-Client Area */
2173 screenMousePos = mousePos;
2174 hWndOldCapture = GetCapture();
2176 GetWindowRect(descr->self, &screenRect);
2177 ClientToScreen(descr->self, &screenMousePos);
2179 if(!PtInRect(&screenRect, screenMousePos))
2181 LISTBOX_SetCaretIndex( descr, descr->lphc->droppedIndex, FALSE );
2182 LISTBOX_SetSelection( descr, descr->lphc->droppedIndex, FALSE, FALSE );
2183 COMBO_FlipListbox( descr->lphc, FALSE, FALSE );
2187 /* Check to see the NC is a scrollbar */
2189 LONG style = GetWindowLongW( descr->self, GWL_STYLE );
2190 /* Check Vertical scroll bar */
2191 if (style & WS_VSCROLL)
2193 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2194 if (PtInRect( &clientRect, mousePos ))
2195 nHitTestType = HTVSCROLL;
2197 /* Check horizontal scroll bar */
2198 if (style & WS_HSCROLL)
2200 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2201 if (PtInRect( &clientRect, mousePos ))
2202 nHitTestType = HTHSCROLL;
2204 /* Windows sends this message when a scrollbar is clicked
2207 if(nHitTestType != 0)
2209 SendMessageW(descr->self, WM_NCLBUTTONDOWN, nHitTestType,
2210 MAKELONG(screenMousePos.x, screenMousePos.y));
2212 /* Resume the Capture after scrolling is complete
2214 if(hWndOldCapture != 0)
2215 SetCapture(hWndOldCapture);
2221 /***********************************************************************
2222 * LISTBOX_HandleLButtonUp
2224 static LRESULT LISTBOX_HandleLButtonUp( LB_DESCR *descr )
2226 if (LISTBOX_Timer != LB_TIMER_NONE)
2227 KillSystemTimer( descr->self, LB_TIMER_ID );
2228 LISTBOX_Timer = LB_TIMER_NONE;
2229 if (descr->captured)
2231 descr->captured = FALSE;
2232 if (GetCapture() == descr->self) ReleaseCapture();
2233 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2234 SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2240 /***********************************************************************
2241 * LISTBOX_HandleTimer
2243 * Handle scrolling upon a timer event.
2244 * Return TRUE if scrolling should continue.
2246 static LRESULT LISTBOX_HandleTimer( LB_DESCR *descr, INT index, TIMER_DIRECTION dir )
2251 if (descr->top_item) index = descr->top_item - 1;
2255 if (descr->top_item) index -= descr->page_size;
2258 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2259 if (index == descr->focus_item) index++;
2260 if (index >= descr->nb_items) index = descr->nb_items - 1;
2262 case LB_TIMER_RIGHT:
2263 if (index + descr->page_size < descr->nb_items)
2264 index += descr->page_size;
2269 if (index == descr->focus_item) return FALSE;
2270 LISTBOX_MoveCaret( descr, index, FALSE );
2275 /***********************************************************************
2276 * LISTBOX_HandleSystemTimer
2278 * WM_SYSTIMER handler.
2280 static LRESULT LISTBOX_HandleSystemTimer( LB_DESCR *descr )
2282 if (!LISTBOX_HandleTimer( descr, descr->focus_item, LISTBOX_Timer ))
2284 KillSystemTimer( descr->self, LB_TIMER_ID );
2285 LISTBOX_Timer = LB_TIMER_NONE;
2291 /***********************************************************************
2292 * LISTBOX_HandleMouseMove
2294 * WM_MOUSEMOVE handler.
2296 static void LISTBOX_HandleMouseMove( LB_DESCR *descr,
2300 TIMER_DIRECTION dir = LB_TIMER_NONE;
2302 if (!descr->captured) return;
2304 if (descr->style & LBS_MULTICOLUMN)
2307 else if (y >= descr->item_height * descr->page_size)
2308 y = descr->item_height * descr->page_size - 1;
2312 dir = LB_TIMER_LEFT;
2315 else if (x >= descr->width)
2317 dir = LB_TIMER_RIGHT;
2318 x = descr->width - 1;
2323 if (y < 0) dir = LB_TIMER_UP; /* above */
2324 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2327 index = LISTBOX_GetItemFromPoint( descr, x, y );
2328 if (index == -1) index = descr->focus_item;
2329 if (!LISTBOX_HandleTimer( descr, index, dir )) dir = LB_TIMER_NONE;
2331 /* Start/stop the system timer */
2333 if (dir != LB_TIMER_NONE)
2334 SetSystemTimer( descr->self, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2335 else if (LISTBOX_Timer != LB_TIMER_NONE)
2336 KillSystemTimer( descr->self, LB_TIMER_ID );
2337 LISTBOX_Timer = dir;
2341 /***********************************************************************
2342 * LISTBOX_HandleKeyDown
2344 static LRESULT LISTBOX_HandleKeyDown( LB_DESCR *descr, DWORD key )
2347 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2348 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2349 bForceSelection = FALSE; /* only for single select list */
2351 if (descr->style & LBS_WANTKEYBOARDINPUT)
2353 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2354 MAKEWPARAM(LOWORD(key), descr->focus_item),
2355 (LPARAM)descr->self );
2356 if (caret == -2) return 0;
2358 if (caret == -1) switch(key)
2361 if (descr->style & LBS_MULTICOLUMN)
2363 bForceSelection = FALSE;
2364 if (descr->focus_item >= descr->page_size)
2365 caret = descr->focus_item - descr->page_size;
2370 caret = descr->focus_item - 1;
2371 if (caret < 0) caret = 0;
2374 if (descr->style & LBS_MULTICOLUMN)
2376 bForceSelection = FALSE;
2377 if (descr->focus_item + descr->page_size < descr->nb_items)
2378 caret = descr->focus_item + descr->page_size;
2383 caret = descr->focus_item + 1;
2384 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2388 if (descr->style & LBS_MULTICOLUMN)
2390 INT page = descr->width / descr->column_width;
2391 if (page < 1) page = 1;
2392 caret = descr->focus_item - (page * descr->page_size) + 1;
2394 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2395 if (caret < 0) caret = 0;
2398 if (descr->style & LBS_MULTICOLUMN)
2400 INT page = descr->width / descr->column_width;
2401 if (page < 1) page = 1;
2402 caret = descr->focus_item + (page * descr->page_size) - 1;
2404 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2405 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2411 caret = descr->nb_items - 1;
2414 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2415 else if (descr->style & LBS_MULTIPLESEL)
2417 LISTBOX_SetSelection( descr, descr->focus_item,
2418 !descr->items[descr->focus_item].selected,
2419 (descr->style & LBS_NOTIFY) != 0 );
2423 bForceSelection = FALSE;
2425 if (bForceSelection) /* focused item is used instead of key */
2426 caret = descr->focus_item;
2429 if (((descr->style & LBS_EXTENDEDSEL) &&
2430 !(GetKeyState( VK_SHIFT ) & 0x8000)) ||
2431 !IS_MULTISELECT(descr))
2432 descr->anchor_item = caret;
2433 LISTBOX_MoveCaret( descr, caret, TRUE );
2435 if (descr->style & LBS_MULTIPLESEL)
2436 descr->selected_item = caret;
2438 LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2439 if (descr->style & LBS_NOTIFY)
2441 if (descr->lphc && IsWindowVisible( descr->self ))
2443 /* make sure that combo parent doesn't hide us */
2444 descr->lphc->wState |= CBF_NOROLLUP;
2446 if (descr->nb_items) SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2453 /***********************************************************************
2454 * LISTBOX_HandleChar
2456 static LRESULT LISTBOX_HandleChar( LB_DESCR *descr, WCHAR charW )
2464 if (descr->style & LBS_WANTKEYBOARDINPUT)
2466 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2467 MAKEWPARAM(charW, descr->focus_item),
2468 (LPARAM)descr->self );
2469 if (caret == -2) return 0;
2472 caret = LISTBOX_FindString( descr, descr->focus_item, str, FALSE);
2475 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2476 LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2477 LISTBOX_MoveCaret( descr, caret, TRUE );
2478 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2479 SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2485 /***********************************************************************
2488 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2491 MEASUREITEMSTRUCT mis;
2494 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2497 GetClientRect( hwnd, &rect );
2499 descr->owner = GetParent( descr->self );
2500 descr->style = GetWindowLongW( descr->self, GWL_STYLE );
2501 descr->width = rect.right - rect.left;
2502 descr->height = rect.bottom - rect.top;
2503 descr->items = NULL;
2504 descr->nb_items = 0;
2505 descr->top_item = 0;
2506 descr->selected_item = -1;
2507 descr->focus_item = 0;
2508 descr->anchor_item = -1;
2509 descr->item_height = 1;
2510 descr->page_size = 1;
2511 descr->column_width = 150;
2512 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0;
2513 descr->horz_pos = 0;
2516 descr->caret_on = lphc ? FALSE : TRUE;
2517 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2518 descr->in_focus = FALSE;
2519 descr->captured = FALSE;
2521 descr->locale = GetUserDefaultLCID();
2524 if (is_old_app(descr) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2526 /* Win95 document "List Box Differences" from MSDN:
2527 If a list box in a version 3.x application has either the
2528 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2529 horizontal and vertical scroll bars.
2531 descr->style |= WS_VSCROLL | WS_HSCROLL;
2536 TRACE("[%p]: resetting owner %p -> %p\n", descr->self, descr->owner, lphc->self );
2537 descr->owner = lphc->self;
2540 SetWindowLongPtrW( descr->self, 0, (LONG_PTR)descr );
2542 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2544 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2545 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2546 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2547 descr->item_height = LISTBOX_SetFont( descr, 0 );
2549 if (descr->style & LBS_OWNERDRAWFIXED)
2551 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2553 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2554 descr->item_height = lphc->fixedOwnerDrawHeight;
2558 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
2559 mis.CtlType = ODT_LISTBOX;
2564 mis.itemHeight = descr->item_height;
2565 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2566 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2570 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2575 /***********************************************************************
2578 static BOOL LISTBOX_Destroy( LB_DESCR *descr )
2580 LISTBOX_ResetContent( descr );
2581 SetWindowLongPtrW( descr->self, 0, 0 );
2582 HeapFree( GetProcessHeap(), 0, descr );
2587 /***********************************************************************
2588 * ListBoxWndProc_common
2590 LRESULT ListBoxWndProc_common( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, BOOL unicode )
2592 LB_DESCR *descr = (LB_DESCR *)GetWindowLongPtrW( hwnd, 0 );
2593 LPHEADCOMBO lphc = 0;
2598 if (!IsWindow(hwnd)) return 0;
2600 if (msg == WM_CREATE)
2602 CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam;
2603 if (lpcs->style & LBS_COMBOBOX) lphc = lpcs->lpCreateParams;
2604 if (!LISTBOX_Create( hwnd, lphc )) return -1;
2605 TRACE("creating hwnd %p descr %p\n", hwnd, (void *)GetWindowLongPtrW( hwnd, 0 ) );
2608 /* Ignore all other messages before we get a WM_CREATE */
2609 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2610 DefWindowProcA( hwnd, msg, wParam, lParam );
2612 if (descr->style & LBS_COMBOBOX) lphc = descr->lphc;
2614 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2615 descr->self, SPY_GetMsgName(msg, descr->self), wParam, lParam );
2619 case LB_RESETCONTENT:
2620 LISTBOX_ResetContent( descr );
2621 LISTBOX_UpdateScroll( descr );
2622 InvalidateRect( descr->self, NULL, TRUE );
2629 if(unicode || !HAS_STRINGS(descr))
2630 textW = (LPWSTR)lParam;
2633 LPSTR textA = (LPSTR)lParam;
2634 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2635 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2636 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2640 wParam = LISTBOX_FindStringPos( descr, textW, FALSE );
2641 ret = LISTBOX_InsertString( descr, wParam, textW );
2642 if (!unicode && HAS_STRINGS(descr))
2643 HeapFree(GetProcessHeap(), 0, textW);
2647 case LB_INSERTSTRING:
2651 if(unicode || !HAS_STRINGS(descr))
2652 textW = (LPWSTR)lParam;
2655 LPSTR textA = (LPSTR)lParam;
2656 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2657 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2658 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2662 ret = LISTBOX_InsertString( descr, wParam, textW );
2663 if(!unicode && HAS_STRINGS(descr))
2664 HeapFree(GetProcessHeap(), 0, textW);
2672 if(unicode || !HAS_STRINGS(descr))
2673 textW = (LPWSTR)lParam;
2676 LPSTR textA = (LPSTR)lParam;
2677 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2678 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2679 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2683 wParam = LISTBOX_FindFileStrPos( descr, textW );
2684 ret = LISTBOX_InsertString( descr, wParam, textW );
2685 if(!unicode && HAS_STRINGS(descr))
2686 HeapFree(GetProcessHeap(), 0, textW);
2690 case LB_DELETESTRING:
2691 if (LISTBOX_RemoveItem( descr, wParam) != LB_ERR)
2692 return descr->nb_items;
2695 SetLastError(ERROR_INVALID_INDEX);
2699 case LB_GETITEMDATA:
2700 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2702 SetLastError(ERROR_INVALID_INDEX);
2705 return descr->items[wParam].data;
2707 case LB_SETITEMDATA:
2708 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2710 SetLastError(ERROR_INVALID_INDEX);
2713 descr->items[wParam].data = lParam;
2714 /* undocumented: returns TRUE, not LB_OKAY (0) */
2718 return descr->nb_items;
2721 return LISTBOX_GetText( descr, wParam, (LPWSTR)lParam, unicode );
2724 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2726 SetLastError(ERROR_INVALID_INDEX);
2729 if (!HAS_STRINGS(descr)) return sizeof(DWORD);
2730 if (unicode) return strlenW( descr->items[wParam].str );
2731 return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
2732 strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
2735 if (descr->nb_items == 0)
2737 if (!IS_MULTISELECT(descr))
2738 return descr->selected_item;
2739 if (descr->selected_item != -1)
2740 return descr->selected_item;
2741 return descr->focus_item;
2742 /* otherwise, if the user tries to move the selection with the */
2743 /* arrow keys, we will give the application something to choke on */
2744 case LB_GETTOPINDEX:
2745 return descr->top_item;
2747 case LB_GETITEMHEIGHT:
2748 return LISTBOX_GetItemHeight( descr, wParam );
2750 case LB_SETITEMHEIGHT:
2751 return LISTBOX_SetItemHeight( descr, wParam, lParam, TRUE );
2753 case LB_ITEMFROMPOINT:
2760 /* The hiword of the return value is not a client area
2761 hittest as suggested by MSDN, but rather a hittest on
2762 the returned listbox item. */
2764 if(descr->nb_items == 0)
2765 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2767 pt.x = (short)LOWORD(lParam);
2768 pt.y = (short)HIWORD(lParam);
2770 SetRect(&rect, 0, 0, descr->width, descr->height);
2772 if(!PtInRect(&rect, pt))
2774 pt.x = min(pt.x, rect.right - 1);
2775 pt.x = max(pt.x, 0);
2776 pt.y = min(pt.y, rect.bottom - 1);
2777 pt.y = max(pt.y, 0);
2781 index = LISTBOX_GetItemFromPoint(descr, pt.x, pt.y);
2785 index = descr->nb_items - 1;
2788 return MAKELONG(index, hit ? 0 : 1);
2791 case LB_SETCARETINDEX:
2792 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2793 if (LISTBOX_SetCaretIndex( descr, wParam, !lParam ) == LB_ERR)
2800 case LB_GETCARETINDEX:
2801 return descr->focus_item;
2803 case LB_SETTOPINDEX:
2804 return LISTBOX_SetTopItem( descr, wParam, TRUE );
2806 case LB_SETCOLUMNWIDTH:
2807 return LISTBOX_SetColumnWidth( descr, wParam );
2809 case LB_GETITEMRECT:
2810 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2816 if(unicode || !HAS_STRINGS(descr))
2817 textW = (LPWSTR)lParam;
2820 LPSTR textA = (LPSTR)lParam;
2821 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2822 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2823 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2825 ret = LISTBOX_FindString( descr, wParam, textW, FALSE );
2826 if(!unicode && HAS_STRINGS(descr))
2827 HeapFree(GetProcessHeap(), 0, textW);
2831 case LB_FINDSTRINGEXACT:
2835 if(unicode || !HAS_STRINGS(descr))
2836 textW = (LPWSTR)lParam;
2839 LPSTR textA = (LPSTR)lParam;
2840 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2841 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2842 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2844 ret = LISTBOX_FindString( descr, wParam, textW, TRUE );
2845 if(!unicode && HAS_STRINGS(descr))
2846 HeapFree(GetProcessHeap(), 0, textW);
2850 case LB_SELECTSTRING:
2855 if(HAS_STRINGS(descr))
2856 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2857 debugstr_a((LPSTR)lParam));
2858 if(unicode || !HAS_STRINGS(descr))
2859 textW = (LPWSTR)lParam;
2862 LPSTR textA = (LPSTR)lParam;
2863 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2864 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2865 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2867 index = LISTBOX_FindString( descr, wParam, textW, FALSE );
2868 if(!unicode && HAS_STRINGS(descr))
2869 HeapFree(GetProcessHeap(), 0, textW);
2870 if (index != LB_ERR)
2872 LISTBOX_MoveCaret( descr, index, TRUE );
2873 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
2879 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2881 return descr->items[wParam].selected;
2884 return LISTBOX_SetSelection( descr, lParam, wParam, FALSE );
2887 if (IS_MULTISELECT(descr)) return LB_ERR;
2888 LISTBOX_SetCaretIndex( descr, wParam, FALSE );
2889 ret = LISTBOX_SetSelection( descr, wParam, TRUE, FALSE );
2890 if (ret != LB_ERR) ret = descr->selected_item;
2893 case LB_GETSELCOUNT:
2894 return LISTBOX_GetSelCount( descr );
2896 case LB_GETSELITEMS:
2897 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2899 case LB_SELITEMRANGE:
2900 if (LOWORD(lParam) <= HIWORD(lParam))
2901 return LISTBOX_SelectItemRange( descr, LOWORD(lParam),
2902 HIWORD(lParam), wParam );
2904 return LISTBOX_SelectItemRange( descr, HIWORD(lParam),
2905 LOWORD(lParam), wParam );
2907 case LB_SELITEMRANGEEX:
2908 if ((INT)lParam >= (INT)wParam)
2909 return LISTBOX_SelectItemRange( descr, wParam, lParam, TRUE );
2911 return LISTBOX_SelectItemRange( descr, lParam, wParam, FALSE);
2913 case LB_GETHORIZONTALEXTENT:
2914 return descr->horz_extent;
2916 case LB_SETHORIZONTALEXTENT:
2917 return LISTBOX_SetHorizontalExtent( descr, wParam );
2919 case LB_GETANCHORINDEX:
2920 return descr->anchor_item;
2922 case LB_SETANCHORINDEX:
2923 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2925 SetLastError(ERROR_INVALID_INDEX);
2928 descr->anchor_item = (INT)wParam;
2936 textW = (LPWSTR)lParam;
2939 LPSTR textA = (LPSTR)lParam;
2940 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2941 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2942 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2944 ret = LISTBOX_Directory( descr, wParam, textW, msg == LB_DIR );
2946 HeapFree(GetProcessHeap(), 0, textW);
2951 return descr->locale;
2956 if (!IsValidLocale((LCID)wParam, LCID_INSTALLED))
2958 ret = descr->locale;
2959 descr->locale = (LCID)wParam;
2963 case LB_INITSTORAGE:
2964 return LISTBOX_InitStorage( descr, wParam );
2967 return LISTBOX_SetCount( descr, (INT)wParam );
2969 case LB_SETTABSTOPS:
2970 return LISTBOX_SetTabStops( descr, wParam, (LPINT)lParam );
2973 if (descr->caret_on)
2975 descr->caret_on = TRUE;
2976 if ((descr->focus_item != -1) && (descr->in_focus))
2977 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2981 if (!descr->caret_on)
2983 descr->caret_on = FALSE;
2984 if ((descr->focus_item != -1) && (descr->in_focus))
2985 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2988 case LB_GETLISTBOXINFO:
2989 FIXME("LB_GETLISTBOXINFO: stub!\n");
2993 return LISTBOX_Destroy( descr );
2996 InvalidateRect( descr->self, NULL, TRUE );
3000 LISTBOX_SetRedraw( descr, wParam != 0 );
3004 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3006 case WM_PRINTCLIENT:
3010 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( descr->self, &ps );
3011 ret = LISTBOX_Paint( descr, hdc );
3012 if( !wParam ) EndPaint( descr->self, &ps );
3016 LISTBOX_UpdateSize( descr );
3019 return (LRESULT)descr->font;
3021 LISTBOX_SetFont( descr, (HFONT)wParam );
3022 if (lParam) InvalidateRect( descr->self, 0, TRUE );
3025 descr->in_focus = TRUE;
3026 descr->caret_on = TRUE;
3027 if (descr->focus_item != -1)
3028 LISTBOX_DrawFocusRect( descr, TRUE );
3029 SEND_NOTIFICATION( descr, LBN_SETFOCUS );
3032 descr->in_focus = FALSE;
3033 if ((descr->focus_item != -1) && descr->caret_on)
3034 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3035 SEND_NOTIFICATION( descr, LBN_KILLFOCUS );
3038 return LISTBOX_HandleHScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3040 return LISTBOX_HandleVScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3042 if (wParam & (MK_SHIFT | MK_CONTROL))
3043 return DefWindowProcW( descr->self, msg, wParam, lParam );
3044 return LISTBOX_HandleMouseWheel( descr, (SHORT)HIWORD(wParam) );
3045 case WM_LBUTTONDOWN:
3047 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3048 (INT16)LOWORD(lParam),
3049 (INT16)HIWORD(lParam) );
3050 return LISTBOX_HandleLButtonDown( descr, wParam,
3051 (INT16)LOWORD(lParam),
3052 (INT16)HIWORD(lParam) );
3053 case WM_LBUTTONDBLCLK:
3055 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3056 (INT16)LOWORD(lParam),
3057 (INT16)HIWORD(lParam) );
3058 if (descr->style & LBS_NOTIFY)
3059 SEND_NOTIFICATION( descr, LBN_DBLCLK );
3062 if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) )
3064 BOOL captured = descr->captured;
3068 mousePos.x = (INT16)LOWORD(lParam);
3069 mousePos.y = (INT16)HIWORD(lParam);
3072 * If we are in a dropdown combobox, we simulate that
3073 * the mouse is captured to show the tracking of the item.
3075 if (GetClientRect(descr->self, &clientRect) && PtInRect( &clientRect, mousePos ))
3076 descr->captured = TRUE;
3078 LISTBOX_HandleMouseMove( descr, mousePos.x, mousePos.y);
3080 descr->captured = captured;
3082 else if (GetCapture() == descr->self)
3084 LISTBOX_HandleMouseMove( descr, (INT16)LOWORD(lParam),
3085 (INT16)HIWORD(lParam) );
3095 * If the mouse button "up" is not in the listbox,
3096 * we make sure there is no selection by re-selecting the
3097 * item that was selected when the listbox was made visible.
3099 mousePos.x = (INT16)LOWORD(lParam);
3100 mousePos.y = (INT16)HIWORD(lParam);
3102 GetClientRect(descr->self, &clientRect);
3105 * When the user clicks outside the combobox and the focus
3106 * is lost, the owning combobox will send a fake buttonup with
3107 * 0xFFFFFFF as the mouse location, we must also revert the
3108 * selection to the original selection.
3110 if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) )
3111 LISTBOX_MoveCaret( descr, lphc->droppedIndex, FALSE );
3113 return LISTBOX_HandleLButtonUp( descr );
3115 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3117 /* for some reason Windows makes it possible to
3118 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3120 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3121 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3122 && (wParam == VK_DOWN || wParam == VK_UP)) )
3124 COMBO_FlipListbox( lphc, FALSE, FALSE );
3128 return LISTBOX_HandleKeyDown( descr, wParam );
3133 charW = (WCHAR)wParam;
3136 CHAR charA = (CHAR)wParam;
3137 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
3139 return LISTBOX_HandleChar( descr, charW );
3142 return LISTBOX_HandleSystemTimer( descr );
3144 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
3147 HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
3148 wParam, (LPARAM)descr->self );
3149 TRACE("hbrush = %p\n", hbrush);
3151 hbrush = GetSysColorBrush(COLOR_WINDOW);
3154 GetClientRect(descr->self, &rect);
3155 FillRect((HDC)wParam, &rect, hbrush);
3160 if( lphc ) return 0;
3161 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3162 SendMessageA( descr->owner, msg, wParam, lParam );
3165 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3174 if ((msg >= WM_USER) && (msg < 0xc000))
3175 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3176 hwnd, msg, wParam, lParam );
3179 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3180 DefWindowProcA( hwnd, msg, wParam, lParam );
3183 /***********************************************************************
3186 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3188 return wow_handlers.listbox_proc( hwnd, msg, wParam, lParam, FALSE );
3191 /***********************************************************************
3194 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3196 return wow_handlers.listbox_proc( hwnd, msg, wParam, lParam, TRUE );