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"
46 #include "wine/unicode.h"
47 #include "user_private.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(listbox);
53 /* Items array granularity */
54 #define LB_ARRAY_GRANULARITY 16
56 /* Scrolling timeout in ms */
57 #define LB_SCROLL_TIMEOUT 50
59 /* Listbox system timer id */
62 /* flag listbox changed while setredraw false - internal style */
63 #define LBS_DISPLAYCHANGED 0x80000000
68 LPWSTR str; /* Item text */
69 BOOL selected; /* Is item selected? */
70 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
71 DWORD data; /* User data */
74 /* Listbox structure */
77 HWND self; /* Our own window handle */
78 HWND owner; /* Owner window to send notifications to */
79 UINT style; /* Window style */
80 INT width; /* Window width */
81 INT height; /* Window height */
82 LB_ITEMDATA *items; /* Array of items */
83 INT nb_items; /* Number of items */
84 INT top_item; /* Top visible item */
85 INT selected_item; /* Selected item */
86 INT focus_item; /* Item that has the focus */
87 INT anchor_item; /* Anchor item for extended selection */
88 INT item_height; /* Default item height */
89 INT page_size; /* Items per listbox page */
90 INT column_width; /* Column width for multi-column listboxes */
91 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
92 INT horz_pos; /* Horizontal position */
93 INT nb_tabs; /* Number of tabs in array */
94 INT *tabs; /* Array of tabs */
95 BOOL caret_on; /* Is caret on? */
96 BOOL captured; /* Is mouse captured? */
98 HFONT font; /* Current font */
99 LCID locale; /* Current locale for string comparisons */
100 LPHEADCOMBO lphc; /* ComboLBox */
104 #define IS_OWNERDRAW(descr) \
105 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
107 #define HAS_STRINGS(descr) \
108 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
111 #define IS_MULTISELECT(descr) \
112 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
113 !((descr)->style & LBS_NOSEL))
115 #define SEND_NOTIFICATION(descr,code) \
116 (SendMessageW( (descr)->owner, WM_COMMAND, \
117 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
119 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
121 /* Current timer status */
131 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
133 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
134 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
136 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect );
138 /*********************************************************************
139 * listbox class descriptor
141 const struct builtin_class_descr LISTBOX_builtin_class =
143 "ListBox", /* name */
144 CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
145 ListBoxWndProcA, /* procA */
146 ListBoxWndProcW, /* procW */
147 sizeof(LB_DESCR *), /* extra */
148 IDC_ARROW, /* cursor */
153 /*********************************************************************
154 * combolbox class descriptor
156 const struct builtin_class_descr COMBOLBOX_builtin_class =
158 "ComboLBox", /* 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 inline static 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( 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( 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 );
305 if (index > max) index = max;
306 if (index < 0) index = 0;
307 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
308 if (descr->top_item == index) return LB_OKAY;
309 if (descr->style & LBS_MULTICOLUMN)
311 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
312 if (scroll && (abs(diff) < descr->width))
313 ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
314 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
322 if (descr->style & LBS_OWNERDRAWVARIABLE)
326 if (index > descr->top_item)
328 for (i = index - 1; i >= descr->top_item; i--)
329 diff -= descr->items[i].height;
333 for (i = index; i < descr->top_item; i++)
334 diff += descr->items[i].height;
338 diff = (descr->top_item - index) * descr->item_height;
340 if (abs(diff) < descr->height)
341 ScrollWindowEx( descr->self, 0, diff, NULL, NULL, 0, NULL,
342 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
346 if (!scroll) InvalidateRect( descr->self, NULL, TRUE );
347 descr->top_item = index;
348 LISTBOX_UpdateScroll( descr );
353 /***********************************************************************
356 * Update the page size. Should be called when the size of
357 * the client area or the item height changes.
359 static void LISTBOX_UpdatePage( LB_DESCR *descr )
363 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
365 if (page_size == descr->page_size) return;
366 descr->page_size = page_size;
367 if (descr->style & LBS_MULTICOLUMN)
368 InvalidateRect( descr->self, NULL, TRUE );
369 LISTBOX_SetTopItem( descr, descr->top_item, FALSE );
373 /***********************************************************************
376 * Update the size of the listbox. Should be called when the size of
377 * the client area changes.
379 static void LISTBOX_UpdateSize( LB_DESCR *descr )
383 GetClientRect( descr->self, &rect );
384 descr->width = rect.right - rect.left;
385 descr->height = rect.bottom - rect.top;
386 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
391 GetWindowRect( descr->self, &rect );
392 if(descr->item_height != 0)
393 remaining = descr->height % descr->item_height;
396 if ((descr->height > descr->item_height) && remaining)
398 if (is_old_app(descr))
399 { /* give a margin for error to 16 bits programs - if we need
400 less than the height of the nonclient area, round to the
401 *next* number of items */
402 int ncheight = rect.bottom - rect.top - descr->height;
403 if ((descr->item_height - remaining) <= ncheight)
404 remaining = remaining - descr->item_height;
406 TRACE("[%p]: changing height %d -> %d\n",
407 descr->self, descr->height, descr->height - remaining );
408 SetWindowPos( descr->self, 0, 0, 0, rect.right - rect.left,
409 rect.bottom - rect.top - remaining,
410 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
414 TRACE("[%p]: new size = %d,%d\n", descr->self, descr->width, descr->height );
415 LISTBOX_UpdatePage( descr );
416 LISTBOX_UpdateScroll( descr );
418 /* Invalidate the focused item so it will be repainted correctly */
419 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
421 InvalidateRect( descr->self, &rect, FALSE );
426 /***********************************************************************
427 * LISTBOX_GetItemRect
429 * Get the rectangle enclosing an item, in listbox client coordinates.
430 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
432 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect )
434 /* Index <= 0 is legal even on empty listboxes */
435 if (index && (index >= descr->nb_items)) return -1;
436 SetRect( rect, 0, 0, descr->width, descr->height );
437 if (descr->style & LBS_MULTICOLUMN)
439 INT col = (index / descr->page_size) -
440 (descr->top_item / descr->page_size);
441 rect->left += col * descr->column_width;
442 rect->right = rect->left + descr->column_width;
443 rect->top += (index % descr->page_size) * descr->item_height;
444 rect->bottom = rect->top + descr->item_height;
446 else if (descr->style & LBS_OWNERDRAWVARIABLE)
449 rect->right += descr->horz_pos;
450 if ((index >= 0) && (index < descr->nb_items))
452 if (index < descr->top_item)
454 for (i = descr->top_item-1; i >= index; i--)
455 rect->top -= descr->items[i].height;
459 for (i = descr->top_item; i < index; i++)
460 rect->top += descr->items[i].height;
462 rect->bottom = rect->top + descr->items[index].height;
468 rect->top += (index - descr->top_item) * descr->item_height;
469 rect->bottom = rect->top + descr->item_height;
470 rect->right += descr->horz_pos;
473 return ((rect->left < descr->width) && (rect->right > 0) &&
474 (rect->top < descr->height) && (rect->bottom > 0));
478 /***********************************************************************
479 * LISTBOX_GetItemFromPoint
481 * Return the item nearest from point (x,y) (in client coordinates).
483 static INT LISTBOX_GetItemFromPoint( LB_DESCR *descr, INT x, INT y )
485 INT index = descr->top_item;
487 if (!descr->nb_items) return -1; /* No items */
488 if (descr->style & LBS_OWNERDRAWVARIABLE)
493 while (index < descr->nb_items)
495 if ((pos += descr->items[index].height) > y) break;
504 if ((pos -= descr->items[index].height) <= y) break;
508 else if (descr->style & LBS_MULTICOLUMN)
510 if (y >= descr->item_height * descr->page_size) return -1;
511 if (y >= 0) index += y / descr->item_height;
512 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
513 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
517 index += (y / descr->item_height);
519 if (index < 0) return 0;
520 if (index >= descr->nb_items) return -1;
525 /***********************************************************************
530 static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect,
531 INT index, UINT action, BOOL ignoreFocus )
533 LB_ITEMDATA *item = NULL;
534 if (index < descr->nb_items) item = &descr->items[index];
536 if (IS_OWNERDRAW(descr))
541 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
545 if (action == ODA_FOCUS)
546 DrawFocusRect( hdc, rect );
548 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
552 /* some programs mess with the clipping region when
553 drawing the item, *and* restore the previous region
554 after they are done, so a region has better to exist
555 else everything ends clipped */
556 GetClientRect(descr->self, &r);
557 hrgn = CreateRectRgnIndirect(&r);
558 SelectClipRgn( hdc, hrgn);
559 DeleteObject( hrgn );
561 dis.CtlType = ODT_LISTBOX;
563 dis.hwndItem = descr->self;
564 dis.itemAction = action;
568 if (item && item->selected) dis.itemState |= ODS_SELECTED;
569 if (!ignoreFocus && (descr->focus_item == index) &&
571 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
572 if (!IsWindowEnabled(descr->self)) dis.itemState |= ODS_DISABLED;
573 dis.itemData = item ? item->data : 0;
575 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%ld,%ld-%ld,%ld\n",
576 descr->self, index, item ? debugstr_w(item->str) : "", action,
577 dis.itemState, rect->left, rect->top, rect->right, rect->bottom );
578 SendMessageW(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
582 COLORREF oldText = 0, oldBk = 0;
584 if (action == ODA_FOCUS)
586 DrawFocusRect( hdc, rect );
589 if (item && item->selected)
591 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
592 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
595 TRACE("[%p]: painting %d (%s) action=%02x rect=%ld,%ld-%ld,%ld\n",
596 descr->self, index, item ? debugstr_w(item->str) : "", action,
597 rect->left, rect->top, rect->right, rect->bottom );
599 ExtTextOutW( hdc, rect->left + 1, rect->top,
600 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
601 else if (!(descr->style & LBS_USETABSTOPS))
602 ExtTextOutW( hdc, rect->left + 1, rect->top,
603 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
604 strlenW(item->str), NULL );
607 /* Output empty string to paint background in the full width. */
608 ExtTextOutW( hdc, rect->left + 1, rect->top,
609 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
610 TabbedTextOutW( hdc, rect->left + 1 , rect->top,
611 item->str, strlenW(item->str),
612 descr->nb_tabs, descr->tabs, 0);
614 if (item && item->selected)
616 SetBkColor( hdc, oldBk );
617 SetTextColor( hdc, oldText );
619 if (!ignoreFocus && (descr->focus_item == index) &&
621 (descr->in_focus)) DrawFocusRect( hdc, rect );
626 /***********************************************************************
629 * Change the redraw flag.
631 static void LISTBOX_SetRedraw( LB_DESCR *descr, BOOL on )
635 if (!(descr->style & LBS_NOREDRAW)) return;
636 descr->style &= ~LBS_NOREDRAW;
637 if (descr->style & LBS_DISPLAYCHANGED)
638 { /* page was changed while setredraw false, refresh automatically */
639 InvalidateRect(descr->self, NULL, TRUE);
640 if ((descr->top_item + descr->page_size) > descr->nb_items)
641 { /* reset top of page if less than number of items/page */
642 descr->top_item = descr->nb_items - descr->page_size;
643 if (descr->top_item < 0) descr->top_item = 0;
645 descr->style &= ~LBS_DISPLAYCHANGED;
647 LISTBOX_UpdateScroll( descr );
649 else descr->style |= LBS_NOREDRAW;
653 /***********************************************************************
654 * LISTBOX_RepaintItem
656 * Repaint a single item synchronously.
658 static void LISTBOX_RepaintItem( LB_DESCR *descr, INT index, UINT action )
663 HBRUSH hbrush, oldBrush = 0;
665 /* Do not repaint the item if the item is not visible */
666 if (!IsWindowVisible(descr->self)) return;
667 if (descr->style & LBS_NOREDRAW)
669 descr->style |= LBS_DISPLAYCHANGED;
672 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
673 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
674 if (descr->font) oldFont = SelectObject( hdc, descr->font );
675 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
676 (WPARAM)hdc, (LPARAM)descr->self );
677 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
678 if (!IsWindowEnabled(descr->self))
679 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
680 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
681 LISTBOX_PaintItem( descr, hdc, &rect, index, action, FALSE );
682 if (oldFont) SelectObject( hdc, oldFont );
683 if (oldBrush) SelectObject( hdc, oldBrush );
684 ReleaseDC( descr->self, hdc );
688 /***********************************************************************
689 * LISTBOX_InitStorage
691 static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items )
695 nb_items += LB_ARRAY_GRANULARITY - 1;
696 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
698 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
699 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
700 nb_items * sizeof(LB_ITEMDATA));
703 item = HeapAlloc( GetProcessHeap(), 0,
704 nb_items * sizeof(LB_ITEMDATA));
709 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
717 /***********************************************************************
718 * LISTBOX_SetTabStops
720 static BOOL LISTBOX_SetTabStops( LB_DESCR *descr, INT count, LPINT tabs, BOOL short_ints )
722 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
723 HeapFree( GetProcessHeap(), 0, descr->tabs );
724 if (!(descr->nb_tabs = count))
729 /* FIXME: count = 1 */
730 if (!(descr->tabs = HeapAlloc( GetProcessHeap(), 0,
731 descr->nb_tabs * sizeof(INT) )))
736 LPINT16 p = (LPINT16)tabs;
738 TRACE("[%p]: settabstops ", descr->self );
739 for (i = 0; i < descr->nb_tabs; i++) {
740 descr->tabs[i] = *p++<<1; /* FIXME */
741 TRACE("%hd ", descr->tabs[i]);
745 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
746 /* FIXME: repaint the window? */
751 /***********************************************************************
754 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPWSTR buffer, BOOL unicode )
756 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
757 if (HAS_STRINGS(descr))
761 DWORD len = strlenW(descr->items[index].str);
764 return WideCharToMultiByte( CP_ACP, 0, descr->items[index].str, len,
765 NULL, 0, NULL, NULL );
768 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
772 strcpyW( buffer, descr->items[index].str );
773 return strlenW(buffer);
777 return WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, (LPSTR)buffer, 0x7FFFFFFF, NULL, NULL) - 1;
781 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
782 return sizeof(DWORD);
787 /***********************************************************************
788 * LISTBOX_FindStringPos
790 * Find the nearest string located before a given string in sort order.
791 * If 'exact' is TRUE, return an error if we don't get an exact match.
793 static INT LISTBOX_FindStringPos( LB_DESCR *descr, LPCWSTR str, BOOL exact )
795 INT index, min, max, res = -1;
797 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
799 max = descr->nb_items;
802 index = (min + max) / 2;
803 if (HAS_STRINGS(descr))
804 res = lstrcmpiW( str, descr->items[index].str);
807 COMPAREITEMSTRUCT cis;
808 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
810 cis.CtlType = ODT_LISTBOX;
812 cis.hwndItem = descr->self;
813 /* note that some application (MetaStock) expects the second item
814 * to be in the listbox */
816 cis.itemData1 = (DWORD)str;
818 cis.itemData2 = descr->items[index].data;
819 cis.dwLocaleId = descr->locale;
820 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
822 if (!res) return index;
823 if (res < 0) max = index;
824 else min = index + 1;
826 return exact ? -1 : max;
830 /***********************************************************************
831 * LISTBOX_FindFileStrPos
833 * Find the nearest string located before a given string in directory
834 * sort order (i.e. first files, then directories, then drives).
836 static INT LISTBOX_FindFileStrPos( LB_DESCR *descr, LPCWSTR str )
838 INT min, max, res = -1;
840 if (!HAS_STRINGS(descr))
841 return LISTBOX_FindStringPos( descr, str, FALSE );
843 max = descr->nb_items;
846 INT index = (min + max) / 2;
847 LPCWSTR p = descr->items[index].str;
848 if (*p == '[') /* drive or directory */
850 if (*str != '[') res = -1;
851 else if (p[1] == '-') /* drive */
853 if (str[1] == '-') res = str[2] - p[2];
858 if (str[1] == '-') res = 1;
859 else res = lstrcmpiW( str, p );
864 if (*str == '[') res = 1;
865 else res = lstrcmpiW( str, p );
867 if (!res) return index;
868 if (res < 0) max = index;
869 else min = index + 1;
875 /***********************************************************************
878 * Find the item beginning with a given string.
880 static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exact )
885 if (start >= descr->nb_items) start = -1;
886 item = descr->items + start + 1;
887 if (HAS_STRINGS(descr))
889 if (!str || ! str[0] ) return LB_ERR;
892 for (i = start + 1; i < descr->nb_items; i++, item++)
893 if (!lstrcmpiW( str, item->str )) return i;
894 for (i = 0, item = descr->items; i <= start; i++, item++)
895 if (!lstrcmpiW( str, item->str )) return i;
899 /* Special case for drives and directories: ignore prefix */
900 #define CHECK_DRIVE(item) \
901 if ((item)->str[0] == '[') \
903 if (!strncmpiW( str, (item)->str+1, len )) return i; \
904 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
908 INT len = strlenW(str);
909 for (i = start + 1; i < descr->nb_items; i++, item++)
911 if (!strncmpiW( str, item->str, len )) return i;
914 for (i = 0, item = descr->items; i <= start; i++, item++)
916 if (!strncmpiW( str, item->str, len )) return i;
924 if (exact && (descr->style & LBS_SORT))
925 /* If sorted, use a WM_COMPAREITEM binary search */
926 return LISTBOX_FindStringPos( descr, str, TRUE );
928 /* Otherwise use a linear search */
929 for (i = start + 1; i < descr->nb_items; i++, item++)
930 if (item->data == (DWORD)str) return i;
931 for (i = 0, item = descr->items; i <= start; i++, item++)
932 if (item->data == (DWORD)str) return i;
938 /***********************************************************************
939 * LISTBOX_GetSelCount
941 static LRESULT LISTBOX_GetSelCount( LB_DESCR *descr )
944 LB_ITEMDATA *item = descr->items;
946 if (!(descr->style & LBS_MULTIPLESEL) ||
947 (descr->style & LBS_NOSEL))
949 for (i = count = 0; i < descr->nb_items; i++, item++)
950 if (item->selected) count++;
955 /***********************************************************************
956 * LISTBOX_GetSelItems16
958 static LRESULT LISTBOX_GetSelItems16( LB_DESCR *descr, INT16 max, LPINT16 array )
961 LB_ITEMDATA *item = descr->items;
963 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
964 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
965 if (item->selected) array[count++] = (INT16)i;
970 /***********************************************************************
971 * LISTBOX_GetSelItems
973 static LRESULT LISTBOX_GetSelItems( LB_DESCR *descr, INT max, LPINT array )
976 LB_ITEMDATA *item = descr->items;
978 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
979 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
980 if (item->selected) array[count++] = i;
985 /***********************************************************************
988 static LRESULT LISTBOX_Paint( LB_DESCR *descr, HDC hdc )
990 INT i, col_pos = descr->page_size - 1;
992 RECT focusRect = {-1, -1, -1, -1};
994 HBRUSH hbrush, oldBrush = 0;
996 if (descr->style & LBS_NOREDRAW) return 0;
998 SetRect( &rect, 0, 0, descr->width, descr->height );
999 if (descr->style & LBS_MULTICOLUMN)
1000 rect.right = rect.left + descr->column_width;
1001 else if (descr->horz_pos)
1003 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
1004 rect.right += descr->horz_pos;
1007 if (descr->font) oldFont = SelectObject( hdc, descr->font );
1008 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1009 (WPARAM)hdc, (LPARAM)descr->self );
1010 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1011 if (!IsWindowEnabled(descr->self)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1013 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1016 /* Special case for empty listbox: paint focus rect */
1017 rect.bottom = rect.top + descr->item_height;
1018 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1019 &rect, NULL, 0, NULL );
1020 LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, FALSE );
1021 rect.top = rect.bottom;
1024 /* Paint all the item, regarding the selection
1025 Focus state will be painted after */
1027 for (i = descr->top_item; i < descr->nb_items; i++)
1029 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1030 rect.bottom = rect.top + descr->item_height;
1032 rect.bottom = rect.top + descr->items[i].height;
1034 if (i == descr->focus_item)
1036 /* keep the focus rect, to paint the focus item after */
1037 focusRect.left = rect.left;
1038 focusRect.right = rect.right;
1039 focusRect.top = rect.top;
1040 focusRect.bottom = rect.bottom;
1042 LISTBOX_PaintItem( descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1043 rect.top = rect.bottom;
1045 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1047 if (!IS_OWNERDRAW(descr))
1049 /* Clear the bottom of the column */
1050 if (rect.top < descr->height)
1052 rect.bottom = descr->height;
1053 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1054 &rect, NULL, 0, NULL );
1058 /* Go to the next column */
1059 rect.left += descr->column_width;
1060 rect.right += descr->column_width;
1062 col_pos = descr->page_size - 1;
1067 if (rect.top >= descr->height) break;
1071 /* Paint the focus item now */
1072 if (focusRect.top != focusRect.bottom &&
1073 descr->caret_on && descr->in_focus)
1074 LISTBOX_PaintItem( descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1076 if (!IS_OWNERDRAW(descr))
1078 /* Clear the remainder of the client area */
1079 if (rect.top < descr->height)
1081 rect.bottom = descr->height;
1082 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1083 &rect, NULL, 0, NULL );
1085 if (rect.right < descr->width)
1087 rect.left = rect.right;
1088 rect.right = descr->width;
1090 rect.bottom = descr->height;
1091 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1092 &rect, NULL, 0, NULL );
1095 if (oldFont) SelectObject( hdc, oldFont );
1096 if (oldBrush) SelectObject( hdc, oldBrush );
1101 /***********************************************************************
1102 * LISTBOX_InvalidateItems
1104 * Invalidate all items from a given item. If the specified item is not
1105 * visible, nothing happens.
1107 static void LISTBOX_InvalidateItems( LB_DESCR *descr, INT index )
1111 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1113 if (descr->style & LBS_NOREDRAW)
1115 descr->style |= LBS_DISPLAYCHANGED;
1118 rect.bottom = descr->height;
1119 InvalidateRect( descr->self, &rect, TRUE );
1120 if (descr->style & LBS_MULTICOLUMN)
1122 /* Repaint the other columns */
1123 rect.left = rect.right;
1124 rect.right = descr->width;
1126 InvalidateRect( descr->self, &rect, TRUE );
1131 static void LISTBOX_InvalidateItemRect( LB_DESCR *descr, INT index )
1135 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1136 InvalidateRect( descr->self, &rect, TRUE );
1139 /***********************************************************************
1140 * LISTBOX_GetItemHeight
1142 static LRESULT LISTBOX_GetItemHeight( LB_DESCR *descr, INT index )
1144 if (descr->style & LBS_OWNERDRAWVARIABLE)
1146 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1147 return descr->items[index].height;
1149 else return descr->item_height;
1153 /***********************************************************************
1154 * LISTBOX_SetItemHeight
1156 static LRESULT LISTBOX_SetItemHeight( LB_DESCR *descr, INT index, INT height, BOOL repaint )
1158 if (!height) height = 1;
1160 if (descr->style & LBS_OWNERDRAWVARIABLE)
1162 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1163 TRACE("[%p]: item %d height = %d\n", descr->self, index, height );
1164 descr->items[index].height = height;
1165 LISTBOX_UpdateScroll( descr );
1167 LISTBOX_InvalidateItems( descr, index );
1169 else if (height != descr->item_height)
1171 TRACE("[%p]: new height = %d\n", descr->self, height );
1172 descr->item_height = height;
1173 LISTBOX_UpdatePage( descr );
1174 LISTBOX_UpdateScroll( descr );
1176 InvalidateRect( descr->self, 0, TRUE );
1182 /***********************************************************************
1183 * LISTBOX_SetHorizontalPos
1185 static void LISTBOX_SetHorizontalPos( LB_DESCR *descr, INT pos )
1189 if (pos > descr->horz_extent - descr->width)
1190 pos = descr->horz_extent - descr->width;
1191 if (pos < 0) pos = 0;
1192 if (!(diff = descr->horz_pos - pos)) return;
1193 TRACE("[%p]: new horz pos = %d\n", descr->self, pos );
1194 descr->horz_pos = pos;
1195 LISTBOX_UpdateScroll( descr );
1196 if (abs(diff) < descr->width)
1199 /* Invalidate the focused item so it will be repainted correctly */
1200 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
1201 InvalidateRect( descr->self, &rect, TRUE );
1202 ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
1203 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1206 InvalidateRect( descr->self, NULL, TRUE );
1210 /***********************************************************************
1211 * LISTBOX_SetHorizontalExtent
1213 static LRESULT LISTBOX_SetHorizontalExtent( LB_DESCR *descr, INT extent )
1215 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1217 if (extent <= 0) extent = 1;
1218 if (extent == descr->horz_extent) return LB_OKAY;
1219 TRACE("[%p]: new horz extent = %d\n", descr->self, extent );
1220 descr->horz_extent = extent;
1221 if (descr->horz_pos > extent - descr->width)
1222 LISTBOX_SetHorizontalPos( descr, extent - descr->width );
1224 LISTBOX_UpdateScroll( descr );
1229 /***********************************************************************
1230 * LISTBOX_SetColumnWidth
1232 static LRESULT LISTBOX_SetColumnWidth( LB_DESCR *descr, INT width)
1234 if (width == descr->column_width) return LB_OKAY;
1235 TRACE("[%p]: new column width = %d\n", descr->self, width );
1236 descr->column_width = width;
1237 LISTBOX_UpdatePage( descr );
1242 /***********************************************************************
1245 * Returns the item height.
1247 static INT LISTBOX_SetFont( LB_DESCR *descr, HFONT font )
1255 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE )))
1257 ERR("unable to get DC.\n" );
1260 if (font) oldFont = SelectObject( hdc, font );
1261 GetTextMetricsW( hdc, &tm );
1262 if (oldFont) SelectObject( hdc, oldFont );
1263 ReleaseDC( descr->self, hdc );
1264 if (!IS_OWNERDRAW(descr))
1265 LISTBOX_SetItemHeight( descr, 0, tm.tmHeight, FALSE );
1270 /***********************************************************************
1271 * LISTBOX_MakeItemVisible
1273 * Make sure that a given item is partially or fully visible.
1275 static void LISTBOX_MakeItemVisible( LB_DESCR *descr, INT index, BOOL fully )
1279 if (index <= descr->top_item) top = index;
1280 else if (descr->style & LBS_MULTICOLUMN)
1282 INT cols = descr->width;
1283 if (!fully) cols += descr->column_width - 1;
1284 if (cols >= descr->column_width) cols /= descr->column_width;
1286 if (index < descr->top_item + (descr->page_size * cols)) return;
1287 top = index - descr->page_size * (cols - 1);
1289 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1291 INT height = fully ? descr->items[index].height : 1;
1292 for (top = index; top > descr->top_item; top--)
1293 if ((height += descr->items[top-1].height) > descr->height) break;
1297 if (index < descr->top_item + descr->page_size) return;
1298 if (!fully && (index == descr->top_item + descr->page_size) &&
1299 (descr->height > (descr->page_size * descr->item_height))) return;
1300 top = index - descr->page_size + 1;
1302 LISTBOX_SetTopItem( descr, top, TRUE );
1305 /***********************************************************************
1306 * LISTBOX_SetCaretIndex
1309 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1312 static LRESULT LISTBOX_SetCaretIndex( LB_DESCR *descr, INT index, BOOL fully_visible )
1314 INT oldfocus = descr->focus_item;
1316 if (descr->style & LBS_NOSEL) return LB_ERR;
1317 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1318 if (index == oldfocus) return LB_OKAY;
1319 descr->focus_item = index;
1320 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1321 LISTBOX_RepaintItem( descr, oldfocus, ODA_FOCUS );
1323 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1324 if (descr->caret_on && (descr->in_focus))
1325 LISTBOX_RepaintItem( descr, index, ODA_FOCUS );
1331 /***********************************************************************
1332 * LISTBOX_SelectItemRange
1334 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1336 static LRESULT LISTBOX_SelectItemRange( LB_DESCR *descr, INT first,
1341 /* A few sanity checks */
1343 if (descr->style & LBS_NOSEL) return LB_ERR;
1344 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1345 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1346 if (last == -1) last = descr->nb_items - 1;
1347 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1348 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1350 if (on) /* Turn selection on */
1352 for (i = first; i <= last; i++)
1354 if (descr->items[i].selected) continue;
1355 descr->items[i].selected = TRUE;
1356 LISTBOX_InvalidateItemRect(descr, i);
1359 else /* Turn selection off */
1361 for (i = first; i <= last; i++)
1363 if (!descr->items[i].selected) continue;
1364 descr->items[i].selected = FALSE;
1365 LISTBOX_InvalidateItemRect(descr, i);
1371 /***********************************************************************
1372 * LISTBOX_SetSelection
1374 static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index,
1375 BOOL on, BOOL send_notify )
1377 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1379 if (descr->style & LBS_NOSEL)
1381 descr->selected_item = index;
1384 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1385 if (descr->style & LBS_MULTIPLESEL)
1387 if (index == -1) /* Select all items */
1388 return LISTBOX_SelectItemRange( descr, 0, -1, on );
1389 else /* Only one item */
1390 return LISTBOX_SelectItemRange( descr, index, index, on );
1394 INT oldsel = descr->selected_item;
1395 if (index == oldsel) return LB_OKAY;
1396 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1397 if (index != -1) descr->items[index].selected = TRUE;
1398 descr->selected_item = index;
1399 if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT );
1400 if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT );
1401 if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr,
1402 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1404 if( descr->lphc ) /* set selection change flag for parent combo */
1405 descr->lphc->wState |= CBF_SELCHANGE;
1411 /***********************************************************************
1414 * Change the caret position and extend the selection to the new caret.
1416 static void LISTBOX_MoveCaret( LB_DESCR *descr, INT index, BOOL fully_visible )
1418 INT oldfocus = descr->focus_item;
1420 if ((index < 0) || (index >= descr->nb_items))
1423 /* Important, repaint needs to be done in this order if
1424 you want to mimic Windows behavior:
1425 1. Remove the focus and paint the item
1426 2. Remove the selection and paint the item(s)
1427 3. Set the selection and repaint the item(s)
1428 4. Set the focus to 'index' and repaint the item */
1430 /* 1. remove the focus and repaint the item */
1431 descr->focus_item = -1;
1432 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1433 LISTBOX_RepaintItem( descr, oldfocus, ODA_FOCUS );
1435 /* 2. then turn off the previous selection */
1436 /* 3. repaint the new selected item */
1437 if (descr->style & LBS_EXTENDEDSEL)
1439 if (descr->anchor_item != -1)
1441 INT first = min( index, descr->anchor_item );
1442 INT last = max( index, descr->anchor_item );
1444 LISTBOX_SelectItemRange( descr, 0, first - 1, FALSE );
1445 LISTBOX_SelectItemRange( descr, last + 1, -1, FALSE );
1446 LISTBOX_SelectItemRange( descr, first, last, TRUE );
1449 else if (!(descr->style & LBS_MULTIPLESEL))
1451 /* Set selection to new caret item */
1452 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
1455 /* 4. repaint the new item with the focus */
1456 descr->focus_item = index;
1457 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1458 if (descr->caret_on && (descr->in_focus))
1459 LISTBOX_RepaintItem( descr, index, ODA_FOCUS );
1463 /***********************************************************************
1464 * LISTBOX_InsertItem
1466 static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index,
1467 LPWSTR str, DWORD data )
1471 INT oldfocus = descr->focus_item;
1473 if (index == -1) index = descr->nb_items;
1474 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1475 if (!descr->items) max_items = 0;
1476 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1477 if (descr->nb_items == max_items)
1479 /* We need to grow the array */
1480 max_items += LB_ARRAY_GRANULARITY;
1482 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1483 max_items * sizeof(LB_ITEMDATA) );
1485 item = HeapAlloc( GetProcessHeap(), 0,
1486 max_items * sizeof(LB_ITEMDATA) );
1489 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1492 descr->items = item;
1495 /* Insert the item structure */
1497 item = &descr->items[index];
1498 if (index < descr->nb_items)
1499 RtlMoveMemory( item + 1, item,
1500 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1504 item->selected = FALSE;
1507 /* Get item height */
1509 if (descr->style & LBS_OWNERDRAWVARIABLE)
1511 MEASUREITEMSTRUCT mis;
1512 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1514 mis.CtlType = ODT_LISTBOX;
1517 mis.itemData = descr->items[index].data;
1518 mis.itemHeight = descr->item_height;
1519 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1520 item->height = mis.itemHeight ? mis.itemHeight : 1;
1521 TRACE("[%p]: measure item %d (%s) = %d\n",
1522 descr->self, index, str ? debugstr_w(str) : "", item->height );
1525 /* Repaint the items */
1527 LISTBOX_UpdateScroll( descr );
1528 LISTBOX_InvalidateItems( descr, index );
1530 /* Move selection and focused item */
1531 /* If listbox was empty, set focus to the first item */
1532 if (descr->nb_items == 1)
1533 LISTBOX_SetCaretIndex( descr, 0, FALSE );
1534 /* single select don't change selection index in win31 */
1535 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1537 descr->selected_item++;
1538 LISTBOX_SetSelection( descr, descr->selected_item-1, TRUE, FALSE );
1542 if (index <= descr->selected_item)
1544 descr->selected_item++;
1545 descr->focus_item = oldfocus; /* focus not changed */
1552 /***********************************************************************
1553 * LISTBOX_InsertString
1555 static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index, LPCWSTR str )
1557 LPWSTR new_str = NULL;
1561 if (HAS_STRINGS(descr))
1563 static const WCHAR empty_stringW[] = { 0 };
1564 if (!str) str = empty_stringW;
1565 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1567 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1570 strcpyW(new_str, str);
1572 else data = (DWORD)str;
1574 if (index == -1) index = descr->nb_items;
1575 if ((ret = LISTBOX_InsertItem( descr, index, new_str, data )) != 0)
1577 HeapFree( GetProcessHeap(), 0, new_str );
1581 TRACE("[%p]: added item %d %s\n",
1582 descr->self, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1587 /***********************************************************************
1588 * LISTBOX_DeleteItem
1590 * Delete the content of an item. 'index' must be a valid index.
1592 static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index )
1594 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1595 * while Win95 sends it for all items with user data.
1596 * It's probably better to send it too often than not
1597 * often enough, so this is what we do here.
1599 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1601 DELETEITEMSTRUCT dis;
1602 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1604 dis.CtlType = ODT_LISTBOX;
1607 dis.hwndItem = descr->self;
1608 dis.itemData = descr->items[index].data;
1609 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1611 if (HAS_STRINGS(descr) && descr->items[index].str)
1612 HeapFree( GetProcessHeap(), 0, descr->items[index].str );
1616 /***********************************************************************
1617 * LISTBOX_RemoveItem
1619 * Remove an item from the listbox and delete its content.
1621 static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index )
1626 if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1627 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1629 /* We need to invalidate the original rect instead of the updated one. */
1630 LISTBOX_InvalidateItems( descr, index );
1632 LISTBOX_DeleteItem( descr, index );
1634 /* Remove the item */
1636 item = &descr->items[index];
1637 if (index < descr->nb_items-1)
1638 RtlMoveMemory( item, item + 1,
1639 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1641 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1643 /* Shrink the item array if possible */
1645 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1646 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1648 max_items -= LB_ARRAY_GRANULARITY;
1649 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1650 max_items * sizeof(LB_ITEMDATA) );
1651 if (item) descr->items = item;
1653 /* Repaint the items */
1655 LISTBOX_UpdateScroll( descr );
1656 /* if we removed the scrollbar, reset the top of the list
1657 (correct for owner-drawn ???) */
1658 if (descr->nb_items == descr->page_size)
1659 LISTBOX_SetTopItem( descr, 0, TRUE );
1661 /* Move selection and focused item */
1662 if (!IS_MULTISELECT(descr))
1664 if (index == descr->selected_item)
1665 descr->selected_item = -1;
1666 else if (index < descr->selected_item)
1668 descr->selected_item--;
1669 if (ISWIN31) /* win 31 do not change the selected item number */
1670 LISTBOX_SetSelection( descr, descr->selected_item + 1, TRUE, FALSE);
1674 if (descr->focus_item >= descr->nb_items)
1676 descr->focus_item = descr->nb_items - 1;
1677 if (descr->focus_item < 0) descr->focus_item = 0;
1683 /***********************************************************************
1684 * LISTBOX_ResetContent
1686 static void LISTBOX_ResetContent( LB_DESCR *descr )
1690 for(i = descr->nb_items - 1; i>=0; i--) LISTBOX_DeleteItem( descr, i);
1691 HeapFree( GetProcessHeap(), 0, descr->items );
1692 descr->nb_items = 0;
1693 descr->top_item = 0;
1694 descr->selected_item = -1;
1695 descr->focus_item = 0;
1696 descr->anchor_item = -1;
1697 descr->items = NULL;
1701 /***********************************************************************
1704 static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count )
1708 if (HAS_STRINGS(descr)) return LB_ERR;
1709 /* FIXME: this is far from optimal... */
1710 if (count > descr->nb_items)
1712 while (count > descr->nb_items)
1713 if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0)
1716 else if (count < descr->nb_items)
1718 while (count < descr->nb_items)
1719 if ((ret = LISTBOX_RemoveItem( descr, -1 )) < 0)
1726 /***********************************************************************
1729 static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib,
1730 LPCWSTR filespec, BOOL long_names )
1733 LRESULT ret = LB_OKAY;
1734 WIN32_FIND_DATAW entry;
1737 /* don't scan directory if we just want drives exclusively */
1738 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1739 /* scan directory */
1740 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1742 int le = GetLastError();
1743 if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR;
1750 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1752 static const WCHAR bracketW[] = { ']',0 };
1753 static const WCHAR dotW[] = { '.',0 };
1754 if (!(attrib & DDL_DIRECTORY) ||
1755 !strcmpW( entry.cFileName, dotW )) continue;
1757 if (!long_names && entry.cAlternateFileName[0])
1758 strcpyW( buffer + 1, entry.cAlternateFileName );
1760 strcpyW( buffer + 1, entry.cFileName );
1761 strcatW(buffer, bracketW);
1763 else /* not a directory */
1765 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1766 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1768 if ((attrib & DDL_EXCLUSIVE) &&
1769 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1772 if (!long_names && entry.cAlternateFileName[0])
1773 strcpyW( buffer, entry.cAlternateFileName );
1775 strcpyW( buffer, entry.cFileName );
1777 if (!long_names) CharLowerW( buffer );
1778 pos = LISTBOX_FindFileStrPos( descr, buffer );
1779 if ((ret = LISTBOX_InsertString( descr, pos, buffer )) < 0)
1781 } while (FindNextFileW( handle, &entry ));
1782 FindClose( handle );
1787 if ((ret >= 0) && (attrib & DDL_DRIVES))
1789 WCHAR buffer[] = {'[','-','a','-',']',0};
1790 WCHAR root[] = {'A',':','\\',0};
1792 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1794 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1795 if ((ret = LISTBOX_InsertString( descr, -1, buffer )) < 0)
1803 /***********************************************************************
1804 * LISTBOX_HandleVScroll
1806 static LRESULT LISTBOX_HandleVScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1810 if (descr->style & LBS_MULTICOLUMN) return 0;
1814 LISTBOX_SetTopItem( descr, descr->top_item - 1, TRUE );
1817 LISTBOX_SetTopItem( descr, descr->top_item + 1, TRUE );
1820 LISTBOX_SetTopItem( descr, descr->top_item -
1821 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1824 LISTBOX_SetTopItem( descr, descr->top_item +
1825 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1827 case SB_THUMBPOSITION:
1828 LISTBOX_SetTopItem( descr, pos, TRUE );
1831 info.cbSize = sizeof(info);
1832 info.fMask = SIF_TRACKPOS;
1833 GetScrollInfo( descr->self, SB_VERT, &info );
1834 LISTBOX_SetTopItem( descr, info.nTrackPos, TRUE );
1837 LISTBOX_SetTopItem( descr, 0, TRUE );
1840 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1847 /***********************************************************************
1848 * LISTBOX_HandleHScroll
1850 static LRESULT LISTBOX_HandleHScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1855 if (descr->style & LBS_MULTICOLUMN)
1860 LISTBOX_SetTopItem( descr, descr->top_item-descr->page_size,
1864 LISTBOX_SetTopItem( descr, descr->top_item+descr->page_size,
1868 page = descr->width / descr->column_width;
1869 if (page < 1) page = 1;
1870 LISTBOX_SetTopItem( descr,
1871 descr->top_item - page * descr->page_size, TRUE );
1874 page = descr->width / descr->column_width;
1875 if (page < 1) page = 1;
1876 LISTBOX_SetTopItem( descr,
1877 descr->top_item + page * descr->page_size, TRUE );
1879 case SB_THUMBPOSITION:
1880 LISTBOX_SetTopItem( descr, pos*descr->page_size, TRUE );
1883 info.cbSize = sizeof(info);
1884 info.fMask = SIF_TRACKPOS;
1885 GetScrollInfo( descr->self, SB_VERT, &info );
1886 LISTBOX_SetTopItem( descr, info.nTrackPos*descr->page_size,
1890 LISTBOX_SetTopItem( descr, 0, TRUE );
1893 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1897 else if (descr->horz_extent)
1902 LISTBOX_SetHorizontalPos( descr, descr->horz_pos - 1 );
1905 LISTBOX_SetHorizontalPos( descr, descr->horz_pos + 1 );
1908 LISTBOX_SetHorizontalPos( descr,
1909 descr->horz_pos - descr->width );
1912 LISTBOX_SetHorizontalPos( descr,
1913 descr->horz_pos + descr->width );
1915 case SB_THUMBPOSITION:
1916 LISTBOX_SetHorizontalPos( descr, pos );
1919 info.cbSize = sizeof(info);
1920 info.fMask = SIF_TRACKPOS;
1921 GetScrollInfo( descr->self, SB_HORZ, &info );
1922 LISTBOX_SetHorizontalPos( descr, info.nTrackPos );
1925 LISTBOX_SetHorizontalPos( descr, 0 );
1928 LISTBOX_SetHorizontalPos( descr,
1929 descr->horz_extent - descr->width );
1936 static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta )
1938 short gcWheelDelta = 0;
1939 UINT pulScrollLines = 3;
1941 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1943 gcWheelDelta -= delta;
1945 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1947 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1948 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1949 LISTBOX_SetTopItem( descr, descr->top_item + cLineScroll, TRUE );
1954 /***********************************************************************
1955 * LISTBOX_HandleLButtonDown
1957 static LRESULT LISTBOX_HandleLButtonDown( LB_DESCR *descr, DWORD keys, INT x, INT y )
1959 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
1960 TRACE("[%p]: lbuttondown %d,%d item %d\n", descr->self, x, y, index );
1961 if (!descr->caret_on && (descr->in_focus)) return 0;
1963 if (!descr->in_focus)
1965 if( !descr->lphc ) SetFocus( descr->self );
1966 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
1969 if (index == -1) return 0;
1971 if (descr->style & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL))
1973 /* we should perhaps make sure that all items are deselected
1974 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1975 if (!(keys & (MK_SHIFT|MK_CONTROL)))
1976 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
1979 if (!(keys & MK_SHIFT)) descr->anchor_item = index;
1980 if (keys & MK_CONTROL)
1982 LISTBOX_SetCaretIndex( descr, index, FALSE );
1983 LISTBOX_SetSelection( descr, index,
1984 !descr->items[index].selected,
1985 (descr->style & LBS_NOTIFY) != 0);
1989 LISTBOX_MoveCaret( descr, index, FALSE );
1991 if (descr->style & LBS_EXTENDEDSEL)
1993 LISTBOX_SetSelection( descr, index,
1994 descr->items[index].selected,
1995 (descr->style & LBS_NOTIFY) != 0 );
1999 LISTBOX_SetSelection( descr, index,
2000 !descr->items[index].selected,
2001 (descr->style & LBS_NOTIFY) != 0 );
2007 descr->anchor_item = index;
2008 LISTBOX_MoveCaret( descr, index, FALSE );
2009 LISTBOX_SetSelection( descr, index,
2010 TRUE, (descr->style & LBS_NOTIFY) != 0 );
2013 descr->captured = TRUE;
2014 SetCapture( descr->self );
2018 if (descr->style & LBS_NOTIFY )
2019 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
2020 MAKELPARAM( x, y ) );
2021 if (GetWindowLongW( descr->self, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
2028 if (DragDetect( descr->self, pt ))
2029 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
2036 /*************************************************************************
2037 * LISTBOX_HandleLButtonDownCombo [Internal]
2039 * Process LButtonDown message for the ComboListBox
2042 * pWnd [I] The windows internal structure
2043 * pDescr [I] The ListBox internal structure
2044 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2045 * x [I] X Mouse Coordinate
2046 * y [I] Y Mouse Coordinate
2049 * 0 since we are processing the WM_LBUTTONDOWN Message
2052 * This function is only to be used when a ListBox is a ComboListBox
2055 static LRESULT LISTBOX_HandleLButtonDownCombo( LB_DESCR *descr, UINT msg, DWORD keys, INT x, INT y)
2057 RECT clientRect, screenRect;
2063 GetClientRect(descr->self, &clientRect);
2065 if(PtInRect(&clientRect, mousePos))
2067 /* MousePos is in client, resume normal processing */
2068 if (msg == WM_LBUTTONDOWN)
2070 descr->lphc->droppedIndex = descr->nb_items ? descr->selected_item : -1;
2071 return LISTBOX_HandleLButtonDown( descr, keys, x, y);
2073 else if (descr->style & LBS_NOTIFY)
2074 SEND_NOTIFICATION( descr, LBN_DBLCLK );
2078 POINT screenMousePos;
2079 HWND hWndOldCapture;
2081 /* Check the Non-Client Area */
2082 screenMousePos = mousePos;
2083 hWndOldCapture = GetCapture();
2085 GetWindowRect(descr->self, &screenRect);
2086 ClientToScreen(descr->self, &screenMousePos);
2088 if(!PtInRect(&screenRect, screenMousePos))
2090 LISTBOX_SetCaretIndex( descr, descr->lphc->droppedIndex, FALSE );
2091 LISTBOX_SetSelection( descr, descr->lphc->droppedIndex, FALSE, FALSE );
2092 COMBO_FlipListbox( descr->lphc, FALSE, FALSE );
2096 /* Check to see the NC is a scrollbar */
2098 LONG style = GetWindowLongW( descr->self, GWL_STYLE );
2099 /* Check Vertical scroll bar */
2100 if (style & WS_VSCROLL)
2102 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2103 if (PtInRect( &clientRect, mousePos ))
2104 nHitTestType = HTVSCROLL;
2106 /* Check horizontal scroll bar */
2107 if (style & WS_HSCROLL)
2109 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2110 if (PtInRect( &clientRect, mousePos ))
2111 nHitTestType = HTHSCROLL;
2113 /* Windows sends this message when a scrollbar is clicked
2116 if(nHitTestType != 0)
2118 SendMessageW(descr->self, WM_NCLBUTTONDOWN, nHitTestType,
2119 MAKELONG(screenMousePos.x, screenMousePos.y));
2121 /* Resume the Capture after scrolling is complete
2123 if(hWndOldCapture != 0)
2124 SetCapture(hWndOldCapture);
2130 /***********************************************************************
2131 * LISTBOX_HandleLButtonUp
2133 static LRESULT LISTBOX_HandleLButtonUp( LB_DESCR *descr )
2135 if (LISTBOX_Timer != LB_TIMER_NONE)
2136 KillSystemTimer( descr->self, LB_TIMER_ID );
2137 LISTBOX_Timer = LB_TIMER_NONE;
2138 if (descr->captured)
2140 descr->captured = FALSE;
2141 if (GetCapture() == descr->self) ReleaseCapture();
2142 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2143 SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2149 /***********************************************************************
2150 * LISTBOX_HandleTimer
2152 * Handle scrolling upon a timer event.
2153 * Return TRUE if scrolling should continue.
2155 static LRESULT LISTBOX_HandleTimer( LB_DESCR *descr, INT index, TIMER_DIRECTION dir )
2160 if (descr->top_item) index = descr->top_item - 1;
2164 if (descr->top_item) index -= descr->page_size;
2167 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2168 if (index == descr->focus_item) index++;
2169 if (index >= descr->nb_items) index = descr->nb_items - 1;
2171 case LB_TIMER_RIGHT:
2172 if (index + descr->page_size < descr->nb_items)
2173 index += descr->page_size;
2178 if (index == descr->focus_item) return FALSE;
2179 LISTBOX_MoveCaret( descr, index, FALSE );
2184 /***********************************************************************
2185 * LISTBOX_HandleSystemTimer
2187 * WM_SYSTIMER handler.
2189 static LRESULT LISTBOX_HandleSystemTimer( LB_DESCR *descr )
2191 if (!LISTBOX_HandleTimer( descr, descr->focus_item, LISTBOX_Timer ))
2193 KillSystemTimer( descr->self, LB_TIMER_ID );
2194 LISTBOX_Timer = LB_TIMER_NONE;
2200 /***********************************************************************
2201 * LISTBOX_HandleMouseMove
2203 * WM_MOUSEMOVE handler.
2205 static void LISTBOX_HandleMouseMove( LB_DESCR *descr,
2209 TIMER_DIRECTION dir = LB_TIMER_NONE;
2211 if (!descr->captured) return;
2213 if (descr->style & LBS_MULTICOLUMN)
2216 else if (y >= descr->item_height * descr->page_size)
2217 y = descr->item_height * descr->page_size - 1;
2221 dir = LB_TIMER_LEFT;
2224 else if (x >= descr->width)
2226 dir = LB_TIMER_RIGHT;
2227 x = descr->width - 1;
2232 if (y < 0) dir = LB_TIMER_UP; /* above */
2233 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2236 index = LISTBOX_GetItemFromPoint( descr, x, y );
2237 if (index == -1) index = descr->focus_item;
2238 if (!LISTBOX_HandleTimer( descr, index, dir )) dir = LB_TIMER_NONE;
2240 /* Start/stop the system timer */
2242 if (dir != LB_TIMER_NONE)
2243 SetSystemTimer( descr->self, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2244 else if (LISTBOX_Timer != LB_TIMER_NONE)
2245 KillSystemTimer( descr->self, LB_TIMER_ID );
2246 LISTBOX_Timer = dir;
2250 /***********************************************************************
2251 * LISTBOX_HandleKeyDown
2253 static LRESULT LISTBOX_HandleKeyDown( LB_DESCR *descr, DWORD key )
2256 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2257 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2258 bForceSelection = FALSE; /* only for single select list */
2260 if (descr->style & LBS_WANTKEYBOARDINPUT)
2262 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2263 MAKEWPARAM(LOWORD(key), descr->focus_item),
2264 (LPARAM)descr->self );
2265 if (caret == -2) return 0;
2267 if (caret == -1) switch(key)
2270 if (descr->style & LBS_MULTICOLUMN)
2272 bForceSelection = FALSE;
2273 if (descr->focus_item >= descr->page_size)
2274 caret = descr->focus_item - descr->page_size;
2279 caret = descr->focus_item - 1;
2280 if (caret < 0) caret = 0;
2283 if (descr->style & LBS_MULTICOLUMN)
2285 bForceSelection = FALSE;
2286 if (descr->focus_item + descr->page_size < descr->nb_items)
2287 caret = descr->focus_item + descr->page_size;
2292 caret = descr->focus_item + 1;
2293 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2297 if (descr->style & LBS_MULTICOLUMN)
2299 INT page = descr->width / descr->column_width;
2300 if (page < 1) page = 1;
2301 caret = descr->focus_item - (page * descr->page_size) + 1;
2303 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2304 if (caret < 0) caret = 0;
2307 if (descr->style & LBS_MULTICOLUMN)
2309 INT page = descr->width / descr->column_width;
2310 if (page < 1) page = 1;
2311 caret = descr->focus_item + (page * descr->page_size) - 1;
2313 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2314 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2320 caret = descr->nb_items - 1;
2323 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2324 else if (descr->style & LBS_MULTIPLESEL)
2326 LISTBOX_SetSelection( descr, descr->focus_item,
2327 !descr->items[descr->focus_item].selected,
2328 (descr->style & LBS_NOTIFY) != 0 );
2332 bForceSelection = FALSE;
2334 if (bForceSelection) /* focused item is used instead of key */
2335 caret = descr->focus_item;
2338 if (((descr->style & LBS_EXTENDEDSEL) &&
2339 !(GetKeyState( VK_SHIFT ) & 0x8000)) ||
2340 !IS_MULTISELECT(descr))
2341 descr->anchor_item = caret;
2342 LISTBOX_MoveCaret( descr, caret, TRUE );
2344 if (descr->style & LBS_MULTIPLESEL)
2345 descr->selected_item = caret;
2347 LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2348 if (descr->style & LBS_NOTIFY)
2352 /* make sure that combo parent doesn't hide us */
2353 descr->lphc->wState |= CBF_NOROLLUP;
2355 if (descr->nb_items) SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2362 /***********************************************************************
2363 * LISTBOX_HandleChar
2365 static LRESULT LISTBOX_HandleChar( LB_DESCR *descr, WCHAR charW )
2373 if (descr->style & LBS_WANTKEYBOARDINPUT)
2375 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2376 MAKEWPARAM(charW, descr->focus_item),
2377 (LPARAM)descr->self );
2378 if (caret == -2) return 0;
2381 caret = LISTBOX_FindString( descr, descr->focus_item, str, FALSE);
2384 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2385 LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2386 LISTBOX_MoveCaret( descr, caret, TRUE );
2387 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2388 SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2394 /***********************************************************************
2397 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2400 MEASUREITEMSTRUCT mis;
2403 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2406 GetClientRect( hwnd, &rect );
2408 descr->owner = GetParent( descr->self );
2409 descr->style = GetWindowLongW( descr->self, GWL_STYLE );
2410 descr->width = rect.right - rect.left;
2411 descr->height = rect.bottom - rect.top;
2412 descr->items = NULL;
2413 descr->nb_items = 0;
2414 descr->top_item = 0;
2415 descr->selected_item = -1;
2416 descr->focus_item = 0;
2417 descr->anchor_item = -1;
2418 descr->item_height = 1;
2419 descr->page_size = 1;
2420 descr->column_width = 150;
2421 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0;
2422 descr->horz_pos = 0;
2425 descr->caret_on = lphc ? FALSE : TRUE;
2426 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2427 descr->in_focus = FALSE;
2428 descr->captured = FALSE;
2430 descr->locale = 0; /* FIXME */
2433 if (is_old_app(descr) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2435 /* Win95 document "List Box Differences" from MSDN:
2436 If a list box in a version 3.x application has either the
2437 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2438 horizontal and vertical scroll bars.
2440 descr->style |= WS_VSCROLL | WS_HSCROLL;
2445 TRACE("[%p]: resetting owner %p -> %p\n", descr->self, descr->owner, lphc->self );
2446 descr->owner = lphc->self;
2449 SetWindowLongW( descr->self, 0, (LONG)descr );
2451 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2453 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2454 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2455 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2456 descr->item_height = LISTBOX_SetFont( descr, 0 );
2458 if (descr->style & LBS_OWNERDRAWFIXED)
2460 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2462 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2463 descr->item_height = lphc->fixedOwnerDrawHeight;
2467 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
2468 mis.CtlType = ODT_LISTBOX;
2473 mis.itemHeight = descr->item_height;
2474 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2475 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2479 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2484 /***********************************************************************
2487 static BOOL LISTBOX_Destroy( LB_DESCR *descr )
2489 LISTBOX_ResetContent( descr );
2490 SetWindowLongW( descr->self, 0, 0 );
2491 HeapFree( GetProcessHeap(), 0, descr );
2496 /***********************************************************************
2497 * ListBoxWndProc_common
2499 static LRESULT WINAPI ListBoxWndProc_common( HWND hwnd, UINT msg,
2500 WPARAM wParam, LPARAM lParam, BOOL unicode )
2502 LB_DESCR *descr = (LB_DESCR *)GetWindowLongW( hwnd, 0 );
2503 LPHEADCOMBO lphc = 0;
2508 if (!IsWindow(hwnd)) return 0;
2510 if (msg == WM_CREATE)
2512 CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam;
2513 if (lpcs->style & LBS_COMBOBOX) lphc = (LPHEADCOMBO)lpcs->lpCreateParams;
2514 if (!LISTBOX_Create( hwnd, lphc )) return -1;
2515 TRACE("creating wnd=%p descr=%lx\n", hwnd, GetWindowLongW( hwnd, 0 ) );
2518 /* Ignore all other messages before we get a WM_CREATE */
2519 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2520 DefWindowProcA( hwnd, msg, wParam, lParam );
2522 if (descr->style & LBS_COMBOBOX) lphc = descr->lphc;
2524 TRACE("[%p]: msg %s wp %08x lp %08lx\n",
2525 descr->self, SPY_GetMsgName(msg, descr->self), wParam, lParam );
2529 case LB_RESETCONTENT16:
2530 case LB_RESETCONTENT:
2531 LISTBOX_ResetContent( descr );
2532 LISTBOX_UpdateScroll( descr );
2533 InvalidateRect( descr->self, NULL, TRUE );
2536 case LB_ADDSTRING16:
2537 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2543 if(unicode || !HAS_STRINGS(descr))
2544 textW = (LPWSTR)lParam;
2547 LPSTR textA = (LPSTR)lParam;
2548 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2549 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2550 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2552 wParam = LISTBOX_FindStringPos( descr, textW, FALSE );
2553 ret = LISTBOX_InsertString( descr, wParam, textW );
2554 if (!unicode && HAS_STRINGS(descr))
2555 HeapFree(GetProcessHeap(), 0, textW);
2559 case LB_INSERTSTRING16:
2560 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2561 wParam = (INT)(INT16)wParam;
2563 case LB_INSERTSTRING:
2567 if(unicode || !HAS_STRINGS(descr))
2568 textW = (LPWSTR)lParam;
2571 LPSTR textA = (LPSTR)lParam;
2572 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2573 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2574 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2576 ret = LISTBOX_InsertString( descr, wParam, textW );
2577 if(!unicode && HAS_STRINGS(descr))
2578 HeapFree(GetProcessHeap(), 0, textW);
2583 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2589 if(unicode || !HAS_STRINGS(descr))
2590 textW = (LPWSTR)lParam;
2593 LPSTR textA = (LPSTR)lParam;
2594 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2595 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2596 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2598 wParam = LISTBOX_FindFileStrPos( descr, textW );
2599 ret = LISTBOX_InsertString( descr, wParam, textW );
2600 if(!unicode && HAS_STRINGS(descr))
2601 HeapFree(GetProcessHeap(), 0, textW);
2605 case LB_DELETESTRING16:
2606 case LB_DELETESTRING:
2607 if (LISTBOX_RemoveItem( descr, wParam) != LB_ERR)
2608 return descr->nb_items;
2612 case LB_GETITEMDATA16:
2613 case LB_GETITEMDATA:
2614 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2616 return descr->items[wParam].data;
2618 case LB_SETITEMDATA16:
2619 case LB_SETITEMDATA:
2620 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2622 descr->items[wParam].data = (DWORD)lParam;
2627 return descr->nb_items;
2630 lParam = (LPARAM)MapSL(lParam);
2633 return LISTBOX_GetText( descr, wParam, (LPWSTR)lParam, unicode );
2635 case LB_GETTEXTLEN16:
2638 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2640 if (!HAS_STRINGS(descr)) return sizeof(DWORD);
2641 if (unicode) return strlenW( descr->items[wParam].str );
2642 return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
2643 strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
2645 case LB_GETCURSEL16:
2647 if (descr->nb_items == 0)
2649 if (!IS_MULTISELECT(descr))
2650 return descr->selected_item;
2651 if (descr->selected_item != -1)
2652 return descr->selected_item;
2653 return descr->focus_item;
2654 /* otherwise, if the user tries to move the selection with the */
2655 /* arrow keys, we will give the application something to choke on */
2656 case LB_GETTOPINDEX16:
2657 case LB_GETTOPINDEX:
2658 return descr->top_item;
2660 case LB_GETITEMHEIGHT16:
2661 case LB_GETITEMHEIGHT:
2662 return LISTBOX_GetItemHeight( descr, wParam );
2664 case LB_SETITEMHEIGHT16:
2665 lParam = LOWORD(lParam);
2667 case LB_SETITEMHEIGHT:
2668 return LISTBOX_SetItemHeight( descr, wParam, lParam, TRUE );
2670 case LB_ITEMFROMPOINT:
2675 pt.x = LOWORD(lParam);
2676 pt.y = HIWORD(lParam);
2679 rect.right = descr->width;
2680 rect.bottom = descr->height;
2682 return MAKELONG( LISTBOX_GetItemFromPoint(descr, pt.x, pt.y),
2683 !PtInRect( &rect, pt ) );
2686 case LB_SETCARETINDEX16:
2687 case LB_SETCARETINDEX:
2688 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2689 if (LISTBOX_SetCaretIndex( descr, wParam, !lParam ) == LB_ERR)
2696 case LB_GETCARETINDEX16:
2697 case LB_GETCARETINDEX:
2698 return descr->focus_item;
2700 case LB_SETTOPINDEX16:
2701 case LB_SETTOPINDEX:
2702 return LISTBOX_SetTopItem( descr, wParam, TRUE );
2704 case LB_SETCOLUMNWIDTH16:
2705 case LB_SETCOLUMNWIDTH:
2706 return LISTBOX_SetColumnWidth( descr, wParam );
2708 case LB_GETITEMRECT16:
2711 RECT16 *r16 = MapSL(lParam);
2712 ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2713 r16->left = rect.left;
2714 r16->top = rect.top;
2715 r16->right = rect.right;
2716 r16->bottom = rect.bottom;
2720 case LB_GETITEMRECT:
2721 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2723 case LB_FINDSTRING16:
2724 wParam = (INT)(INT16)wParam;
2725 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2731 if(unicode || !HAS_STRINGS(descr))
2732 textW = (LPWSTR)lParam;
2735 LPSTR textA = (LPSTR)lParam;
2736 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2737 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2738 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2740 ret = LISTBOX_FindString( descr, wParam, textW, FALSE );
2741 if(!unicode && HAS_STRINGS(descr))
2742 HeapFree(GetProcessHeap(), 0, textW);
2746 case LB_FINDSTRINGEXACT16:
2747 wParam = (INT)(INT16)wParam;
2748 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2750 case LB_FINDSTRINGEXACT:
2754 if(unicode || !HAS_STRINGS(descr))
2755 textW = (LPWSTR)lParam;
2758 LPSTR textA = (LPSTR)lParam;
2759 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2760 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2761 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2763 ret = LISTBOX_FindString( descr, wParam, textW, TRUE );
2764 if(!unicode && HAS_STRINGS(descr))
2765 HeapFree(GetProcessHeap(), 0, textW);
2769 case LB_SELECTSTRING16:
2770 wParam = (INT)(INT16)wParam;
2771 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2773 case LB_SELECTSTRING:
2778 if(HAS_STRINGS(descr))
2779 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2780 debugstr_a((LPSTR)lParam));
2781 if(unicode || !HAS_STRINGS(descr))
2782 textW = (LPWSTR)lParam;
2785 LPSTR textA = (LPSTR)lParam;
2786 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2787 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2788 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2790 index = LISTBOX_FindString( descr, wParam, textW, FALSE );
2791 if(!unicode && HAS_STRINGS(descr))
2792 HeapFree(GetProcessHeap(), 0, textW);
2793 if (index != LB_ERR)
2795 LISTBOX_MoveCaret( descr, index, TRUE );
2796 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
2802 wParam = (INT)(INT16)wParam;
2805 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2807 return descr->items[wParam].selected;
2810 lParam = (INT)(INT16)lParam;
2813 return LISTBOX_SetSelection( descr, lParam, wParam, FALSE );
2815 case LB_SETCURSEL16:
2816 wParam = (INT)(INT16)wParam;
2819 if (IS_MULTISELECT(descr)) return LB_ERR;
2820 LISTBOX_SetCaretIndex( descr, wParam, TRUE );
2821 ret = LISTBOX_SetSelection( descr, wParam, TRUE, FALSE );
2822 if (lphc && ret != LB_ERR) ret = descr->selected_item;
2825 case LB_GETSELCOUNT16:
2826 case LB_GETSELCOUNT:
2827 return LISTBOX_GetSelCount( descr );
2829 case LB_GETSELITEMS16:
2830 return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2832 case LB_GETSELITEMS:
2833 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2835 case LB_SELITEMRANGE16:
2836 case LB_SELITEMRANGE:
2837 if (LOWORD(lParam) <= HIWORD(lParam))
2838 return LISTBOX_SelectItemRange( descr, LOWORD(lParam),
2839 HIWORD(lParam), wParam );
2841 return LISTBOX_SelectItemRange( descr, HIWORD(lParam),
2842 LOWORD(lParam), wParam );
2844 case LB_SELITEMRANGEEX16:
2845 case LB_SELITEMRANGEEX:
2846 if ((INT)lParam >= (INT)wParam)
2847 return LISTBOX_SelectItemRange( descr, wParam, lParam, TRUE );
2849 return LISTBOX_SelectItemRange( descr, lParam, wParam, FALSE);
2851 case LB_GETHORIZONTALEXTENT16:
2852 case LB_GETHORIZONTALEXTENT:
2853 return descr->horz_extent;
2855 case LB_SETHORIZONTALEXTENT16:
2856 case LB_SETHORIZONTALEXTENT:
2857 return LISTBOX_SetHorizontalExtent( descr, wParam );
2859 case LB_GETANCHORINDEX16:
2860 case LB_GETANCHORINDEX:
2861 return descr->anchor_item;
2863 case LB_SETANCHORINDEX16:
2864 wParam = (INT)(INT16)wParam;
2866 case LB_SETANCHORINDEX:
2867 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2869 descr->anchor_item = (INT)wParam;
2873 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2874 * be set automatically (this is different in Win32) */
2875 if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2876 lParam = (LPARAM)MapSL(lParam);
2883 textW = (LPWSTR)lParam;
2886 LPSTR textA = (LPSTR)lParam;
2887 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2888 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2889 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2891 ret = LISTBOX_Directory( descr, wParam, textW, msg == LB_DIR );
2893 HeapFree(GetProcessHeap(), 0, textW);
2898 return descr->locale;
2901 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2904 case LB_INITSTORAGE:
2905 return LISTBOX_InitStorage( descr, wParam );
2908 return LISTBOX_SetCount( descr, (INT)wParam );
2910 case LB_SETTABSTOPS16:
2911 return LISTBOX_SetTabStops( descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2913 case LB_SETTABSTOPS:
2914 return LISTBOX_SetTabStops( descr, wParam, (LPINT)lParam, FALSE );
2918 if (descr->caret_on)
2920 descr->caret_on = TRUE;
2921 if ((descr->focus_item != -1) && (descr->in_focus))
2922 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2927 if (!descr->caret_on)
2929 descr->caret_on = FALSE;
2930 if ((descr->focus_item != -1) && (descr->in_focus))
2931 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2935 return LISTBOX_Destroy( descr );
2938 InvalidateRect( descr->self, NULL, TRUE );
2942 LISTBOX_SetRedraw( descr, wParam != 0 );
2946 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2951 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( descr->self, &ps );
2952 ret = LISTBOX_Paint( descr, hdc );
2953 if( !wParam ) EndPaint( descr->self, &ps );
2957 LISTBOX_UpdateSize( descr );
2960 return (LRESULT)descr->font;
2962 LISTBOX_SetFont( descr, (HFONT)wParam );
2963 if (lParam) InvalidateRect( descr->self, 0, TRUE );
2966 descr->in_focus = TRUE;
2967 descr->caret_on = TRUE;
2968 if (descr->focus_item != -1)
2969 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2970 SEND_NOTIFICATION( descr, LBN_SETFOCUS );
2973 descr->in_focus = FALSE;
2974 if ((descr->focus_item != -1) && descr->caret_on)
2975 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2976 SEND_NOTIFICATION( descr, LBN_KILLFOCUS );
2979 return LISTBOX_HandleHScroll( descr, LOWORD(wParam), HIWORD(wParam) );
2981 return LISTBOX_HandleVScroll( descr, LOWORD(wParam), HIWORD(wParam) );
2983 if (wParam & (MK_SHIFT | MK_CONTROL))
2984 return DefWindowProcW( descr->self, msg, wParam, lParam );
2985 return LISTBOX_HandleMouseWheel( descr, (SHORT)HIWORD(wParam) );
2986 case WM_LBUTTONDOWN:
2988 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
2989 (INT16)LOWORD(lParam),
2990 (INT16)HIWORD(lParam) );
2991 return LISTBOX_HandleLButtonDown( descr, wParam,
2992 (INT16)LOWORD(lParam),
2993 (INT16)HIWORD(lParam) );
2994 case WM_LBUTTONDBLCLK:
2996 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
2997 (INT16)LOWORD(lParam),
2998 (INT16)HIWORD(lParam) );
2999 if (descr->style & LBS_NOTIFY)
3000 SEND_NOTIFICATION( descr, LBN_DBLCLK );
3003 if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) )
3005 BOOL captured = descr->captured;
3009 mousePos.x = (INT16)LOWORD(lParam);
3010 mousePos.y = (INT16)HIWORD(lParam);
3013 * If we are in a dropdown combobox, we simulate that
3014 * the mouse is captured to show the tracking of the item.
3016 if (GetClientRect(descr->self, &clientRect) && PtInRect( &clientRect, mousePos ))
3017 descr->captured = TRUE;
3019 LISTBOX_HandleMouseMove( descr, mousePos.x, mousePos.y);
3021 descr->captured = captured;
3023 else if (GetCapture() == descr->self)
3025 LISTBOX_HandleMouseMove( descr, (INT16)LOWORD(lParam),
3026 (INT16)HIWORD(lParam) );
3036 * If the mouse button "up" is not in the listbox,
3037 * we make sure there is no selection by re-selecting the
3038 * item that was selected when the listbox was made visible.
3040 mousePos.x = (INT16)LOWORD(lParam);
3041 mousePos.y = (INT16)HIWORD(lParam);
3043 GetClientRect(descr->self, &clientRect);
3046 * When the user clicks outside the combobox and the focus
3047 * is lost, the owning combobox will send a fake buttonup with
3048 * 0xFFFFFFF as the mouse location, we must also revert the
3049 * selection to the original selection.
3051 if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) )
3052 LISTBOX_MoveCaret( descr, lphc->droppedIndex, FALSE );
3054 return LISTBOX_HandleLButtonUp( descr );
3056 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3058 /* for some reason Windows makes it possible to
3059 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3061 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3062 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3063 && (wParam == VK_DOWN || wParam == VK_UP)) )
3065 COMBO_FlipListbox( lphc, FALSE, FALSE );
3069 return LISTBOX_HandleKeyDown( descr, wParam );
3074 charW = (WCHAR)wParam;
3077 CHAR charA = (CHAR)wParam;
3078 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
3080 return LISTBOX_HandleChar( descr, charW );
3083 return LISTBOX_HandleSystemTimer( descr );
3085 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
3088 HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
3089 wParam, (LPARAM)descr->self );
3090 TRACE("hbrush = %p\n", hbrush);
3092 hbrush = GetSysColorBrush(COLOR_WINDOW);
3095 GetClientRect(descr->self, &rect);
3096 FillRect((HDC)wParam, &rect, hbrush);
3101 if( lphc ) return 0;
3102 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3103 SendMessageA( descr->owner, msg, wParam, lParam );
3106 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3115 if ((msg >= WM_USER) && (msg < 0xc000))
3116 WARN("[%p]: unknown msg %04x wp %08x lp %08lx\n",
3117 hwnd, msg, wParam, lParam );
3120 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3121 DefWindowProcA( hwnd, msg, wParam, lParam );
3124 /***********************************************************************
3127 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3129 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3132 /***********************************************************************
3135 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3137 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE );