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 LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect );
133 /*********************************************************************
134 * listbox class descriptor
136 static const WCHAR listboxW[] = {'L','i','s','t','B','o','x',0};
137 const struct builtin_class_descr LISTBOX_builtin_class =
140 CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
141 WINPROC_LISTBOX, /* proc */
142 sizeof(LB_DESCR *), /* extra */
143 IDC_ARROW, /* cursor */
148 /*********************************************************************
149 * combolbox class descriptor
151 static const WCHAR combolboxW[] = {'C','o','m','b','o','L','B','o','x',0};
152 const struct builtin_class_descr COMBOLBOX_builtin_class =
154 combolboxW, /* name */
155 CS_DBLCLKS | CS_SAVEBITS, /* style */
156 WINPROC_LISTBOX, /* proc */
157 sizeof(LB_DESCR *), /* extra */
158 IDC_ARROW, /* cursor */
163 /* check whether app is a Win 3.1 app */
164 static inline BOOL is_old_app( LB_DESCR *descr )
166 return (GetExpWinVer16( GetWindowLongPtrW(descr->self, GWLP_HINSTANCE) ) & 0xFF00 ) == 0x0300;
170 /***********************************************************************
171 * LISTBOX_GetCurrentPageSize
173 * Return the current page size
175 static INT LISTBOX_GetCurrentPageSize( const LB_DESCR *descr )
178 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
179 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
181 if ((height += descr->items[i].height) > descr->height) break;
183 if (i == descr->top_item) return 1;
184 else return i - descr->top_item;
188 /***********************************************************************
189 * LISTBOX_GetMaxTopIndex
191 * Return the maximum possible index for the top of the listbox.
193 static INT LISTBOX_GetMaxTopIndex( const LB_DESCR *descr )
197 if (descr->style & LBS_OWNERDRAWVARIABLE)
199 page = descr->height;
200 for (max = descr->nb_items - 1; max >= 0; max--)
201 if ((page -= descr->items[max].height) < 0) break;
202 if (max < descr->nb_items - 1) max++;
204 else if (descr->style & LBS_MULTICOLUMN)
206 if ((page = descr->width / descr->column_width) < 1) page = 1;
207 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
208 max = (max - page) * descr->page_size;
212 max = descr->nb_items - descr->page_size;
214 if (max < 0) max = 0;
219 /***********************************************************************
220 * LISTBOX_UpdateScroll
222 * Update the scrollbars. Should be called whenever the content
223 * of the listbox changes.
225 static void LISTBOX_UpdateScroll( LB_DESCR *descr )
229 /* Check the listbox scroll bar flags individually before we call
230 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
231 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
232 scroll bar when we do not need one.
233 if (!(descr->style & WS_VSCROLL)) return;
236 /* It is important that we check descr->style, and not wnd->dwStyle,
237 for WS_VSCROLL, as the former is exactly the one passed in
238 argument to CreateWindow.
239 In Windows (and from now on in Wine :) a listbox created
240 with such a style (no WS_SCROLL) does not update
241 the scrollbar with listbox-related data, thus letting
242 the programmer use it for his/her own purposes. */
244 if (descr->style & LBS_NOREDRAW) return;
245 info.cbSize = sizeof(info);
247 if (descr->style & LBS_MULTICOLUMN)
250 info.nMax = (descr->nb_items - 1) / descr->page_size;
251 info.nPos = descr->top_item / descr->page_size;
252 info.nPage = descr->width / descr->column_width;
253 if (info.nPage < 1) info.nPage = 1;
254 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
255 if (descr->style & LBS_DISABLENOSCROLL)
256 info.fMask |= SIF_DISABLENOSCROLL;
257 if (descr->style & WS_HSCROLL)
258 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
260 info.fMask = SIF_RANGE;
261 if (descr->style & WS_VSCROLL)
262 SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
267 info.nMax = descr->nb_items - 1;
268 info.nPos = descr->top_item;
269 info.nPage = LISTBOX_GetCurrentPageSize( descr );
270 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
271 if (descr->style & LBS_DISABLENOSCROLL)
272 info.fMask |= SIF_DISABLENOSCROLL;
273 if (descr->style & WS_VSCROLL)
274 SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
276 if (descr->horz_extent)
279 info.nMax = descr->horz_extent - 1;
280 info.nPos = descr->horz_pos;
281 info.nPage = descr->width;
282 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
283 if (descr->style & LBS_DISABLENOSCROLL)
284 info.fMask |= SIF_DISABLENOSCROLL;
285 if (descr->style & WS_HSCROLL)
286 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
292 /***********************************************************************
295 * Set the top item of the listbox, scrolling up or down if necessary.
297 static LRESULT LISTBOX_SetTopItem( LB_DESCR *descr, INT index, BOOL scroll )
299 INT max = LISTBOX_GetMaxTopIndex( descr );
301 TRACE("setting top item %d, scroll %d\n", index, scroll);
303 if (index > max) index = max;
304 if (index < 0) index = 0;
305 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
306 if (descr->top_item == index) return LB_OKAY;
307 if (descr->style & LBS_MULTICOLUMN)
309 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
310 if (scroll && (abs(diff) < descr->width))
311 ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
312 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
320 if (descr->style & LBS_OWNERDRAWVARIABLE)
324 if (index > descr->top_item)
326 for (i = index - 1; i >= descr->top_item; i--)
327 diff -= descr->items[i].height;
331 for (i = index; i < descr->top_item; i++)
332 diff += descr->items[i].height;
336 diff = (descr->top_item - index) * descr->item_height;
338 if (abs(diff) < descr->height)
339 ScrollWindowEx( descr->self, 0, diff, NULL, NULL, 0, NULL,
340 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
344 if (!scroll) InvalidateRect( descr->self, NULL, TRUE );
345 descr->top_item = index;
346 LISTBOX_UpdateScroll( descr );
351 /***********************************************************************
354 * Update the page size. Should be called when the size of
355 * the client area or the item height changes.
357 static void LISTBOX_UpdatePage( LB_DESCR *descr )
361 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
363 if (page_size == descr->page_size) return;
364 descr->page_size = page_size;
365 if (descr->style & LBS_MULTICOLUMN)
366 InvalidateRect( descr->self, NULL, TRUE );
367 LISTBOX_SetTopItem( descr, descr->top_item, FALSE );
371 /***********************************************************************
374 * Update the size of the listbox. Should be called when the size of
375 * the client area changes.
377 static void LISTBOX_UpdateSize( LB_DESCR *descr )
381 GetClientRect( descr->self, &rect );
382 descr->width = rect.right - rect.left;
383 descr->height = rect.bottom - rect.top;
384 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
389 GetWindowRect( descr->self, &rect );
390 if(descr->item_height != 0)
391 remaining = descr->height % descr->item_height;
394 if ((descr->height > descr->item_height) && remaining)
396 if (is_old_app(descr))
397 { /* give a margin for error to 16 bits programs - if we need
398 less than the height of the nonclient area, round to the
399 *next* number of items */
400 int ncheight = rect.bottom - rect.top - descr->height;
401 if ((descr->item_height - remaining) <= ncheight)
402 remaining = remaining - descr->item_height;
404 TRACE("[%p]: changing height %d -> %d\n",
405 descr->self, descr->height, descr->height - remaining );
406 SetWindowPos( descr->self, 0, 0, 0, rect.right - rect.left,
407 rect.bottom - rect.top - remaining,
408 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
412 TRACE("[%p]: new size = %d,%d\n", descr->self, descr->width, descr->height );
413 LISTBOX_UpdatePage( descr );
414 LISTBOX_UpdateScroll( descr );
416 /* Invalidate the focused item so it will be repainted correctly */
417 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
419 InvalidateRect( descr->self, &rect, FALSE );
424 /***********************************************************************
425 * LISTBOX_GetItemRect
427 * Get the rectangle enclosing an item, in listbox client coordinates.
428 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
430 static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect )
432 /* Index <= 0 is legal even on empty listboxes */
433 if (index && (index >= descr->nb_items))
435 memset(rect, 0, sizeof(*rect));
436 SetLastError(ERROR_INVALID_INDEX);
439 SetRect( rect, 0, 0, descr->width, descr->height );
440 if (descr->style & LBS_MULTICOLUMN)
442 INT col = (index / descr->page_size) -
443 (descr->top_item / descr->page_size);
444 rect->left += col * descr->column_width;
445 rect->right = rect->left + descr->column_width;
446 rect->top += (index % descr->page_size) * descr->item_height;
447 rect->bottom = rect->top + descr->item_height;
449 else if (descr->style & LBS_OWNERDRAWVARIABLE)
452 rect->right += descr->horz_pos;
453 if ((index >= 0) && (index < descr->nb_items))
455 if (index < descr->top_item)
457 for (i = descr->top_item-1; i >= index; i--)
458 rect->top -= descr->items[i].height;
462 for (i = descr->top_item; i < index; i++)
463 rect->top += descr->items[i].height;
465 rect->bottom = rect->top + descr->items[index].height;
471 rect->top += (index - descr->top_item) * descr->item_height;
472 rect->bottom = rect->top + descr->item_height;
473 rect->right += descr->horz_pos;
476 TRACE("item %d, rect %s\n", index, wine_dbgstr_rect(rect));
478 return ((rect->left < descr->width) && (rect->right > 0) &&
479 (rect->top < descr->height) && (rect->bottom > 0));
483 /***********************************************************************
484 * LISTBOX_GetItemFromPoint
486 * Return the item nearest from point (x,y) (in client coordinates).
488 static INT LISTBOX_GetItemFromPoint( const LB_DESCR *descr, INT x, INT y )
490 INT index = descr->top_item;
492 if (!descr->nb_items) return -1; /* No items */
493 if (descr->style & LBS_OWNERDRAWVARIABLE)
498 while (index < descr->nb_items)
500 if ((pos += descr->items[index].height) > y) break;
509 if ((pos -= descr->items[index].height) <= y) break;
513 else if (descr->style & LBS_MULTICOLUMN)
515 if (y >= descr->item_height * descr->page_size) return -1;
516 if (y >= 0) index += y / descr->item_height;
517 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
518 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
522 index += (y / descr->item_height);
524 if (index < 0) return 0;
525 if (index >= descr->nb_items) return -1;
530 /***********************************************************************
535 static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect,
536 INT index, UINT action, BOOL ignoreFocus )
538 LB_ITEMDATA *item = NULL;
539 if (index < descr->nb_items) item = &descr->items[index];
541 if (IS_OWNERDRAW(descr))
549 if (action == ODA_FOCUS)
550 DrawFocusRect( hdc, rect );
552 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
556 /* some programs mess with the clipping region when
557 drawing the item, *and* restore the previous region
558 after they are done, so a region has better to exist
559 else everything ends clipped */
560 GetClientRect(descr->self, &r);
561 hrgn = CreateRectRgnIndirect(&r);
562 SelectClipRgn( hdc, hrgn);
563 DeleteObject( hrgn );
565 dis.CtlType = ODT_LISTBOX;
566 dis.CtlID = GetWindowLongPtrW( descr->self, GWLP_ID );
567 dis.hwndItem = descr->self;
568 dis.itemAction = action;
572 if (item->selected) dis.itemState |= ODS_SELECTED;
573 if (!ignoreFocus && (descr->focus_item == index) &&
575 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
576 if (!IsWindowEnabled(descr->self)) dis.itemState |= ODS_DISABLED;
577 dis.itemData = item->data;
579 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
580 descr->self, index, item ? debugstr_w(item->str) : "", action,
581 dis.itemState, wine_dbgstr_rect(rect) );
582 SendMessageW(descr->owner, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
586 COLORREF oldText = 0, oldBk = 0;
588 if (action == ODA_FOCUS)
590 DrawFocusRect( hdc, rect );
593 if (item && item->selected)
595 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
596 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
599 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
600 descr->self, index, item ? debugstr_w(item->str) : "", action,
601 wine_dbgstr_rect(rect) );
603 ExtTextOutW( hdc, rect->left + 1, rect->top,
604 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
605 else if (!(descr->style & LBS_USETABSTOPS))
606 ExtTextOutW( hdc, rect->left + 1, rect->top,
607 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
608 strlenW(item->str), NULL );
611 /* Output empty string to paint background in the full width. */
612 ExtTextOutW( hdc, rect->left + 1, rect->top,
613 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
614 TabbedTextOutW( hdc, rect->left + 1 , rect->top,
615 item->str, strlenW(item->str),
616 descr->nb_tabs, descr->tabs, 0);
618 if (item && item->selected)
620 SetBkColor( hdc, oldBk );
621 SetTextColor( hdc, oldText );
623 if (!ignoreFocus && (descr->focus_item == index) &&
625 (descr->in_focus)) DrawFocusRect( hdc, rect );
630 /***********************************************************************
633 * Change the redraw flag.
635 static void LISTBOX_SetRedraw( LB_DESCR *descr, BOOL on )
639 if (!(descr->style & LBS_NOREDRAW)) return;
640 descr->style &= ~LBS_NOREDRAW;
641 if (descr->style & LBS_DISPLAYCHANGED)
642 { /* page was changed while setredraw false, refresh automatically */
643 InvalidateRect(descr->self, NULL, TRUE);
644 if ((descr->top_item + descr->page_size) > descr->nb_items)
645 { /* reset top of page if less than number of items/page */
646 descr->top_item = descr->nb_items - descr->page_size;
647 if (descr->top_item < 0) descr->top_item = 0;
649 descr->style &= ~LBS_DISPLAYCHANGED;
651 LISTBOX_UpdateScroll( descr );
653 else descr->style |= LBS_NOREDRAW;
657 /***********************************************************************
658 * LISTBOX_RepaintItem
660 * Repaint a single item synchronously.
662 static void LISTBOX_RepaintItem( LB_DESCR *descr, INT index, UINT action )
667 HBRUSH hbrush, oldBrush = 0;
669 /* Do not repaint the item if the item is not visible */
670 if (!IsWindowVisible(descr->self)) return;
671 if (descr->style & LBS_NOREDRAW)
673 descr->style |= LBS_DISPLAYCHANGED;
676 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
677 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
678 if (descr->font) oldFont = SelectObject( hdc, descr->font );
679 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
680 (WPARAM)hdc, (LPARAM)descr->self );
681 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
682 if (!IsWindowEnabled(descr->self))
683 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
684 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
685 LISTBOX_PaintItem( descr, hdc, &rect, index, action, TRUE );
686 if (oldFont) SelectObject( hdc, oldFont );
687 if (oldBrush) SelectObject( hdc, oldBrush );
688 ReleaseDC( descr->self, hdc );
692 /***********************************************************************
693 * LISTBOX_DrawFocusRect
695 static void LISTBOX_DrawFocusRect( LB_DESCR *descr, BOOL on )
701 /* Do not repaint the item if the item is not visible */
702 if (!IsWindowVisible(descr->self)) return;
704 if (descr->focus_item == -1) return;
705 if (!descr->caret_on || !descr->in_focus) return;
707 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) != 1) return;
708 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
709 if (descr->font) oldFont = SelectObject( hdc, descr->font );
710 if (!IsWindowEnabled(descr->self))
711 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
712 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
713 LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, on ? FALSE : TRUE );
714 if (oldFont) SelectObject( hdc, oldFont );
715 ReleaseDC( descr->self, hdc );
719 /***********************************************************************
720 * LISTBOX_InitStorage
722 static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items )
726 nb_items += LB_ARRAY_GRANULARITY - 1;
727 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
729 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
730 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
731 nb_items * sizeof(LB_ITEMDATA));
734 item = HeapAlloc( GetProcessHeap(), 0,
735 nb_items * sizeof(LB_ITEMDATA));
740 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
748 /***********************************************************************
749 * LISTBOX_SetTabStops
751 static BOOL LISTBOX_SetTabStops( LB_DESCR *descr, INT count, LPINT tabs )
755 if (!(descr->style & LBS_USETABSTOPS))
757 SetLastError(ERROR_LB_WITHOUT_TABSTOPS);
761 HeapFree( GetProcessHeap(), 0, descr->tabs );
762 if (!(descr->nb_tabs = count))
767 if (!(descr->tabs = HeapAlloc( GetProcessHeap(), 0,
768 descr->nb_tabs * sizeof(INT) )))
770 memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
772 /* convert into "dialog units"*/
773 for (i = 0; i < descr->nb_tabs; i++)
774 descr->tabs[i] = MulDiv(descr->tabs[i], descr->avg_char_width, 4);
780 /***********************************************************************
783 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPWSTR buffer, BOOL unicode )
787 if ((index < 0) || (index >= descr->nb_items))
789 SetLastError(ERROR_INVALID_INDEX);
792 if (HAS_STRINGS(descr))
796 len = strlenW(descr->items[index].str);
799 return WideCharToMultiByte( CP_ACP, 0, descr->items[index].str, len,
800 NULL, 0, NULL, NULL );
803 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
805 __TRY /* hide a Delphi bug that passes a read-only buffer */
809 strcpyW( buffer, descr->items[index].str );
810 len = strlenW(buffer);
814 len = WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1,
815 (LPSTR)buffer, 0x7FFFFFFF, NULL, NULL) - 1;
820 WARN( "got an invalid buffer (Delphi bug?)\n" );
821 SetLastError( ERROR_INVALID_PARAMETER );
827 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
833 static inline INT LISTBOX_lstrcmpiW( LCID lcid, LPCWSTR str1, LPCWSTR str2 )
835 INT ret = CompareStringW( lcid, NORM_IGNORECASE, str1, -1, str2, -1 );
836 if (ret == CSTR_LESS_THAN)
838 if (ret == CSTR_EQUAL)
840 if (ret == CSTR_GREATER_THAN)
845 /***********************************************************************
846 * LISTBOX_FindStringPos
848 * Find the nearest string located before a given string in sort order.
849 * If 'exact' is TRUE, return an error if we don't get an exact match.
851 static INT LISTBOX_FindStringPos( LB_DESCR *descr, LPCWSTR str, BOOL exact )
853 INT index, min, max, res = -1;
855 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
857 max = descr->nb_items;
860 index = (min + max) / 2;
861 if (HAS_STRINGS(descr))
862 res = LISTBOX_lstrcmpiW( descr->locale, str, descr->items[index].str);
865 COMPAREITEMSTRUCT cis;
866 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
868 cis.CtlType = ODT_LISTBOX;
870 cis.hwndItem = descr->self;
871 /* note that some application (MetaStock) expects the second item
872 * to be in the listbox */
874 cis.itemData1 = (ULONG_PTR)str;
876 cis.itemData2 = descr->items[index].data;
877 cis.dwLocaleId = descr->locale;
878 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
880 if (!res) return index;
881 if (res < 0) max = index;
882 else min = index + 1;
884 return exact ? -1 : max;
888 /***********************************************************************
889 * LISTBOX_FindFileStrPos
891 * Find the nearest string located before a given string in directory
892 * sort order (i.e. first files, then directories, then drives).
894 static INT LISTBOX_FindFileStrPos( LB_DESCR *descr, LPCWSTR str )
896 INT min, max, res = -1;
898 if (!HAS_STRINGS(descr))
899 return LISTBOX_FindStringPos( descr, str, FALSE );
901 max = descr->nb_items;
904 INT index = (min + max) / 2;
905 LPCWSTR p = descr->items[index].str;
906 if (*p == '[') /* drive or directory */
908 if (*str != '[') res = -1;
909 else if (p[1] == '-') /* drive */
911 if (str[1] == '-') res = str[2] - p[2];
916 if (str[1] == '-') res = 1;
917 else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
922 if (*str == '[') res = 1;
923 else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
925 if (!res) return index;
926 if (res < 0) max = index;
927 else min = index + 1;
933 /***********************************************************************
936 * Find the item beginning with a given string.
938 static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exact )
943 if (start >= descr->nb_items) start = -1;
944 item = descr->items + start + 1;
945 if (HAS_STRINGS(descr))
947 if (!str || ! str[0] ) return LB_ERR;
950 for (i = start + 1; i < descr->nb_items; i++, item++)
951 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
952 for (i = 0, item = descr->items; i <= start; i++, item++)
953 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
957 /* Special case for drives and directories: ignore prefix */
958 #define CHECK_DRIVE(item) \
959 if ((item)->str[0] == '[') \
961 if (!strncmpiW( str, (item)->str+1, len )) return i; \
962 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
966 INT len = strlenW(str);
967 for (i = start + 1; i < descr->nb_items; i++, item++)
969 if (!strncmpiW( str, item->str, len )) return i;
972 for (i = 0, item = descr->items; i <= start; i++, item++)
974 if (!strncmpiW( str, item->str, len )) return i;
982 if (exact && (descr->style & LBS_SORT))
983 /* If sorted, use a WM_COMPAREITEM binary search */
984 return LISTBOX_FindStringPos( descr, str, TRUE );
986 /* Otherwise use a linear search */
987 for (i = start + 1; i < descr->nb_items; i++, item++)
988 if (item->data == (ULONG_PTR)str) return i;
989 for (i = 0, item = descr->items; i <= start; i++, item++)
990 if (item->data == (ULONG_PTR)str) return i;
996 /***********************************************************************
997 * LISTBOX_GetSelCount
999 static LRESULT LISTBOX_GetSelCount( const LB_DESCR *descr )
1002 const LB_ITEMDATA *item = descr->items;
1004 if (!(descr->style & LBS_MULTIPLESEL) ||
1005 (descr->style & LBS_NOSEL))
1007 for (i = count = 0; i < descr->nb_items; i++, item++)
1008 if (item->selected) count++;
1013 /***********************************************************************
1014 * LISTBOX_GetSelItems
1016 static LRESULT LISTBOX_GetSelItems( const LB_DESCR *descr, INT max, LPINT array )
1019 const LB_ITEMDATA *item = descr->items;
1021 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1022 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
1023 if (item->selected) array[count++] = i;
1028 /***********************************************************************
1031 static LRESULT LISTBOX_Paint( LB_DESCR *descr, HDC hdc )
1033 INT i, col_pos = descr->page_size - 1;
1035 RECT focusRect = {-1, -1, -1, -1};
1037 HBRUSH hbrush, oldBrush = 0;
1039 if (descr->style & LBS_NOREDRAW) return 0;
1041 SetRect( &rect, 0, 0, descr->width, descr->height );
1042 if (descr->style & LBS_MULTICOLUMN)
1043 rect.right = rect.left + descr->column_width;
1044 else if (descr->horz_pos)
1046 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
1047 rect.right += descr->horz_pos;
1050 if (descr->font) oldFont = SelectObject( hdc, descr->font );
1051 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1052 (WPARAM)hdc, (LPARAM)descr->self );
1053 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1054 if (!IsWindowEnabled(descr->self)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1056 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1059 /* Special case for empty listbox: paint focus rect */
1060 rect.bottom = rect.top + descr->item_height;
1061 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1062 &rect, NULL, 0, NULL );
1063 LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, FALSE );
1064 rect.top = rect.bottom;
1067 /* Paint all the item, regarding the selection
1068 Focus state will be painted after */
1070 for (i = descr->top_item; i < descr->nb_items; i++)
1072 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1073 rect.bottom = rect.top + descr->item_height;
1075 rect.bottom = rect.top + descr->items[i].height;
1077 if (i == descr->focus_item)
1079 /* keep the focus rect, to paint the focus item after */
1080 focusRect.left = rect.left;
1081 focusRect.right = rect.right;
1082 focusRect.top = rect.top;
1083 focusRect.bottom = rect.bottom;
1085 LISTBOX_PaintItem( descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1086 rect.top = rect.bottom;
1088 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1090 if (!IS_OWNERDRAW(descr))
1092 /* Clear the bottom of the column */
1093 if (rect.top < descr->height)
1095 rect.bottom = descr->height;
1096 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1097 &rect, NULL, 0, NULL );
1101 /* Go to the next column */
1102 rect.left += descr->column_width;
1103 rect.right += descr->column_width;
1105 col_pos = descr->page_size - 1;
1110 if (rect.top >= descr->height) break;
1114 /* Paint the focus item now */
1115 if (focusRect.top != focusRect.bottom &&
1116 descr->caret_on && descr->in_focus)
1117 LISTBOX_PaintItem( descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1119 if (!IS_OWNERDRAW(descr))
1121 /* Clear the remainder of the client area */
1122 if (rect.top < descr->height)
1124 rect.bottom = descr->height;
1125 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1126 &rect, NULL, 0, NULL );
1128 if (rect.right < descr->width)
1130 rect.left = rect.right;
1131 rect.right = descr->width;
1133 rect.bottom = descr->height;
1134 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1135 &rect, NULL, 0, NULL );
1138 if (oldFont) SelectObject( hdc, oldFont );
1139 if (oldBrush) SelectObject( hdc, oldBrush );
1144 /***********************************************************************
1145 * LISTBOX_InvalidateItems
1147 * Invalidate all items from a given item. If the specified item is not
1148 * visible, nothing happens.
1150 static void LISTBOX_InvalidateItems( LB_DESCR *descr, INT index )
1154 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1156 if (descr->style & LBS_NOREDRAW)
1158 descr->style |= LBS_DISPLAYCHANGED;
1161 rect.bottom = descr->height;
1162 InvalidateRect( descr->self, &rect, TRUE );
1163 if (descr->style & LBS_MULTICOLUMN)
1165 /* Repaint the other columns */
1166 rect.left = rect.right;
1167 rect.right = descr->width;
1169 InvalidateRect( descr->self, &rect, TRUE );
1174 static void LISTBOX_InvalidateItemRect( LB_DESCR *descr, INT index )
1178 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1179 InvalidateRect( descr->self, &rect, TRUE );
1182 /***********************************************************************
1183 * LISTBOX_GetItemHeight
1185 static LRESULT LISTBOX_GetItemHeight( const LB_DESCR *descr, INT index )
1187 if (descr->style & LBS_OWNERDRAWVARIABLE && descr->nb_items > 0)
1189 if ((index < 0) || (index >= descr->nb_items))
1191 SetLastError(ERROR_INVALID_INDEX);
1194 return descr->items[index].height;
1196 else return descr->item_height;
1200 /***********************************************************************
1201 * LISTBOX_SetItemHeight
1203 static LRESULT LISTBOX_SetItemHeight( LB_DESCR *descr, INT index, INT height, BOOL repaint )
1205 if (height > MAXBYTE)
1208 if (!height) height = 1;
1210 if (descr->style & LBS_OWNERDRAWVARIABLE)
1212 if ((index < 0) || (index >= descr->nb_items))
1214 SetLastError(ERROR_INVALID_INDEX);
1217 TRACE("[%p]: item %d height = %d\n", descr->self, index, height );
1218 descr->items[index].height = height;
1219 LISTBOX_UpdateScroll( descr );
1221 LISTBOX_InvalidateItems( descr, index );
1223 else if (height != descr->item_height)
1225 TRACE("[%p]: new height = %d\n", descr->self, height );
1226 descr->item_height = height;
1227 LISTBOX_UpdatePage( descr );
1228 LISTBOX_UpdateScroll( descr );
1230 InvalidateRect( descr->self, 0, TRUE );
1236 /***********************************************************************
1237 * LISTBOX_SetHorizontalPos
1239 static void LISTBOX_SetHorizontalPos( LB_DESCR *descr, INT pos )
1243 if (pos > descr->horz_extent - descr->width)
1244 pos = descr->horz_extent - descr->width;
1245 if (pos < 0) pos = 0;
1246 if (!(diff = descr->horz_pos - pos)) return;
1247 TRACE("[%p]: new horz pos = %d\n", descr->self, pos );
1248 descr->horz_pos = pos;
1249 LISTBOX_UpdateScroll( descr );
1250 if (abs(diff) < descr->width)
1253 /* Invalidate the focused item so it will be repainted correctly */
1254 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
1255 InvalidateRect( descr->self, &rect, TRUE );
1256 ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
1257 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1260 InvalidateRect( descr->self, NULL, TRUE );
1264 /***********************************************************************
1265 * LISTBOX_SetHorizontalExtent
1267 static LRESULT LISTBOX_SetHorizontalExtent( LB_DESCR *descr, INT extent )
1269 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1271 if (extent <= 0) extent = 1;
1272 if (extent == descr->horz_extent) return LB_OKAY;
1273 TRACE("[%p]: new horz extent = %d\n", descr->self, extent );
1274 descr->horz_extent = extent;
1275 if (descr->horz_pos > extent - descr->width)
1276 LISTBOX_SetHorizontalPos( descr, extent - descr->width );
1278 LISTBOX_UpdateScroll( descr );
1283 /***********************************************************************
1284 * LISTBOX_SetColumnWidth
1286 static LRESULT LISTBOX_SetColumnWidth( LB_DESCR *descr, INT width)
1288 if (width == descr->column_width) return LB_OKAY;
1289 TRACE("[%p]: new column width = %d\n", descr->self, width );
1290 descr->column_width = width;
1291 LISTBOX_UpdatePage( descr );
1296 /***********************************************************************
1299 * Returns the item height.
1301 static INT LISTBOX_SetFont( LB_DESCR *descr, HFONT font )
1305 const char *alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1310 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE )))
1312 ERR("unable to get DC.\n" );
1315 if (font) oldFont = SelectObject( hdc, font );
1316 GetTextExtentPointA( hdc, alphabet, 52, &sz);
1317 if (oldFont) SelectObject( hdc, oldFont );
1318 ReleaseDC( descr->self, hdc );
1320 descr->avg_char_width = (sz.cx / 26 + 1) / 2;
1321 if (!IS_OWNERDRAW(descr))
1322 LISTBOX_SetItemHeight( descr, 0, sz.cy, FALSE );
1327 /***********************************************************************
1328 * LISTBOX_MakeItemVisible
1330 * Make sure that a given item is partially or fully visible.
1332 static void LISTBOX_MakeItemVisible( LB_DESCR *descr, INT index, BOOL fully )
1336 TRACE("current top item %d, index %d, fully %d\n", descr->top_item, index, fully);
1338 if (index <= descr->top_item) top = index;
1339 else if (descr->style & LBS_MULTICOLUMN)
1341 INT cols = descr->width;
1342 if (!fully) cols += descr->column_width - 1;
1343 if (cols >= descr->column_width) cols /= descr->column_width;
1345 if (index < descr->top_item + (descr->page_size * cols)) return;
1346 top = index - descr->page_size * (cols - 1);
1348 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1350 INT height = fully ? descr->items[index].height : 1;
1351 for (top = index; top > descr->top_item; top--)
1352 if ((height += descr->items[top-1].height) > descr->height) break;
1356 if (index < descr->top_item + descr->page_size) return;
1357 if (!fully && (index == descr->top_item + descr->page_size) &&
1358 (descr->height > (descr->page_size * descr->item_height))) return;
1359 top = index - descr->page_size + 1;
1361 LISTBOX_SetTopItem( descr, top, TRUE );
1364 /***********************************************************************
1365 * LISTBOX_SetCaretIndex
1368 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1371 static LRESULT LISTBOX_SetCaretIndex( LB_DESCR *descr, INT index, BOOL fully_visible )
1373 INT oldfocus = descr->focus_item;
1375 TRACE("old focus %d, index %d\n", oldfocus, index);
1377 if (descr->style & LBS_NOSEL) return LB_ERR;
1378 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1379 if (index == oldfocus) return LB_OKAY;
1381 LISTBOX_DrawFocusRect( descr, FALSE );
1382 descr->focus_item = index;
1384 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1385 LISTBOX_DrawFocusRect( descr, TRUE );
1391 /***********************************************************************
1392 * LISTBOX_SelectItemRange
1394 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1396 static LRESULT LISTBOX_SelectItemRange( LB_DESCR *descr, INT first,
1401 /* A few sanity checks */
1403 if (descr->style & LBS_NOSEL) return LB_ERR;
1404 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1406 if (!descr->nb_items) return LB_OKAY;
1408 if (last == -1 || last >= descr->nb_items) last = descr->nb_items - 1;
1409 if (first < 0) first = 0;
1410 if (last < first) return LB_OKAY;
1412 if (on) /* Turn selection on */
1414 for (i = first; i <= last; i++)
1416 if (descr->items[i].selected) continue;
1417 descr->items[i].selected = TRUE;
1418 LISTBOX_InvalidateItemRect(descr, i);
1421 else /* Turn selection off */
1423 for (i = first; i <= last; i++)
1425 if (!descr->items[i].selected) continue;
1426 descr->items[i].selected = FALSE;
1427 LISTBOX_InvalidateItemRect(descr, i);
1433 /***********************************************************************
1434 * LISTBOX_SetSelection
1436 static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index,
1437 BOOL on, BOOL send_notify )
1439 TRACE( "cur_sel=%d index=%d notify=%s\n",
1440 descr->selected_item, index, send_notify ? "YES" : "NO" );
1442 if (descr->style & LBS_NOSEL)
1444 descr->selected_item = index;
1447 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1448 if (descr->style & LBS_MULTIPLESEL)
1450 if (index == -1) /* Select all items */
1451 return LISTBOX_SelectItemRange( descr, 0, descr->nb_items, on );
1452 else /* Only one item */
1453 return LISTBOX_SelectItemRange( descr, index, index, on );
1457 INT oldsel = descr->selected_item;
1458 if (index == oldsel) return LB_OKAY;
1459 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1460 if (index != -1) descr->items[index].selected = TRUE;
1461 if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT );
1462 descr->selected_item = index;
1463 if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT );
1464 if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr,
1465 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1467 if( descr->lphc ) /* set selection change flag for parent combo */
1468 descr->lphc->wState |= CBF_SELCHANGE;
1474 /***********************************************************************
1477 * Change the caret position and extend the selection to the new caret.
1479 static void LISTBOX_MoveCaret( LB_DESCR *descr, INT index, BOOL fully_visible )
1481 TRACE("old focus %d, index %d\n", descr->focus_item, index);
1483 if ((index < 0) || (index >= descr->nb_items))
1486 /* Important, repaint needs to be done in this order if
1487 you want to mimic Windows behavior:
1488 1. Remove the focus and paint the item
1489 2. Remove the selection and paint the item(s)
1490 3. Set the selection and repaint the item(s)
1491 4. Set the focus to 'index' and repaint the item */
1493 /* 1. remove the focus and repaint the item */
1494 LISTBOX_DrawFocusRect( descr, FALSE );
1496 /* 2. then turn off the previous selection */
1497 /* 3. repaint the new selected item */
1498 if (descr->style & LBS_EXTENDEDSEL)
1500 if (descr->anchor_item != -1)
1502 INT first = min( index, descr->anchor_item );
1503 INT last = max( index, descr->anchor_item );
1505 LISTBOX_SelectItemRange( descr, 0, first - 1, FALSE );
1506 LISTBOX_SelectItemRange( descr, last + 1, -1, FALSE );
1507 LISTBOX_SelectItemRange( descr, first, last, TRUE );
1510 else if (!(descr->style & LBS_MULTIPLESEL))
1512 /* Set selection to new caret item */
1513 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
1516 /* 4. repaint the new item with the focus */
1517 descr->focus_item = index;
1518 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1519 LISTBOX_DrawFocusRect( descr, TRUE );
1523 /***********************************************************************
1524 * LISTBOX_InsertItem
1526 static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index,
1527 LPWSTR str, ULONG_PTR data )
1531 INT oldfocus = descr->focus_item;
1533 if (index == -1) index = descr->nb_items;
1534 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1535 if (!descr->items) max_items = 0;
1536 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1537 if (descr->nb_items == max_items)
1539 /* We need to grow the array */
1540 max_items += LB_ARRAY_GRANULARITY;
1542 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1543 max_items * sizeof(LB_ITEMDATA) );
1545 item = HeapAlloc( GetProcessHeap(), 0,
1546 max_items * sizeof(LB_ITEMDATA) );
1549 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1552 descr->items = item;
1555 /* Insert the item structure */
1557 item = &descr->items[index];
1558 if (index < descr->nb_items)
1559 RtlMoveMemory( item + 1, item,
1560 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1564 item->selected = FALSE;
1567 /* Get item height */
1569 if (descr->style & LBS_OWNERDRAWVARIABLE)
1571 MEASUREITEMSTRUCT mis;
1572 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1574 mis.CtlType = ODT_LISTBOX;
1577 mis.itemData = descr->items[index].data;
1578 mis.itemHeight = descr->item_height;
1579 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1580 item->height = mis.itemHeight ? mis.itemHeight : 1;
1581 TRACE("[%p]: measure item %d (%s) = %d\n",
1582 descr->self, index, str ? debugstr_w(str) : "", item->height );
1585 /* Repaint the items */
1587 LISTBOX_UpdateScroll( descr );
1588 LISTBOX_InvalidateItems( descr, index );
1590 /* Move selection and focused item */
1591 /* If listbox was empty, set focus to the first item */
1592 if (descr->nb_items == 1)
1593 LISTBOX_SetCaretIndex( descr, 0, FALSE );
1594 /* single select don't change selection index in win31 */
1595 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1597 descr->selected_item++;
1598 LISTBOX_SetSelection( descr, descr->selected_item-1, TRUE, FALSE );
1602 if (index <= descr->selected_item)
1604 descr->selected_item++;
1605 descr->focus_item = oldfocus; /* focus not changed */
1612 /***********************************************************************
1613 * LISTBOX_InsertString
1615 static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index, LPCWSTR str )
1617 LPWSTR new_str = NULL;
1621 if (HAS_STRINGS(descr))
1623 static const WCHAR empty_stringW[] = { 0 };
1624 if (!str) str = empty_stringW;
1625 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1627 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1630 strcpyW(new_str, str);
1632 else data = (ULONG_PTR)str;
1634 if (index == -1) index = descr->nb_items;
1635 if ((ret = LISTBOX_InsertItem( descr, index, new_str, data )) != 0)
1637 HeapFree( GetProcessHeap(), 0, new_str );
1641 TRACE("[%p]: added item %d %s\n",
1642 descr->self, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1647 /***********************************************************************
1648 * LISTBOX_DeleteItem
1650 * Delete the content of an item. 'index' must be a valid index.
1652 static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index )
1654 /* save the item data before it gets freed by LB_RESETCONTENT */
1655 ULONG_PTR item_data = descr->items[index].data;
1656 LPWSTR item_str = descr->items[index].str;
1658 if (!descr->nb_items)
1659 SendMessageW( descr->self, LB_RESETCONTENT, 0, 0 );
1661 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1662 * while Win95 sends it for all items with user data.
1663 * It's probably better to send it too often than not
1664 * often enough, so this is what we do here.
1666 if (IS_OWNERDRAW(descr) || item_data)
1668 DELETEITEMSTRUCT dis;
1669 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1671 dis.CtlType = ODT_LISTBOX;
1674 dis.hwndItem = descr->self;
1675 dis.itemData = item_data;
1676 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1678 if (HAS_STRINGS(descr))
1679 HeapFree( GetProcessHeap(), 0, item_str );
1683 /***********************************************************************
1684 * LISTBOX_RemoveItem
1686 * Remove an item from the listbox and delete its content.
1688 static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index )
1693 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1695 /* We need to invalidate the original rect instead of the updated one. */
1696 LISTBOX_InvalidateItems( descr, index );
1699 LISTBOX_DeleteItem( descr, index );
1701 if (!descr->nb_items) return LB_OKAY;
1703 /* Remove the item */
1705 item = &descr->items[index];
1706 if (index < descr->nb_items)
1707 RtlMoveMemory( item, item + 1,
1708 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1709 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1711 /* Shrink the item array if possible */
1713 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1714 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1716 max_items -= LB_ARRAY_GRANULARITY;
1717 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1718 max_items * sizeof(LB_ITEMDATA) );
1719 if (item) descr->items = item;
1721 /* Repaint the items */
1723 LISTBOX_UpdateScroll( descr );
1724 /* if we removed the scrollbar, reset the top of the list
1725 (correct for owner-drawn ???) */
1726 if (descr->nb_items == descr->page_size)
1727 LISTBOX_SetTopItem( descr, 0, TRUE );
1729 /* Move selection and focused item */
1730 if (!IS_MULTISELECT(descr))
1732 if (index == descr->selected_item)
1733 descr->selected_item = -1;
1734 else if (index < descr->selected_item)
1736 descr->selected_item--;
1737 if (ISWIN31) /* win 31 do not change the selected item number */
1738 LISTBOX_SetSelection( descr, descr->selected_item + 1, TRUE, FALSE);
1742 if (descr->focus_item >= descr->nb_items)
1744 descr->focus_item = descr->nb_items - 1;
1745 if (descr->focus_item < 0) descr->focus_item = 0;
1751 /***********************************************************************
1752 * LISTBOX_ResetContent
1754 static void LISTBOX_ResetContent( LB_DESCR *descr )
1758 for(i = descr->nb_items - 1; i>=0; i--) LISTBOX_DeleteItem( descr, i);
1759 HeapFree( GetProcessHeap(), 0, descr->items );
1760 descr->nb_items = 0;
1761 descr->top_item = 0;
1762 descr->selected_item = -1;
1763 descr->focus_item = 0;
1764 descr->anchor_item = -1;
1765 descr->items = NULL;
1769 /***********************************************************************
1772 static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count )
1776 if (HAS_STRINGS(descr))
1778 SetLastError(ERROR_SETCOUNT_ON_BAD_LB);
1782 /* FIXME: this is far from optimal... */
1783 if (count > descr->nb_items)
1785 while (count > descr->nb_items)
1786 if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0)
1789 else if (count < descr->nb_items)
1791 while (count < descr->nb_items)
1792 if ((ret = LISTBOX_RemoveItem( descr, (descr->nb_items - 1) )) < 0)
1799 /***********************************************************************
1802 static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib,
1803 LPCWSTR filespec, BOOL long_names )
1806 LRESULT ret = LB_OKAY;
1807 WIN32_FIND_DATAW entry;
1809 LRESULT maxinsert = LB_ERR;
1811 /* don't scan directory if we just want drives exclusively */
1812 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1813 /* scan directory */
1814 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1816 int le = GetLastError();
1817 if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR;
1824 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1826 static const WCHAR bracketW[] = { ']',0 };
1827 static const WCHAR dotW[] = { '.',0 };
1828 if (!(attrib & DDL_DIRECTORY) ||
1829 !strcmpW( entry.cFileName, dotW )) continue;
1831 if (!long_names && entry.cAlternateFileName[0])
1832 strcpyW( buffer + 1, entry.cAlternateFileName );
1834 strcpyW( buffer + 1, entry.cFileName );
1835 strcatW(buffer, bracketW);
1837 else /* not a directory */
1839 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1840 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1842 if ((attrib & DDL_EXCLUSIVE) &&
1843 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1846 if (!long_names && entry.cAlternateFileName[0])
1847 strcpyW( buffer, entry.cAlternateFileName );
1849 strcpyW( buffer, entry.cFileName );
1851 if (!long_names) CharLowerW( buffer );
1852 pos = LISTBOX_FindFileStrPos( descr, buffer );
1853 if ((ret = LISTBOX_InsertString( descr, pos, buffer )) < 0)
1855 if (ret <= maxinsert) maxinsert++; else maxinsert = ret;
1856 } while (FindNextFileW( handle, &entry ));
1857 FindClose( handle );
1865 if (attrib & DDL_DRIVES)
1867 WCHAR buffer[] = {'[','-','a','-',']',0};
1868 WCHAR root[] = {'A',':','\\',0};
1870 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1872 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1873 if ((ret = LISTBOX_InsertString( descr, -1, buffer )) < 0)
1882 /***********************************************************************
1883 * LISTBOX_HandleVScroll
1885 static LRESULT LISTBOX_HandleVScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1889 if (descr->style & LBS_MULTICOLUMN) return 0;
1893 LISTBOX_SetTopItem( descr, descr->top_item - 1, TRUE );
1896 LISTBOX_SetTopItem( descr, descr->top_item + 1, TRUE );
1899 LISTBOX_SetTopItem( descr, descr->top_item -
1900 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1903 LISTBOX_SetTopItem( descr, descr->top_item +
1904 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1906 case SB_THUMBPOSITION:
1907 LISTBOX_SetTopItem( descr, pos, TRUE );
1910 info.cbSize = sizeof(info);
1911 info.fMask = SIF_TRACKPOS;
1912 GetScrollInfo( descr->self, SB_VERT, &info );
1913 LISTBOX_SetTopItem( descr, info.nTrackPos, TRUE );
1916 LISTBOX_SetTopItem( descr, 0, TRUE );
1919 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1926 /***********************************************************************
1927 * LISTBOX_HandleHScroll
1929 static LRESULT LISTBOX_HandleHScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1934 if (descr->style & LBS_MULTICOLUMN)
1939 LISTBOX_SetTopItem( descr, descr->top_item-descr->page_size,
1943 LISTBOX_SetTopItem( descr, descr->top_item+descr->page_size,
1947 page = descr->width / descr->column_width;
1948 if (page < 1) page = 1;
1949 LISTBOX_SetTopItem( descr,
1950 descr->top_item - page * descr->page_size, TRUE );
1953 page = descr->width / descr->column_width;
1954 if (page < 1) page = 1;
1955 LISTBOX_SetTopItem( descr,
1956 descr->top_item + page * descr->page_size, TRUE );
1958 case SB_THUMBPOSITION:
1959 LISTBOX_SetTopItem( descr, pos*descr->page_size, TRUE );
1962 info.cbSize = sizeof(info);
1963 info.fMask = SIF_TRACKPOS;
1964 GetScrollInfo( descr->self, SB_VERT, &info );
1965 LISTBOX_SetTopItem( descr, info.nTrackPos*descr->page_size,
1969 LISTBOX_SetTopItem( descr, 0, TRUE );
1972 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1976 else if (descr->horz_extent)
1981 LISTBOX_SetHorizontalPos( descr, descr->horz_pos - 1 );
1984 LISTBOX_SetHorizontalPos( descr, descr->horz_pos + 1 );
1987 LISTBOX_SetHorizontalPos( descr,
1988 descr->horz_pos - descr->width );
1991 LISTBOX_SetHorizontalPos( descr,
1992 descr->horz_pos + descr->width );
1994 case SB_THUMBPOSITION:
1995 LISTBOX_SetHorizontalPos( descr, pos );
1998 info.cbSize = sizeof(info);
1999 info.fMask = SIF_TRACKPOS;
2000 GetScrollInfo( descr->self, SB_HORZ, &info );
2001 LISTBOX_SetHorizontalPos( descr, info.nTrackPos );
2004 LISTBOX_SetHorizontalPos( descr, 0 );
2007 LISTBOX_SetHorizontalPos( descr,
2008 descr->horz_extent - descr->width );
2015 static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta )
2017 short gcWheelDelta = 0;
2018 UINT pulScrollLines = 3;
2020 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
2022 gcWheelDelta -= delta;
2024 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
2026 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
2027 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
2028 LISTBOX_SetTopItem( descr, descr->top_item + cLineScroll, TRUE );
2033 /***********************************************************************
2034 * LISTBOX_HandleLButtonDown
2036 static LRESULT LISTBOX_HandleLButtonDown( LB_DESCR *descr, DWORD keys, INT x, INT y )
2038 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
2040 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2041 descr->self, x, y, index, descr->focus_item);
2043 if (!descr->caret_on && (descr->in_focus)) return 0;
2045 if (!descr->in_focus)
2047 if( !descr->lphc ) SetFocus( descr->self );
2048 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
2051 if (index == -1) return 0;
2055 if (descr->style & LBS_NOTIFY )
2056 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
2057 MAKELPARAM( x, y ) );
2060 descr->captured = TRUE;
2061 SetCapture( descr->self );
2063 if (descr->style & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL))
2065 /* we should perhaps make sure that all items are deselected
2066 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2067 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2068 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2071 if (!(keys & MK_SHIFT)) descr->anchor_item = index;
2072 if (keys & MK_CONTROL)
2074 LISTBOX_SetCaretIndex( descr, index, FALSE );
2075 LISTBOX_SetSelection( descr, index,
2076 !descr->items[index].selected,
2077 (descr->style & LBS_NOTIFY) != 0);
2081 LISTBOX_MoveCaret( descr, index, FALSE );
2083 if (descr->style & LBS_EXTENDEDSEL)
2085 LISTBOX_SetSelection( descr, index,
2086 descr->items[index].selected,
2087 (descr->style & LBS_NOTIFY) != 0 );
2091 LISTBOX_SetSelection( descr, index,
2092 !descr->items[index].selected,
2093 (descr->style & LBS_NOTIFY) != 0 );
2099 descr->anchor_item = index;
2100 LISTBOX_MoveCaret( descr, index, FALSE );
2101 LISTBOX_SetSelection( descr, index,
2102 TRUE, (descr->style & LBS_NOTIFY) != 0 );
2107 if (GetWindowLongW( descr->self, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
2114 if (DragDetect( descr->self, pt ))
2115 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
2122 /*************************************************************************
2123 * LISTBOX_HandleLButtonDownCombo [Internal]
2125 * Process LButtonDown message for the ComboListBox
2128 * pWnd [I] The windows internal structure
2129 * pDescr [I] The ListBox internal structure
2130 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2131 * x [I] X Mouse Coordinate
2132 * y [I] Y Mouse Coordinate
2135 * 0 since we are processing the WM_LBUTTONDOWN Message
2138 * This function is only to be used when a ListBox is a ComboListBox
2141 static LRESULT LISTBOX_HandleLButtonDownCombo( LB_DESCR *descr, UINT msg, DWORD keys, INT x, INT y)
2143 RECT clientRect, screenRect;
2149 GetClientRect(descr->self, &clientRect);
2151 if(PtInRect(&clientRect, mousePos))
2153 /* MousePos is in client, resume normal processing */
2154 if (msg == WM_LBUTTONDOWN)
2156 descr->lphc->droppedIndex = descr->nb_items ? descr->selected_item : -1;
2157 return LISTBOX_HandleLButtonDown( descr, keys, x, y);
2159 else if (descr->style & LBS_NOTIFY)
2160 SEND_NOTIFICATION( descr, LBN_DBLCLK );
2164 POINT screenMousePos;
2165 HWND hWndOldCapture;
2167 /* Check the Non-Client Area */
2168 screenMousePos = mousePos;
2169 hWndOldCapture = GetCapture();
2171 GetWindowRect(descr->self, &screenRect);
2172 ClientToScreen(descr->self, &screenMousePos);
2174 if(!PtInRect(&screenRect, screenMousePos))
2176 LISTBOX_SetCaretIndex( descr, descr->lphc->droppedIndex, FALSE );
2177 LISTBOX_SetSelection( descr, descr->lphc->droppedIndex, FALSE, FALSE );
2178 COMBO_FlipListbox( descr->lphc, FALSE, FALSE );
2182 /* Check to see the NC is a scrollbar */
2184 LONG style = GetWindowLongW( descr->self, GWL_STYLE );
2185 /* Check Vertical scroll bar */
2186 if (style & WS_VSCROLL)
2188 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2189 if (PtInRect( &clientRect, mousePos ))
2190 nHitTestType = HTVSCROLL;
2192 /* Check horizontal scroll bar */
2193 if (style & WS_HSCROLL)
2195 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2196 if (PtInRect( &clientRect, mousePos ))
2197 nHitTestType = HTHSCROLL;
2199 /* Windows sends this message when a scrollbar is clicked
2202 if(nHitTestType != 0)
2204 SendMessageW(descr->self, WM_NCLBUTTONDOWN, nHitTestType,
2205 MAKELONG(screenMousePos.x, screenMousePos.y));
2207 /* Resume the Capture after scrolling is complete
2209 if(hWndOldCapture != 0)
2210 SetCapture(hWndOldCapture);
2216 /***********************************************************************
2217 * LISTBOX_HandleLButtonUp
2219 static LRESULT LISTBOX_HandleLButtonUp( LB_DESCR *descr )
2221 if (LISTBOX_Timer != LB_TIMER_NONE)
2222 KillSystemTimer( descr->self, LB_TIMER_ID );
2223 LISTBOX_Timer = LB_TIMER_NONE;
2224 if (descr->captured)
2226 descr->captured = FALSE;
2227 if (GetCapture() == descr->self) ReleaseCapture();
2228 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2229 SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2235 /***********************************************************************
2236 * LISTBOX_HandleTimer
2238 * Handle scrolling upon a timer event.
2239 * Return TRUE if scrolling should continue.
2241 static LRESULT LISTBOX_HandleTimer( LB_DESCR *descr, INT index, TIMER_DIRECTION dir )
2246 if (descr->top_item) index = descr->top_item - 1;
2250 if (descr->top_item) index -= descr->page_size;
2253 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2254 if (index == descr->focus_item) index++;
2255 if (index >= descr->nb_items) index = descr->nb_items - 1;
2257 case LB_TIMER_RIGHT:
2258 if (index + descr->page_size < descr->nb_items)
2259 index += descr->page_size;
2264 if (index == descr->focus_item) return FALSE;
2265 LISTBOX_MoveCaret( descr, index, FALSE );
2270 /***********************************************************************
2271 * LISTBOX_HandleSystemTimer
2273 * WM_SYSTIMER handler.
2275 static LRESULT LISTBOX_HandleSystemTimer( LB_DESCR *descr )
2277 if (!LISTBOX_HandleTimer( descr, descr->focus_item, LISTBOX_Timer ))
2279 KillSystemTimer( descr->self, LB_TIMER_ID );
2280 LISTBOX_Timer = LB_TIMER_NONE;
2286 /***********************************************************************
2287 * LISTBOX_HandleMouseMove
2289 * WM_MOUSEMOVE handler.
2291 static void LISTBOX_HandleMouseMove( LB_DESCR *descr,
2295 TIMER_DIRECTION dir = LB_TIMER_NONE;
2297 if (!descr->captured) return;
2299 if (descr->style & LBS_MULTICOLUMN)
2302 else if (y >= descr->item_height * descr->page_size)
2303 y = descr->item_height * descr->page_size - 1;
2307 dir = LB_TIMER_LEFT;
2310 else if (x >= descr->width)
2312 dir = LB_TIMER_RIGHT;
2313 x = descr->width - 1;
2318 if (y < 0) dir = LB_TIMER_UP; /* above */
2319 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2322 index = LISTBOX_GetItemFromPoint( descr, x, y );
2323 if (index == -1) index = descr->focus_item;
2324 if (!LISTBOX_HandleTimer( descr, index, dir )) dir = LB_TIMER_NONE;
2326 /* Start/stop the system timer */
2328 if (dir != LB_TIMER_NONE)
2329 SetSystemTimer( descr->self, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2330 else if (LISTBOX_Timer != LB_TIMER_NONE)
2331 KillSystemTimer( descr->self, LB_TIMER_ID );
2332 LISTBOX_Timer = dir;
2336 /***********************************************************************
2337 * LISTBOX_HandleKeyDown
2339 static LRESULT LISTBOX_HandleKeyDown( LB_DESCR *descr, DWORD key )
2342 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2343 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2344 bForceSelection = FALSE; /* only for single select list */
2346 if (descr->style & LBS_WANTKEYBOARDINPUT)
2348 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2349 MAKEWPARAM(LOWORD(key), descr->focus_item),
2350 (LPARAM)descr->self );
2351 if (caret == -2) return 0;
2353 if (caret == -1) switch(key)
2356 if (descr->style & LBS_MULTICOLUMN)
2358 bForceSelection = FALSE;
2359 if (descr->focus_item >= descr->page_size)
2360 caret = descr->focus_item - descr->page_size;
2365 caret = descr->focus_item - 1;
2366 if (caret < 0) caret = 0;
2369 if (descr->style & LBS_MULTICOLUMN)
2371 bForceSelection = FALSE;
2372 if (descr->focus_item + descr->page_size < descr->nb_items)
2373 caret = descr->focus_item + descr->page_size;
2378 caret = descr->focus_item + 1;
2379 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2383 if (descr->style & LBS_MULTICOLUMN)
2385 INT page = descr->width / descr->column_width;
2386 if (page < 1) page = 1;
2387 caret = descr->focus_item - (page * descr->page_size) + 1;
2389 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2390 if (caret < 0) caret = 0;
2393 if (descr->style & LBS_MULTICOLUMN)
2395 INT page = descr->width / descr->column_width;
2396 if (page < 1) page = 1;
2397 caret = descr->focus_item + (page * descr->page_size) - 1;
2399 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2400 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2406 caret = descr->nb_items - 1;
2409 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2410 else if (descr->style & LBS_MULTIPLESEL)
2412 LISTBOX_SetSelection( descr, descr->focus_item,
2413 !descr->items[descr->focus_item].selected,
2414 (descr->style & LBS_NOTIFY) != 0 );
2418 bForceSelection = FALSE;
2420 if (bForceSelection) /* focused item is used instead of key */
2421 caret = descr->focus_item;
2424 if (((descr->style & LBS_EXTENDEDSEL) &&
2425 !(GetKeyState( VK_SHIFT ) & 0x8000)) ||
2426 !IS_MULTISELECT(descr))
2427 descr->anchor_item = caret;
2428 LISTBOX_MoveCaret( descr, caret, TRUE );
2430 if (descr->style & LBS_MULTIPLESEL)
2431 descr->selected_item = caret;
2433 LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2434 if (descr->style & LBS_NOTIFY)
2436 if (descr->lphc && IsWindowVisible( descr->self ))
2438 /* make sure that combo parent doesn't hide us */
2439 descr->lphc->wState |= CBF_NOROLLUP;
2441 if (descr->nb_items) SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2448 /***********************************************************************
2449 * LISTBOX_HandleChar
2451 static LRESULT LISTBOX_HandleChar( LB_DESCR *descr, WCHAR charW )
2459 if (descr->style & LBS_WANTKEYBOARDINPUT)
2461 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2462 MAKEWPARAM(charW, descr->focus_item),
2463 (LPARAM)descr->self );
2464 if (caret == -2) return 0;
2467 caret = LISTBOX_FindString( descr, descr->focus_item, str, FALSE);
2470 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2471 LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2472 LISTBOX_MoveCaret( descr, caret, TRUE );
2473 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2474 SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2480 /***********************************************************************
2483 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2486 MEASUREITEMSTRUCT mis;
2489 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2492 GetClientRect( hwnd, &rect );
2494 descr->owner = GetParent( descr->self );
2495 descr->style = GetWindowLongW( descr->self, GWL_STYLE );
2496 descr->width = rect.right - rect.left;
2497 descr->height = rect.bottom - rect.top;
2498 descr->items = NULL;
2499 descr->nb_items = 0;
2500 descr->top_item = 0;
2501 descr->selected_item = -1;
2502 descr->focus_item = 0;
2503 descr->anchor_item = -1;
2504 descr->item_height = 1;
2505 descr->page_size = 1;
2506 descr->column_width = 150;
2507 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0;
2508 descr->horz_pos = 0;
2511 descr->caret_on = lphc ? FALSE : TRUE;
2512 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2513 descr->in_focus = FALSE;
2514 descr->captured = FALSE;
2516 descr->locale = GetUserDefaultLCID();
2519 if (is_old_app(descr) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2521 /* Win95 document "List Box Differences" from MSDN:
2522 If a list box in a version 3.x application has either the
2523 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2524 horizontal and vertical scroll bars.
2526 descr->style |= WS_VSCROLL | WS_HSCROLL;
2531 TRACE("[%p]: resetting owner %p -> %p\n", descr->self, descr->owner, lphc->self );
2532 descr->owner = lphc->self;
2535 SetWindowLongPtrW( descr->self, 0, (LONG_PTR)descr );
2537 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2539 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2540 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2541 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2542 descr->item_height = LISTBOX_SetFont( descr, 0 );
2544 if (descr->style & LBS_OWNERDRAWFIXED)
2546 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2548 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2549 descr->item_height = lphc->fixedOwnerDrawHeight;
2553 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
2554 mis.CtlType = ODT_LISTBOX;
2559 mis.itemHeight = descr->item_height;
2560 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2561 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2565 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2570 /***********************************************************************
2573 static BOOL LISTBOX_Destroy( LB_DESCR *descr )
2575 LISTBOX_ResetContent( descr );
2576 SetWindowLongPtrW( descr->self, 0, 0 );
2577 HeapFree( GetProcessHeap(), 0, descr );
2582 /***********************************************************************
2583 * ListBoxWndProc_common
2585 LRESULT ListBoxWndProc_common( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, BOOL unicode )
2587 LB_DESCR *descr = (LB_DESCR *)GetWindowLongPtrW( hwnd, 0 );
2588 LPHEADCOMBO lphc = 0;
2593 if (!IsWindow(hwnd)) return 0;
2595 if (msg == WM_CREATE)
2597 CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam;
2598 if (lpcs->style & LBS_COMBOBOX) lphc = lpcs->lpCreateParams;
2599 if (!LISTBOX_Create( hwnd, lphc )) return -1;
2600 TRACE("creating hwnd %p descr %p\n", hwnd, (void *)GetWindowLongPtrW( hwnd, 0 ) );
2603 /* Ignore all other messages before we get a WM_CREATE */
2604 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2605 DefWindowProcA( hwnd, msg, wParam, lParam );
2607 if (descr->style & LBS_COMBOBOX) lphc = descr->lphc;
2609 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2610 descr->self, SPY_GetMsgName(msg, descr->self), wParam, lParam );
2614 case LB_RESETCONTENT:
2615 LISTBOX_ResetContent( descr );
2616 LISTBOX_UpdateScroll( descr );
2617 InvalidateRect( descr->self, NULL, TRUE );
2624 if(unicode || !HAS_STRINGS(descr))
2625 textW = (LPWSTR)lParam;
2628 LPSTR textA = (LPSTR)lParam;
2629 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2630 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2631 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2635 wParam = LISTBOX_FindStringPos( descr, textW, FALSE );
2636 ret = LISTBOX_InsertString( descr, wParam, textW );
2637 if (!unicode && HAS_STRINGS(descr))
2638 HeapFree(GetProcessHeap(), 0, textW);
2642 case LB_INSERTSTRING:
2646 if(unicode || !HAS_STRINGS(descr))
2647 textW = (LPWSTR)lParam;
2650 LPSTR textA = (LPSTR)lParam;
2651 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2652 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2653 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2657 ret = LISTBOX_InsertString( descr, wParam, textW );
2658 if(!unicode && HAS_STRINGS(descr))
2659 HeapFree(GetProcessHeap(), 0, textW);
2667 if(unicode || !HAS_STRINGS(descr))
2668 textW = (LPWSTR)lParam;
2671 LPSTR textA = (LPSTR)lParam;
2672 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2673 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2674 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2678 wParam = LISTBOX_FindFileStrPos( descr, textW );
2679 ret = LISTBOX_InsertString( descr, wParam, textW );
2680 if(!unicode && HAS_STRINGS(descr))
2681 HeapFree(GetProcessHeap(), 0, textW);
2685 case LB_DELETESTRING:
2686 if (LISTBOX_RemoveItem( descr, wParam) != LB_ERR)
2687 return descr->nb_items;
2690 SetLastError(ERROR_INVALID_INDEX);
2694 case LB_GETITEMDATA:
2695 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2697 SetLastError(ERROR_INVALID_INDEX);
2700 return descr->items[wParam].data;
2702 case LB_SETITEMDATA:
2703 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2705 SetLastError(ERROR_INVALID_INDEX);
2708 descr->items[wParam].data = lParam;
2709 /* undocumented: returns TRUE, not LB_OKAY (0) */
2713 return descr->nb_items;
2716 return LISTBOX_GetText( descr, wParam, (LPWSTR)lParam, unicode );
2719 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2721 SetLastError(ERROR_INVALID_INDEX);
2724 if (!HAS_STRINGS(descr)) return sizeof(DWORD);
2725 if (unicode) return strlenW( descr->items[wParam].str );
2726 return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
2727 strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
2730 if (descr->nb_items == 0)
2732 if (!IS_MULTISELECT(descr))
2733 return descr->selected_item;
2734 if (descr->selected_item != -1)
2735 return descr->selected_item;
2736 return descr->focus_item;
2737 /* otherwise, if the user tries to move the selection with the */
2738 /* arrow keys, we will give the application something to choke on */
2739 case LB_GETTOPINDEX:
2740 return descr->top_item;
2742 case LB_GETITEMHEIGHT:
2743 return LISTBOX_GetItemHeight( descr, wParam );
2745 case LB_SETITEMHEIGHT:
2746 return LISTBOX_SetItemHeight( descr, wParam, lParam, TRUE );
2748 case LB_ITEMFROMPOINT:
2755 /* The hiword of the return value is not a client area
2756 hittest as suggested by MSDN, but rather a hittest on
2757 the returned listbox item. */
2759 if(descr->nb_items == 0)
2760 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2762 pt.x = (short)LOWORD(lParam);
2763 pt.y = (short)HIWORD(lParam);
2765 SetRect(&rect, 0, 0, descr->width, descr->height);
2767 if(!PtInRect(&rect, pt))
2769 pt.x = min(pt.x, rect.right - 1);
2770 pt.x = max(pt.x, 0);
2771 pt.y = min(pt.y, rect.bottom - 1);
2772 pt.y = max(pt.y, 0);
2776 index = LISTBOX_GetItemFromPoint(descr, pt.x, pt.y);
2780 index = descr->nb_items - 1;
2783 return MAKELONG(index, hit ? 0 : 1);
2786 case LB_SETCARETINDEX:
2787 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2788 if (LISTBOX_SetCaretIndex( descr, wParam, !lParam ) == LB_ERR)
2795 case LB_GETCARETINDEX:
2796 return descr->focus_item;
2798 case LB_SETTOPINDEX:
2799 return LISTBOX_SetTopItem( descr, wParam, TRUE );
2801 case LB_SETCOLUMNWIDTH:
2802 return LISTBOX_SetColumnWidth( descr, wParam );
2804 case LB_GETITEMRECT:
2805 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2811 if(unicode || !HAS_STRINGS(descr))
2812 textW = (LPWSTR)lParam;
2815 LPSTR textA = (LPSTR)lParam;
2816 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2817 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2818 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2820 ret = LISTBOX_FindString( descr, wParam, textW, FALSE );
2821 if(!unicode && HAS_STRINGS(descr))
2822 HeapFree(GetProcessHeap(), 0, textW);
2826 case LB_FINDSTRINGEXACT:
2830 if(unicode || !HAS_STRINGS(descr))
2831 textW = (LPWSTR)lParam;
2834 LPSTR textA = (LPSTR)lParam;
2835 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2836 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2837 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2839 ret = LISTBOX_FindString( descr, wParam, textW, TRUE );
2840 if(!unicode && HAS_STRINGS(descr))
2841 HeapFree(GetProcessHeap(), 0, textW);
2845 case LB_SELECTSTRING:
2850 if(HAS_STRINGS(descr))
2851 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2852 debugstr_a((LPSTR)lParam));
2853 if(unicode || !HAS_STRINGS(descr))
2854 textW = (LPWSTR)lParam;
2857 LPSTR textA = (LPSTR)lParam;
2858 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2859 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2860 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2862 index = LISTBOX_FindString( descr, wParam, textW, FALSE );
2863 if(!unicode && HAS_STRINGS(descr))
2864 HeapFree(GetProcessHeap(), 0, textW);
2865 if (index != LB_ERR)
2867 LISTBOX_MoveCaret( descr, index, TRUE );
2868 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
2874 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2876 return descr->items[wParam].selected;
2879 return LISTBOX_SetSelection( descr, lParam, wParam, FALSE );
2882 if (IS_MULTISELECT(descr)) return LB_ERR;
2883 LISTBOX_SetCaretIndex( descr, wParam, FALSE );
2884 ret = LISTBOX_SetSelection( descr, wParam, TRUE, FALSE );
2885 if (ret != LB_ERR) ret = descr->selected_item;
2888 case LB_GETSELCOUNT:
2889 return LISTBOX_GetSelCount( descr );
2891 case LB_GETSELITEMS:
2892 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2894 case LB_SELITEMRANGE:
2895 if (LOWORD(lParam) <= HIWORD(lParam))
2896 return LISTBOX_SelectItemRange( descr, LOWORD(lParam),
2897 HIWORD(lParam), wParam );
2899 return LISTBOX_SelectItemRange( descr, HIWORD(lParam),
2900 LOWORD(lParam), wParam );
2902 case LB_SELITEMRANGEEX:
2903 if ((INT)lParam >= (INT)wParam)
2904 return LISTBOX_SelectItemRange( descr, wParam, lParam, TRUE );
2906 return LISTBOX_SelectItemRange( descr, lParam, wParam, FALSE);
2908 case LB_GETHORIZONTALEXTENT:
2909 return descr->horz_extent;
2911 case LB_SETHORIZONTALEXTENT:
2912 return LISTBOX_SetHorizontalExtent( descr, wParam );
2914 case LB_GETANCHORINDEX:
2915 return descr->anchor_item;
2917 case LB_SETANCHORINDEX:
2918 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2920 SetLastError(ERROR_INVALID_INDEX);
2923 descr->anchor_item = (INT)wParam;
2931 textW = (LPWSTR)lParam;
2934 LPSTR textA = (LPSTR)lParam;
2935 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2936 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2937 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2939 ret = LISTBOX_Directory( descr, wParam, textW, msg == LB_DIR );
2941 HeapFree(GetProcessHeap(), 0, textW);
2946 return descr->locale;
2951 if (!IsValidLocale((LCID)wParam, LCID_INSTALLED))
2953 ret = descr->locale;
2954 descr->locale = (LCID)wParam;
2958 case LB_INITSTORAGE:
2959 return LISTBOX_InitStorage( descr, wParam );
2962 return LISTBOX_SetCount( descr, (INT)wParam );
2964 case LB_SETTABSTOPS:
2965 return LISTBOX_SetTabStops( descr, wParam, (LPINT)lParam );
2968 if (descr->caret_on)
2970 descr->caret_on = TRUE;
2971 if ((descr->focus_item != -1) && (descr->in_focus))
2972 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2976 if (!descr->caret_on)
2978 descr->caret_on = FALSE;
2979 if ((descr->focus_item != -1) && (descr->in_focus))
2980 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2983 case LB_GETLISTBOXINFO:
2984 FIXME("LB_GETLISTBOXINFO: stub!\n");
2988 return LISTBOX_Destroy( descr );
2991 InvalidateRect( descr->self, NULL, TRUE );
2995 LISTBOX_SetRedraw( descr, wParam != 0 );
2999 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3001 case WM_PRINTCLIENT:
3005 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( descr->self, &ps );
3006 ret = LISTBOX_Paint( descr, hdc );
3007 if( !wParam ) EndPaint( descr->self, &ps );
3011 LISTBOX_UpdateSize( descr );
3014 return (LRESULT)descr->font;
3016 LISTBOX_SetFont( descr, (HFONT)wParam );
3017 if (lParam) InvalidateRect( descr->self, 0, TRUE );
3020 descr->in_focus = TRUE;
3021 descr->caret_on = TRUE;
3022 if (descr->focus_item != -1)
3023 LISTBOX_DrawFocusRect( descr, TRUE );
3024 SEND_NOTIFICATION( descr, LBN_SETFOCUS );
3027 descr->in_focus = FALSE;
3028 if ((descr->focus_item != -1) && descr->caret_on)
3029 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3030 SEND_NOTIFICATION( descr, LBN_KILLFOCUS );
3033 return LISTBOX_HandleHScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3035 return LISTBOX_HandleVScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3037 if (wParam & (MK_SHIFT | MK_CONTROL))
3038 return DefWindowProcW( descr->self, msg, wParam, lParam );
3039 return LISTBOX_HandleMouseWheel( descr, (SHORT)HIWORD(wParam) );
3040 case WM_LBUTTONDOWN:
3042 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3043 (INT16)LOWORD(lParam),
3044 (INT16)HIWORD(lParam) );
3045 return LISTBOX_HandleLButtonDown( descr, wParam,
3046 (INT16)LOWORD(lParam),
3047 (INT16)HIWORD(lParam) );
3048 case WM_LBUTTONDBLCLK:
3050 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3051 (INT16)LOWORD(lParam),
3052 (INT16)HIWORD(lParam) );
3053 if (descr->style & LBS_NOTIFY)
3054 SEND_NOTIFICATION( descr, LBN_DBLCLK );
3057 if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) )
3059 BOOL captured = descr->captured;
3063 mousePos.x = (INT16)LOWORD(lParam);
3064 mousePos.y = (INT16)HIWORD(lParam);
3067 * If we are in a dropdown combobox, we simulate that
3068 * the mouse is captured to show the tracking of the item.
3070 if (GetClientRect(descr->self, &clientRect) && PtInRect( &clientRect, mousePos ))
3071 descr->captured = TRUE;
3073 LISTBOX_HandleMouseMove( descr, mousePos.x, mousePos.y);
3075 descr->captured = captured;
3077 else if (GetCapture() == descr->self)
3079 LISTBOX_HandleMouseMove( descr, (INT16)LOWORD(lParam),
3080 (INT16)HIWORD(lParam) );
3090 * If the mouse button "up" is not in the listbox,
3091 * we make sure there is no selection by re-selecting the
3092 * item that was selected when the listbox was made visible.
3094 mousePos.x = (INT16)LOWORD(lParam);
3095 mousePos.y = (INT16)HIWORD(lParam);
3097 GetClientRect(descr->self, &clientRect);
3100 * When the user clicks outside the combobox and the focus
3101 * is lost, the owning combobox will send a fake buttonup with
3102 * 0xFFFFFFF as the mouse location, we must also revert the
3103 * selection to the original selection.
3105 if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) )
3106 LISTBOX_MoveCaret( descr, lphc->droppedIndex, FALSE );
3108 return LISTBOX_HandleLButtonUp( descr );
3110 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3112 /* for some reason Windows makes it possible to
3113 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3115 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3116 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3117 && (wParam == VK_DOWN || wParam == VK_UP)) )
3119 COMBO_FlipListbox( lphc, FALSE, FALSE );
3123 return LISTBOX_HandleKeyDown( descr, wParam );
3128 charW = (WCHAR)wParam;
3131 CHAR charA = (CHAR)wParam;
3132 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
3134 return LISTBOX_HandleChar( descr, charW );
3137 return LISTBOX_HandleSystemTimer( descr );
3139 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
3142 HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
3143 wParam, (LPARAM)descr->self );
3144 TRACE("hbrush = %p\n", hbrush);
3146 hbrush = GetSysColorBrush(COLOR_WINDOW);
3149 GetClientRect(descr->self, &rect);
3150 FillRect((HDC)wParam, &rect, hbrush);
3155 if( lphc ) return 0;
3156 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3157 SendMessageA( descr->owner, msg, wParam, lParam );
3160 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3169 if ((msg >= WM_USER) && (msg < 0xc000))
3170 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3171 hwnd, msg, wParam, lParam );
3174 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3175 DefWindowProcA( hwnd, msg, wParam, lParam );