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 */
142 BUILTIN_WINPROC(WINPROC_LISTBOX), /* procW */
143 sizeof(LB_DESCR *), /* extra */
144 IDC_ARROW, /* cursor */
149 /*********************************************************************
150 * combolbox class descriptor
152 static const WCHAR combolboxW[] = {'C','o','m','b','o','L','B','o','x',0};
153 const struct builtin_class_descr COMBOLBOX_builtin_class =
155 combolboxW, /* name */
156 CS_DBLCLKS | CS_SAVEBITS, /* style */
158 BUILTIN_WINPROC(WINPROC_LISTBOX), /* procW */
159 sizeof(LB_DESCR *), /* extra */
160 IDC_ARROW, /* cursor */
165 /* check whether app is a Win 3.1 app */
166 static inline BOOL is_old_app( LB_DESCR *descr )
168 return (GetExpWinVer16( GetWindowLongPtrW(descr->self, GWLP_HINSTANCE) ) & 0xFF00 ) == 0x0300;
172 /***********************************************************************
173 * LISTBOX_GetCurrentPageSize
175 * Return the current page size
177 static INT LISTBOX_GetCurrentPageSize( const LB_DESCR *descr )
180 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
181 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
183 if ((height += descr->items[i].height) > descr->height) break;
185 if (i == descr->top_item) return 1;
186 else return i - descr->top_item;
190 /***********************************************************************
191 * LISTBOX_GetMaxTopIndex
193 * Return the maximum possible index for the top of the listbox.
195 static INT LISTBOX_GetMaxTopIndex( const LB_DESCR *descr )
199 if (descr->style & LBS_OWNERDRAWVARIABLE)
201 page = descr->height;
202 for (max = descr->nb_items - 1; max >= 0; max--)
203 if ((page -= descr->items[max].height) < 0) break;
204 if (max < descr->nb_items - 1) max++;
206 else if (descr->style & LBS_MULTICOLUMN)
208 if ((page = descr->width / descr->column_width) < 1) page = 1;
209 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
210 max = (max - page) * descr->page_size;
214 max = descr->nb_items - descr->page_size;
216 if (max < 0) max = 0;
221 /***********************************************************************
222 * LISTBOX_UpdateScroll
224 * Update the scrollbars. Should be called whenever the content
225 * of the listbox changes.
227 static void LISTBOX_UpdateScroll( LB_DESCR *descr )
231 /* Check the listbox scroll bar flags individually before we call
232 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
233 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
234 scroll bar when we do not need one.
235 if (!(descr->style & WS_VSCROLL)) return;
238 /* It is important that we check descr->style, and not wnd->dwStyle,
239 for WS_VSCROLL, as the former is exactly the one passed in
240 argument to CreateWindow.
241 In Windows (and from now on in Wine :) a listbox created
242 with such a style (no WS_SCROLL) does not update
243 the scrollbar with listbox-related data, thus letting
244 the programmer use it for his/her own purposes. */
246 if (descr->style & LBS_NOREDRAW) return;
247 info.cbSize = sizeof(info);
249 if (descr->style & LBS_MULTICOLUMN)
252 info.nMax = (descr->nb_items - 1) / descr->page_size;
253 info.nPos = descr->top_item / descr->page_size;
254 info.nPage = descr->width / descr->column_width;
255 if (info.nPage < 1) info.nPage = 1;
256 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
257 if (descr->style & LBS_DISABLENOSCROLL)
258 info.fMask |= SIF_DISABLENOSCROLL;
259 if (descr->style & WS_HSCROLL)
260 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
262 info.fMask = SIF_RANGE;
263 if (descr->style & WS_VSCROLL)
264 SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
269 info.nMax = descr->nb_items - 1;
270 info.nPos = descr->top_item;
271 info.nPage = LISTBOX_GetCurrentPageSize( descr );
272 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
273 if (descr->style & LBS_DISABLENOSCROLL)
274 info.fMask |= SIF_DISABLENOSCROLL;
275 if (descr->style & WS_VSCROLL)
276 SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
278 if (descr->horz_extent)
281 info.nMax = descr->horz_extent - 1;
282 info.nPos = descr->horz_pos;
283 info.nPage = descr->width;
284 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
285 if (descr->style & LBS_DISABLENOSCROLL)
286 info.fMask |= SIF_DISABLENOSCROLL;
287 if (descr->style & WS_HSCROLL)
288 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
294 /***********************************************************************
297 * Set the top item of the listbox, scrolling up or down if necessary.
299 static LRESULT LISTBOX_SetTopItem( LB_DESCR *descr, INT index, BOOL scroll )
301 INT max = LISTBOX_GetMaxTopIndex( descr );
303 TRACE("setting top item %d, scroll %d\n", index, scroll);
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( const LB_DESCR *descr, INT index, RECT *rect )
434 /* Index <= 0 is legal even on empty listboxes */
435 if (index && (index >= descr->nb_items))
437 memset(rect, 0, sizeof(*rect));
438 SetLastError(ERROR_INVALID_INDEX);
441 SetRect( rect, 0, 0, descr->width, descr->height );
442 if (descr->style & LBS_MULTICOLUMN)
444 INT col = (index / descr->page_size) -
445 (descr->top_item / descr->page_size);
446 rect->left += col * descr->column_width;
447 rect->right = rect->left + descr->column_width;
448 rect->top += (index % descr->page_size) * descr->item_height;
449 rect->bottom = rect->top + descr->item_height;
451 else if (descr->style & LBS_OWNERDRAWVARIABLE)
454 rect->right += descr->horz_pos;
455 if ((index >= 0) && (index < descr->nb_items))
457 if (index < descr->top_item)
459 for (i = descr->top_item-1; i >= index; i--)
460 rect->top -= descr->items[i].height;
464 for (i = descr->top_item; i < index; i++)
465 rect->top += descr->items[i].height;
467 rect->bottom = rect->top + descr->items[index].height;
473 rect->top += (index - descr->top_item) * descr->item_height;
474 rect->bottom = rect->top + descr->item_height;
475 rect->right += descr->horz_pos;
478 TRACE("item %d, rect %s\n", index, wine_dbgstr_rect(rect));
480 return ((rect->left < descr->width) && (rect->right > 0) &&
481 (rect->top < descr->height) && (rect->bottom > 0));
485 /***********************************************************************
486 * LISTBOX_GetItemFromPoint
488 * Return the item nearest from point (x,y) (in client coordinates).
490 static INT LISTBOX_GetItemFromPoint( const LB_DESCR *descr, INT x, INT y )
492 INT index = descr->top_item;
494 if (!descr->nb_items) return -1; /* No items */
495 if (descr->style & LBS_OWNERDRAWVARIABLE)
500 while (index < descr->nb_items)
502 if ((pos += descr->items[index].height) > y) break;
511 if ((pos -= descr->items[index].height) <= y) break;
515 else if (descr->style & LBS_MULTICOLUMN)
517 if (y >= descr->item_height * descr->page_size) return -1;
518 if (y >= 0) index += y / descr->item_height;
519 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
520 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
524 index += (y / descr->item_height);
526 if (index < 0) return 0;
527 if (index >= descr->nb_items) return -1;
532 /***********************************************************************
537 static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect,
538 INT index, UINT action, BOOL ignoreFocus )
540 LB_ITEMDATA *item = NULL;
541 if (index < descr->nb_items) item = &descr->items[index];
543 if (IS_OWNERDRAW(descr))
551 if (action == ODA_FOCUS)
552 DrawFocusRect( hdc, rect );
554 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
558 /* some programs mess with the clipping region when
559 drawing the item, *and* restore the previous region
560 after they are done, so a region has better to exist
561 else everything ends clipped */
562 GetClientRect(descr->self, &r);
563 hrgn = CreateRectRgnIndirect(&r);
564 SelectClipRgn( hdc, hrgn);
565 DeleteObject( hrgn );
567 dis.CtlType = ODT_LISTBOX;
568 dis.CtlID = GetWindowLongPtrW( descr->self, GWLP_ID );
569 dis.hwndItem = descr->self;
570 dis.itemAction = action;
574 if (item->selected) dis.itemState |= ODS_SELECTED;
575 if (!ignoreFocus && (descr->focus_item == index) &&
577 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
578 if (!IsWindowEnabled(descr->self)) dis.itemState |= ODS_DISABLED;
579 dis.itemData = item->data;
581 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
582 descr->self, index, item ? debugstr_w(item->str) : "", action,
583 dis.itemState, wine_dbgstr_rect(rect) );
584 SendMessageW(descr->owner, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
588 COLORREF oldText = 0, oldBk = 0;
590 if (action == ODA_FOCUS)
592 DrawFocusRect( hdc, rect );
595 if (item && item->selected)
597 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
598 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
601 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
602 descr->self, index, item ? debugstr_w(item->str) : "", action,
603 wine_dbgstr_rect(rect) );
605 ExtTextOutW( hdc, rect->left + 1, rect->top,
606 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
607 else if (!(descr->style & LBS_USETABSTOPS))
608 ExtTextOutW( hdc, rect->left + 1, rect->top,
609 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
610 strlenW(item->str), NULL );
613 /* Output empty string to paint background in the full width. */
614 ExtTextOutW( hdc, rect->left + 1, rect->top,
615 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
616 TabbedTextOutW( hdc, rect->left + 1 , rect->top,
617 item->str, strlenW(item->str),
618 descr->nb_tabs, descr->tabs, 0);
620 if (item && item->selected)
622 SetBkColor( hdc, oldBk );
623 SetTextColor( hdc, oldText );
625 if (!ignoreFocus && (descr->focus_item == index) &&
627 (descr->in_focus)) DrawFocusRect( hdc, rect );
632 /***********************************************************************
635 * Change the redraw flag.
637 static void LISTBOX_SetRedraw( LB_DESCR *descr, BOOL on )
641 if (!(descr->style & LBS_NOREDRAW)) return;
642 descr->style &= ~LBS_NOREDRAW;
643 if (descr->style & LBS_DISPLAYCHANGED)
644 { /* page was changed while setredraw false, refresh automatically */
645 InvalidateRect(descr->self, NULL, TRUE);
646 if ((descr->top_item + descr->page_size) > descr->nb_items)
647 { /* reset top of page if less than number of items/page */
648 descr->top_item = descr->nb_items - descr->page_size;
649 if (descr->top_item < 0) descr->top_item = 0;
651 descr->style &= ~LBS_DISPLAYCHANGED;
653 LISTBOX_UpdateScroll( descr );
655 else descr->style |= LBS_NOREDRAW;
659 /***********************************************************************
660 * LISTBOX_RepaintItem
662 * Repaint a single item synchronously.
664 static void LISTBOX_RepaintItem( LB_DESCR *descr, INT index, UINT action )
669 HBRUSH hbrush, oldBrush = 0;
671 /* Do not repaint the item if the item is not visible */
672 if (!IsWindowVisible(descr->self)) return;
673 if (descr->style & LBS_NOREDRAW)
675 descr->style |= LBS_DISPLAYCHANGED;
678 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
679 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
680 if (descr->font) oldFont = SelectObject( hdc, descr->font );
681 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
682 (WPARAM)hdc, (LPARAM)descr->self );
683 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
684 if (!IsWindowEnabled(descr->self))
685 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
686 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
687 LISTBOX_PaintItem( descr, hdc, &rect, index, action, TRUE );
688 if (oldFont) SelectObject( hdc, oldFont );
689 if (oldBrush) SelectObject( hdc, oldBrush );
690 ReleaseDC( descr->self, hdc );
694 /***********************************************************************
695 * LISTBOX_DrawFocusRect
697 static void LISTBOX_DrawFocusRect( LB_DESCR *descr, BOOL on )
703 /* Do not repaint the item if the item is not visible */
704 if (!IsWindowVisible(descr->self)) return;
706 if (descr->focus_item == -1) return;
707 if (!descr->caret_on || !descr->in_focus) return;
709 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) != 1) return;
710 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
711 if (descr->font) oldFont = SelectObject( hdc, descr->font );
712 if (!IsWindowEnabled(descr->self))
713 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
714 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
715 LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, on ? FALSE : TRUE );
716 if (oldFont) SelectObject( hdc, oldFont );
717 ReleaseDC( descr->self, hdc );
721 /***********************************************************************
722 * LISTBOX_InitStorage
724 static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items )
728 nb_items += LB_ARRAY_GRANULARITY - 1;
729 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
731 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
732 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
733 nb_items * sizeof(LB_ITEMDATA));
736 item = HeapAlloc( GetProcessHeap(), 0,
737 nb_items * sizeof(LB_ITEMDATA));
742 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
750 /***********************************************************************
751 * LISTBOX_SetTabStops
753 static BOOL LISTBOX_SetTabStops( LB_DESCR *descr, INT count, LPINT tabs )
757 if (!(descr->style & LBS_USETABSTOPS))
759 SetLastError(ERROR_LB_WITHOUT_TABSTOPS);
763 HeapFree( GetProcessHeap(), 0, descr->tabs );
764 if (!(descr->nb_tabs = count))
769 if (!(descr->tabs = HeapAlloc( GetProcessHeap(), 0,
770 descr->nb_tabs * sizeof(INT) )))
772 memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
774 /* convert into "dialog units"*/
775 for (i = 0; i < descr->nb_tabs; i++)
776 descr->tabs[i] = MulDiv(descr->tabs[i], descr->avg_char_width, 4);
782 /***********************************************************************
785 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPWSTR buffer, BOOL unicode )
789 if ((index < 0) || (index >= descr->nb_items))
791 SetLastError(ERROR_INVALID_INDEX);
794 if (HAS_STRINGS(descr))
798 len = strlenW(descr->items[index].str);
801 return WideCharToMultiByte( CP_ACP, 0, descr->items[index].str, len,
802 NULL, 0, NULL, NULL );
805 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
807 __TRY /* hide a Delphi bug that passes a read-only buffer */
811 strcpyW( buffer, descr->items[index].str );
812 len = strlenW(buffer);
816 len = WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1,
817 (LPSTR)buffer, 0x7FFFFFFF, NULL, NULL) - 1;
822 WARN( "got an invalid buffer (Delphi bug?)\n" );
823 SetLastError( ERROR_INVALID_PARAMETER );
829 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
835 static inline INT LISTBOX_lstrcmpiW( LCID lcid, LPCWSTR str1, LPCWSTR str2 )
837 INT ret = CompareStringW( lcid, NORM_IGNORECASE, str1, -1, str2, -1 );
838 if (ret == CSTR_LESS_THAN)
840 if (ret == CSTR_EQUAL)
842 if (ret == CSTR_GREATER_THAN)
847 /***********************************************************************
848 * LISTBOX_FindStringPos
850 * Find the nearest string located before a given string in sort order.
851 * If 'exact' is TRUE, return an error if we don't get an exact match.
853 static INT LISTBOX_FindStringPos( LB_DESCR *descr, LPCWSTR str, BOOL exact )
855 INT index, min, max, res = -1;
857 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
859 max = descr->nb_items;
862 index = (min + max) / 2;
863 if (HAS_STRINGS(descr))
864 res = LISTBOX_lstrcmpiW( descr->locale, str, descr->items[index].str);
867 COMPAREITEMSTRUCT cis;
868 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
870 cis.CtlType = ODT_LISTBOX;
872 cis.hwndItem = descr->self;
873 /* note that some application (MetaStock) expects the second item
874 * to be in the listbox */
876 cis.itemData1 = (ULONG_PTR)str;
878 cis.itemData2 = descr->items[index].data;
879 cis.dwLocaleId = descr->locale;
880 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
882 if (!res) return index;
883 if (res < 0) max = index;
884 else min = index + 1;
886 return exact ? -1 : max;
890 /***********************************************************************
891 * LISTBOX_FindFileStrPos
893 * Find the nearest string located before a given string in directory
894 * sort order (i.e. first files, then directories, then drives).
896 static INT LISTBOX_FindFileStrPos( LB_DESCR *descr, LPCWSTR str )
898 INT min, max, res = -1;
900 if (!HAS_STRINGS(descr))
901 return LISTBOX_FindStringPos( descr, str, FALSE );
903 max = descr->nb_items;
906 INT index = (min + max) / 2;
907 LPCWSTR p = descr->items[index].str;
908 if (*p == '[') /* drive or directory */
910 if (*str != '[') res = -1;
911 else if (p[1] == '-') /* drive */
913 if (str[1] == '-') res = str[2] - p[2];
918 if (str[1] == '-') res = 1;
919 else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
924 if (*str == '[') res = 1;
925 else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
927 if (!res) return index;
928 if (res < 0) max = index;
929 else min = index + 1;
935 /***********************************************************************
938 * Find the item beginning with a given string.
940 static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exact )
945 if (start >= descr->nb_items) start = -1;
946 item = descr->items + start + 1;
947 if (HAS_STRINGS(descr))
949 if (!str || ! str[0] ) return LB_ERR;
952 for (i = start + 1; i < descr->nb_items; i++, item++)
953 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
954 for (i = 0, item = descr->items; i <= start; i++, item++)
955 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
959 /* Special case for drives and directories: ignore prefix */
960 #define CHECK_DRIVE(item) \
961 if ((item)->str[0] == '[') \
963 if (!strncmpiW( str, (item)->str+1, len )) return i; \
964 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
968 INT len = strlenW(str);
969 for (i = start + 1; i < descr->nb_items; i++, item++)
971 if (!strncmpiW( str, item->str, len )) return i;
974 for (i = 0, item = descr->items; i <= start; i++, item++)
976 if (!strncmpiW( str, item->str, len )) return i;
984 if (exact && (descr->style & LBS_SORT))
985 /* If sorted, use a WM_COMPAREITEM binary search */
986 return LISTBOX_FindStringPos( descr, str, TRUE );
988 /* Otherwise use a linear search */
989 for (i = start + 1; i < descr->nb_items; i++, item++)
990 if (item->data == (ULONG_PTR)str) return i;
991 for (i = 0, item = descr->items; i <= start; i++, item++)
992 if (item->data == (ULONG_PTR)str) return i;
998 /***********************************************************************
999 * LISTBOX_GetSelCount
1001 static LRESULT LISTBOX_GetSelCount( const LB_DESCR *descr )
1004 const LB_ITEMDATA *item = descr->items;
1006 if (!(descr->style & LBS_MULTIPLESEL) ||
1007 (descr->style & LBS_NOSEL))
1009 for (i = count = 0; i < descr->nb_items; i++, item++)
1010 if (item->selected) count++;
1015 /***********************************************************************
1016 * LISTBOX_GetSelItems
1018 static LRESULT LISTBOX_GetSelItems( const LB_DESCR *descr, INT max, LPINT array )
1021 const LB_ITEMDATA *item = descr->items;
1023 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1024 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
1025 if (item->selected) array[count++] = i;
1030 /***********************************************************************
1033 static LRESULT LISTBOX_Paint( LB_DESCR *descr, HDC hdc )
1035 INT i, col_pos = descr->page_size - 1;
1037 RECT focusRect = {-1, -1, -1, -1};
1039 HBRUSH hbrush, oldBrush = 0;
1041 if (descr->style & LBS_NOREDRAW) return 0;
1043 SetRect( &rect, 0, 0, descr->width, descr->height );
1044 if (descr->style & LBS_MULTICOLUMN)
1045 rect.right = rect.left + descr->column_width;
1046 else if (descr->horz_pos)
1048 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
1049 rect.right += descr->horz_pos;
1052 if (descr->font) oldFont = SelectObject( hdc, descr->font );
1053 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1054 (WPARAM)hdc, (LPARAM)descr->self );
1055 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1056 if (!IsWindowEnabled(descr->self)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1058 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1061 /* Special case for empty listbox: paint focus rect */
1062 rect.bottom = rect.top + descr->item_height;
1063 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1064 &rect, NULL, 0, NULL );
1065 LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, FALSE );
1066 rect.top = rect.bottom;
1069 /* Paint all the item, regarding the selection
1070 Focus state will be painted after */
1072 for (i = descr->top_item; i < descr->nb_items; i++)
1074 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1075 rect.bottom = rect.top + descr->item_height;
1077 rect.bottom = rect.top + descr->items[i].height;
1079 if (i == descr->focus_item)
1081 /* keep the focus rect, to paint the focus item after */
1082 focusRect.left = rect.left;
1083 focusRect.right = rect.right;
1084 focusRect.top = rect.top;
1085 focusRect.bottom = rect.bottom;
1087 LISTBOX_PaintItem( descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1088 rect.top = rect.bottom;
1090 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1092 if (!IS_OWNERDRAW(descr))
1094 /* Clear the bottom of the column */
1095 if (rect.top < descr->height)
1097 rect.bottom = descr->height;
1098 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1099 &rect, NULL, 0, NULL );
1103 /* Go to the next column */
1104 rect.left += descr->column_width;
1105 rect.right += descr->column_width;
1107 col_pos = descr->page_size - 1;
1112 if (rect.top >= descr->height) break;
1116 /* Paint the focus item now */
1117 if (focusRect.top != focusRect.bottom &&
1118 descr->caret_on && descr->in_focus)
1119 LISTBOX_PaintItem( descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1121 if (!IS_OWNERDRAW(descr))
1123 /* Clear the remainder of the client area */
1124 if (rect.top < descr->height)
1126 rect.bottom = descr->height;
1127 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1128 &rect, NULL, 0, NULL );
1130 if (rect.right < descr->width)
1132 rect.left = rect.right;
1133 rect.right = descr->width;
1135 rect.bottom = descr->height;
1136 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1137 &rect, NULL, 0, NULL );
1140 if (oldFont) SelectObject( hdc, oldFont );
1141 if (oldBrush) SelectObject( hdc, oldBrush );
1146 /***********************************************************************
1147 * LISTBOX_InvalidateItems
1149 * Invalidate all items from a given item. If the specified item is not
1150 * visible, nothing happens.
1152 static void LISTBOX_InvalidateItems( LB_DESCR *descr, INT index )
1156 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1158 if (descr->style & LBS_NOREDRAW)
1160 descr->style |= LBS_DISPLAYCHANGED;
1163 rect.bottom = descr->height;
1164 InvalidateRect( descr->self, &rect, TRUE );
1165 if (descr->style & LBS_MULTICOLUMN)
1167 /* Repaint the other columns */
1168 rect.left = rect.right;
1169 rect.right = descr->width;
1171 InvalidateRect( descr->self, &rect, TRUE );
1176 static void LISTBOX_InvalidateItemRect( LB_DESCR *descr, INT index )
1180 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1181 InvalidateRect( descr->self, &rect, TRUE );
1184 /***********************************************************************
1185 * LISTBOX_GetItemHeight
1187 static LRESULT LISTBOX_GetItemHeight( const LB_DESCR *descr, INT index )
1189 if (descr->style & LBS_OWNERDRAWVARIABLE && descr->nb_items > 0)
1191 if ((index < 0) || (index >= descr->nb_items))
1193 SetLastError(ERROR_INVALID_INDEX);
1196 return descr->items[index].height;
1198 else return descr->item_height;
1202 /***********************************************************************
1203 * LISTBOX_SetItemHeight
1205 static LRESULT LISTBOX_SetItemHeight( LB_DESCR *descr, INT index, INT height, BOOL repaint )
1207 if (height > MAXBYTE)
1210 if (!height) height = 1;
1212 if (descr->style & LBS_OWNERDRAWVARIABLE)
1214 if ((index < 0) || (index >= descr->nb_items))
1216 SetLastError(ERROR_INVALID_INDEX);
1219 TRACE("[%p]: item %d height = %d\n", descr->self, index, height );
1220 descr->items[index].height = height;
1221 LISTBOX_UpdateScroll( descr );
1223 LISTBOX_InvalidateItems( descr, index );
1225 else if (height != descr->item_height)
1227 TRACE("[%p]: new height = %d\n", descr->self, height );
1228 descr->item_height = height;
1229 LISTBOX_UpdatePage( descr );
1230 LISTBOX_UpdateScroll( descr );
1232 InvalidateRect( descr->self, 0, TRUE );
1238 /***********************************************************************
1239 * LISTBOX_SetHorizontalPos
1241 static void LISTBOX_SetHorizontalPos( LB_DESCR *descr, INT pos )
1245 if (pos > descr->horz_extent - descr->width)
1246 pos = descr->horz_extent - descr->width;
1247 if (pos < 0) pos = 0;
1248 if (!(diff = descr->horz_pos - pos)) return;
1249 TRACE("[%p]: new horz pos = %d\n", descr->self, pos );
1250 descr->horz_pos = pos;
1251 LISTBOX_UpdateScroll( descr );
1252 if (abs(diff) < descr->width)
1255 /* Invalidate the focused item so it will be repainted correctly */
1256 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
1257 InvalidateRect( descr->self, &rect, TRUE );
1258 ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
1259 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1262 InvalidateRect( descr->self, NULL, TRUE );
1266 /***********************************************************************
1267 * LISTBOX_SetHorizontalExtent
1269 static LRESULT LISTBOX_SetHorizontalExtent( LB_DESCR *descr, INT extent )
1271 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1273 if (extent <= 0) extent = 1;
1274 if (extent == descr->horz_extent) return LB_OKAY;
1275 TRACE("[%p]: new horz extent = %d\n", descr->self, extent );
1276 descr->horz_extent = extent;
1277 if (descr->horz_pos > extent - descr->width)
1278 LISTBOX_SetHorizontalPos( descr, extent - descr->width );
1280 LISTBOX_UpdateScroll( descr );
1285 /***********************************************************************
1286 * LISTBOX_SetColumnWidth
1288 static LRESULT LISTBOX_SetColumnWidth( LB_DESCR *descr, INT width)
1290 if (width == descr->column_width) return LB_OKAY;
1291 TRACE("[%p]: new column width = %d\n", descr->self, width );
1292 descr->column_width = width;
1293 LISTBOX_UpdatePage( descr );
1298 /***********************************************************************
1301 * Returns the item height.
1303 static INT LISTBOX_SetFont( LB_DESCR *descr, HFONT font )
1307 const char *alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1312 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE )))
1314 ERR("unable to get DC.\n" );
1317 if (font) oldFont = SelectObject( hdc, font );
1318 GetTextExtentPointA( hdc, alphabet, 52, &sz);
1319 if (oldFont) SelectObject( hdc, oldFont );
1320 ReleaseDC( descr->self, hdc );
1322 descr->avg_char_width = (sz.cx / 26 + 1) / 2;
1323 if (!IS_OWNERDRAW(descr))
1324 LISTBOX_SetItemHeight( descr, 0, sz.cy, FALSE );
1329 /***********************************************************************
1330 * LISTBOX_MakeItemVisible
1332 * Make sure that a given item is partially or fully visible.
1334 static void LISTBOX_MakeItemVisible( LB_DESCR *descr, INT index, BOOL fully )
1338 TRACE("current top item %d, index %d, fully %d\n", descr->top_item, index, fully);
1340 if (index <= descr->top_item) top = index;
1341 else if (descr->style & LBS_MULTICOLUMN)
1343 INT cols = descr->width;
1344 if (!fully) cols += descr->column_width - 1;
1345 if (cols >= descr->column_width) cols /= descr->column_width;
1347 if (index < descr->top_item + (descr->page_size * cols)) return;
1348 top = index - descr->page_size * (cols - 1);
1350 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1352 INT height = fully ? descr->items[index].height : 1;
1353 for (top = index; top > descr->top_item; top--)
1354 if ((height += descr->items[top-1].height) > descr->height) break;
1358 if (index < descr->top_item + descr->page_size) return;
1359 if (!fully && (index == descr->top_item + descr->page_size) &&
1360 (descr->height > (descr->page_size * descr->item_height))) return;
1361 top = index - descr->page_size + 1;
1363 LISTBOX_SetTopItem( descr, top, TRUE );
1366 /***********************************************************************
1367 * LISTBOX_SetCaretIndex
1370 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1373 static LRESULT LISTBOX_SetCaretIndex( LB_DESCR *descr, INT index, BOOL fully_visible )
1375 INT oldfocus = descr->focus_item;
1377 TRACE("old focus %d, index %d\n", oldfocus, index);
1379 if (descr->style & LBS_NOSEL) return LB_ERR;
1380 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1381 if (index == oldfocus) return LB_OKAY;
1383 LISTBOX_DrawFocusRect( descr, FALSE );
1384 descr->focus_item = index;
1386 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1387 LISTBOX_DrawFocusRect( descr, TRUE );
1393 /***********************************************************************
1394 * LISTBOX_SelectItemRange
1396 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1398 static LRESULT LISTBOX_SelectItemRange( LB_DESCR *descr, INT first,
1403 /* A few sanity checks */
1405 if (descr->style & LBS_NOSEL) return LB_ERR;
1406 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1408 if (!descr->nb_items) return LB_OKAY;
1410 if (last == -1 || last >= descr->nb_items) last = descr->nb_items - 1;
1411 if (first < 0) first = 0;
1412 if (last < first) return LB_OKAY;
1414 if (on) /* Turn selection on */
1416 for (i = first; i <= last; i++)
1418 if (descr->items[i].selected) continue;
1419 descr->items[i].selected = TRUE;
1420 LISTBOX_InvalidateItemRect(descr, i);
1423 else /* Turn selection off */
1425 for (i = first; i <= last; i++)
1427 if (!descr->items[i].selected) continue;
1428 descr->items[i].selected = FALSE;
1429 LISTBOX_InvalidateItemRect(descr, i);
1435 /***********************************************************************
1436 * LISTBOX_SetSelection
1438 static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index,
1439 BOOL on, BOOL send_notify )
1441 TRACE( "cur_sel=%d index=%d notify=%s\n",
1442 descr->selected_item, index, send_notify ? "YES" : "NO" );
1444 if (descr->style & LBS_NOSEL)
1446 descr->selected_item = index;
1449 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1450 if (descr->style & LBS_MULTIPLESEL)
1452 if (index == -1) /* Select all items */
1453 return LISTBOX_SelectItemRange( descr, 0, descr->nb_items, on );
1454 else /* Only one item */
1455 return LISTBOX_SelectItemRange( descr, index, index, on );
1459 INT oldsel = descr->selected_item;
1460 if (index == oldsel) return LB_OKAY;
1461 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1462 if (index != -1) descr->items[index].selected = TRUE;
1463 if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT );
1464 descr->selected_item = index;
1465 if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT );
1466 if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr,
1467 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1469 if( descr->lphc ) /* set selection change flag for parent combo */
1470 descr->lphc->wState |= CBF_SELCHANGE;
1476 /***********************************************************************
1479 * Change the caret position and extend the selection to the new caret.
1481 static void LISTBOX_MoveCaret( LB_DESCR *descr, INT index, BOOL fully_visible )
1483 TRACE("old focus %d, index %d\n", descr->focus_item, index);
1485 if ((index < 0) || (index >= descr->nb_items))
1488 /* Important, repaint needs to be done in this order if
1489 you want to mimic Windows behavior:
1490 1. Remove the focus and paint the item
1491 2. Remove the selection and paint the item(s)
1492 3. Set the selection and repaint the item(s)
1493 4. Set the focus to 'index' and repaint the item */
1495 /* 1. remove the focus and repaint the item */
1496 LISTBOX_DrawFocusRect( descr, FALSE );
1498 /* 2. then turn off the previous selection */
1499 /* 3. repaint the new selected item */
1500 if (descr->style & LBS_EXTENDEDSEL)
1502 if (descr->anchor_item != -1)
1504 INT first = min( index, descr->anchor_item );
1505 INT last = max( index, descr->anchor_item );
1507 LISTBOX_SelectItemRange( descr, 0, first - 1, FALSE );
1508 LISTBOX_SelectItemRange( descr, last + 1, -1, FALSE );
1509 LISTBOX_SelectItemRange( descr, first, last, TRUE );
1512 else if (!(descr->style & LBS_MULTIPLESEL))
1514 /* Set selection to new caret item */
1515 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
1518 /* 4. repaint the new item with the focus */
1519 descr->focus_item = index;
1520 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1521 LISTBOX_DrawFocusRect( descr, TRUE );
1525 /***********************************************************************
1526 * LISTBOX_InsertItem
1528 static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index,
1529 LPWSTR str, ULONG_PTR data )
1533 INT oldfocus = descr->focus_item;
1535 if (index == -1) index = descr->nb_items;
1536 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1537 if (!descr->items) max_items = 0;
1538 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1539 if (descr->nb_items == max_items)
1541 /* We need to grow the array */
1542 max_items += LB_ARRAY_GRANULARITY;
1544 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1545 max_items * sizeof(LB_ITEMDATA) );
1547 item = HeapAlloc( GetProcessHeap(), 0,
1548 max_items * sizeof(LB_ITEMDATA) );
1551 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1554 descr->items = item;
1557 /* Insert the item structure */
1559 item = &descr->items[index];
1560 if (index < descr->nb_items)
1561 RtlMoveMemory( item + 1, item,
1562 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1566 item->selected = FALSE;
1569 /* Get item height */
1571 if (descr->style & LBS_OWNERDRAWVARIABLE)
1573 MEASUREITEMSTRUCT mis;
1574 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1576 mis.CtlType = ODT_LISTBOX;
1579 mis.itemData = descr->items[index].data;
1580 mis.itemHeight = descr->item_height;
1581 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1582 item->height = mis.itemHeight ? mis.itemHeight : 1;
1583 TRACE("[%p]: measure item %d (%s) = %d\n",
1584 descr->self, index, str ? debugstr_w(str) : "", item->height );
1587 /* Repaint the items */
1589 LISTBOX_UpdateScroll( descr );
1590 LISTBOX_InvalidateItems( descr, index );
1592 /* Move selection and focused item */
1593 /* If listbox was empty, set focus to the first item */
1594 if (descr->nb_items == 1)
1595 LISTBOX_SetCaretIndex( descr, 0, FALSE );
1596 /* single select don't change selection index in win31 */
1597 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1599 descr->selected_item++;
1600 LISTBOX_SetSelection( descr, descr->selected_item-1, TRUE, FALSE );
1604 if (index <= descr->selected_item)
1606 descr->selected_item++;
1607 descr->focus_item = oldfocus; /* focus not changed */
1614 /***********************************************************************
1615 * LISTBOX_InsertString
1617 static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index, LPCWSTR str )
1619 LPWSTR new_str = NULL;
1623 if (HAS_STRINGS(descr))
1625 static const WCHAR empty_stringW[] = { 0 };
1626 if (!str) str = empty_stringW;
1627 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1629 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1632 strcpyW(new_str, str);
1634 else data = (ULONG_PTR)str;
1636 if (index == -1) index = descr->nb_items;
1637 if ((ret = LISTBOX_InsertItem( descr, index, new_str, data )) != 0)
1639 HeapFree( GetProcessHeap(), 0, new_str );
1643 TRACE("[%p]: added item %d %s\n",
1644 descr->self, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1649 /***********************************************************************
1650 * LISTBOX_DeleteItem
1652 * Delete the content of an item. 'index' must be a valid index.
1654 static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index )
1656 /* save the item data before it gets freed by LB_RESETCONTENT */
1657 ULONG_PTR item_data = descr->items[index].data;
1658 LPWSTR item_str = descr->items[index].str;
1660 if (!descr->nb_items)
1661 SendMessageW( descr->self, LB_RESETCONTENT, 0, 0 );
1663 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1664 * while Win95 sends it for all items with user data.
1665 * It's probably better to send it too often than not
1666 * often enough, so this is what we do here.
1668 if (IS_OWNERDRAW(descr) || item_data)
1670 DELETEITEMSTRUCT dis;
1671 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1673 dis.CtlType = ODT_LISTBOX;
1676 dis.hwndItem = descr->self;
1677 dis.itemData = item_data;
1678 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1680 if (HAS_STRINGS(descr))
1681 HeapFree( GetProcessHeap(), 0, item_str );
1685 /***********************************************************************
1686 * LISTBOX_RemoveItem
1688 * Remove an item from the listbox and delete its content.
1690 static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index )
1695 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1697 /* We need to invalidate the original rect instead of the updated one. */
1698 LISTBOX_InvalidateItems( descr, index );
1701 LISTBOX_DeleteItem( descr, index );
1703 if (!descr->nb_items) return LB_OKAY;
1705 /* Remove the item */
1707 item = &descr->items[index];
1708 if (index < descr->nb_items)
1709 RtlMoveMemory( item, item + 1,
1710 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1711 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1713 /* Shrink the item array if possible */
1715 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1716 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1718 max_items -= LB_ARRAY_GRANULARITY;
1719 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1720 max_items * sizeof(LB_ITEMDATA) );
1721 if (item) descr->items = item;
1723 /* Repaint the items */
1725 LISTBOX_UpdateScroll( descr );
1726 /* if we removed the scrollbar, reset the top of the list
1727 (correct for owner-drawn ???) */
1728 if (descr->nb_items == descr->page_size)
1729 LISTBOX_SetTopItem( descr, 0, TRUE );
1731 /* Move selection and focused item */
1732 if (!IS_MULTISELECT(descr))
1734 if (index == descr->selected_item)
1735 descr->selected_item = -1;
1736 else if (index < descr->selected_item)
1738 descr->selected_item--;
1739 if (ISWIN31) /* win 31 do not change the selected item number */
1740 LISTBOX_SetSelection( descr, descr->selected_item + 1, TRUE, FALSE);
1744 if (descr->focus_item >= descr->nb_items)
1746 descr->focus_item = descr->nb_items - 1;
1747 if (descr->focus_item < 0) descr->focus_item = 0;
1753 /***********************************************************************
1754 * LISTBOX_ResetContent
1756 static void LISTBOX_ResetContent( LB_DESCR *descr )
1760 for(i = descr->nb_items - 1; i>=0; i--) LISTBOX_DeleteItem( descr, i);
1761 HeapFree( GetProcessHeap(), 0, descr->items );
1762 descr->nb_items = 0;
1763 descr->top_item = 0;
1764 descr->selected_item = -1;
1765 descr->focus_item = 0;
1766 descr->anchor_item = -1;
1767 descr->items = NULL;
1771 /***********************************************************************
1774 static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count )
1778 if (HAS_STRINGS(descr))
1780 SetLastError(ERROR_SETCOUNT_ON_BAD_LB);
1784 /* FIXME: this is far from optimal... */
1785 if (count > descr->nb_items)
1787 while (count > descr->nb_items)
1788 if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0)
1791 else if (count < descr->nb_items)
1793 while (count < descr->nb_items)
1794 if ((ret = LISTBOX_RemoveItem( descr, (descr->nb_items - 1) )) < 0)
1801 /***********************************************************************
1804 static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib,
1805 LPCWSTR filespec, BOOL long_names )
1808 LRESULT ret = LB_OKAY;
1809 WIN32_FIND_DATAW entry;
1811 LRESULT maxinsert = LB_ERR;
1813 /* don't scan directory if we just want drives exclusively */
1814 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1815 /* scan directory */
1816 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1818 int le = GetLastError();
1819 if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR;
1826 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1828 static const WCHAR bracketW[] = { ']',0 };
1829 static const WCHAR dotW[] = { '.',0 };
1830 if (!(attrib & DDL_DIRECTORY) ||
1831 !strcmpW( entry.cFileName, dotW )) continue;
1833 if (!long_names && entry.cAlternateFileName[0])
1834 strcpyW( buffer + 1, entry.cAlternateFileName );
1836 strcpyW( buffer + 1, entry.cFileName );
1837 strcatW(buffer, bracketW);
1839 else /* not a directory */
1841 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1842 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1844 if ((attrib & DDL_EXCLUSIVE) &&
1845 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1848 if (!long_names && entry.cAlternateFileName[0])
1849 strcpyW( buffer, entry.cAlternateFileName );
1851 strcpyW( buffer, entry.cFileName );
1853 if (!long_names) CharLowerW( buffer );
1854 pos = LISTBOX_FindFileStrPos( descr, buffer );
1855 if ((ret = LISTBOX_InsertString( descr, pos, buffer )) < 0)
1857 if (ret <= maxinsert) maxinsert++; else maxinsert = ret;
1858 } while (FindNextFileW( handle, &entry ));
1859 FindClose( handle );
1867 if (attrib & DDL_DRIVES)
1869 WCHAR buffer[] = {'[','-','a','-',']',0};
1870 WCHAR root[] = {'A',':','\\',0};
1872 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1874 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1875 if ((ret = LISTBOX_InsertString( descr, -1, buffer )) < 0)
1884 /***********************************************************************
1885 * LISTBOX_HandleVScroll
1887 static LRESULT LISTBOX_HandleVScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1891 if (descr->style & LBS_MULTICOLUMN) return 0;
1895 LISTBOX_SetTopItem( descr, descr->top_item - 1, TRUE );
1898 LISTBOX_SetTopItem( descr, descr->top_item + 1, TRUE );
1901 LISTBOX_SetTopItem( descr, descr->top_item -
1902 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1905 LISTBOX_SetTopItem( descr, descr->top_item +
1906 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1908 case SB_THUMBPOSITION:
1909 LISTBOX_SetTopItem( descr, pos, TRUE );
1912 info.cbSize = sizeof(info);
1913 info.fMask = SIF_TRACKPOS;
1914 GetScrollInfo( descr->self, SB_VERT, &info );
1915 LISTBOX_SetTopItem( descr, info.nTrackPos, TRUE );
1918 LISTBOX_SetTopItem( descr, 0, TRUE );
1921 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1928 /***********************************************************************
1929 * LISTBOX_HandleHScroll
1931 static LRESULT LISTBOX_HandleHScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1936 if (descr->style & LBS_MULTICOLUMN)
1941 LISTBOX_SetTopItem( descr, descr->top_item-descr->page_size,
1945 LISTBOX_SetTopItem( descr, descr->top_item+descr->page_size,
1949 page = descr->width / descr->column_width;
1950 if (page < 1) page = 1;
1951 LISTBOX_SetTopItem( descr,
1952 descr->top_item - page * descr->page_size, TRUE );
1955 page = descr->width / descr->column_width;
1956 if (page < 1) page = 1;
1957 LISTBOX_SetTopItem( descr,
1958 descr->top_item + page * descr->page_size, TRUE );
1960 case SB_THUMBPOSITION:
1961 LISTBOX_SetTopItem( descr, pos*descr->page_size, TRUE );
1964 info.cbSize = sizeof(info);
1965 info.fMask = SIF_TRACKPOS;
1966 GetScrollInfo( descr->self, SB_VERT, &info );
1967 LISTBOX_SetTopItem( descr, info.nTrackPos*descr->page_size,
1971 LISTBOX_SetTopItem( descr, 0, TRUE );
1974 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1978 else if (descr->horz_extent)
1983 LISTBOX_SetHorizontalPos( descr, descr->horz_pos - 1 );
1986 LISTBOX_SetHorizontalPos( descr, descr->horz_pos + 1 );
1989 LISTBOX_SetHorizontalPos( descr,
1990 descr->horz_pos - descr->width );
1993 LISTBOX_SetHorizontalPos( descr,
1994 descr->horz_pos + descr->width );
1996 case SB_THUMBPOSITION:
1997 LISTBOX_SetHorizontalPos( descr, pos );
2000 info.cbSize = sizeof(info);
2001 info.fMask = SIF_TRACKPOS;
2002 GetScrollInfo( descr->self, SB_HORZ, &info );
2003 LISTBOX_SetHorizontalPos( descr, info.nTrackPos );
2006 LISTBOX_SetHorizontalPos( descr, 0 );
2009 LISTBOX_SetHorizontalPos( descr,
2010 descr->horz_extent - descr->width );
2017 static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta )
2019 short gcWheelDelta = 0;
2020 UINT pulScrollLines = 3;
2022 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
2024 gcWheelDelta -= delta;
2026 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
2028 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
2029 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
2030 LISTBOX_SetTopItem( descr, descr->top_item + cLineScroll, TRUE );
2035 /***********************************************************************
2036 * LISTBOX_HandleLButtonDown
2038 static LRESULT LISTBOX_HandleLButtonDown( LB_DESCR *descr, DWORD keys, INT x, INT y )
2040 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
2042 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2043 descr->self, x, y, index, descr->focus_item);
2045 if (!descr->caret_on && (descr->in_focus)) return 0;
2047 if (!descr->in_focus)
2049 if( !descr->lphc ) SetFocus( descr->self );
2050 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
2053 if (index == -1) return 0;
2057 if (descr->style & LBS_NOTIFY )
2058 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
2059 MAKELPARAM( x, y ) );
2062 descr->captured = TRUE;
2063 SetCapture( descr->self );
2065 if (descr->style & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL))
2067 /* we should perhaps make sure that all items are deselected
2068 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2069 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2070 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2073 if (!(keys & MK_SHIFT)) descr->anchor_item = index;
2074 if (keys & MK_CONTROL)
2076 LISTBOX_SetCaretIndex( descr, index, FALSE );
2077 LISTBOX_SetSelection( descr, index,
2078 !descr->items[index].selected,
2079 (descr->style & LBS_NOTIFY) != 0);
2083 LISTBOX_MoveCaret( descr, index, FALSE );
2085 if (descr->style & LBS_EXTENDEDSEL)
2087 LISTBOX_SetSelection( descr, index,
2088 descr->items[index].selected,
2089 (descr->style & LBS_NOTIFY) != 0 );
2093 LISTBOX_SetSelection( descr, index,
2094 !descr->items[index].selected,
2095 (descr->style & LBS_NOTIFY) != 0 );
2101 descr->anchor_item = index;
2102 LISTBOX_MoveCaret( descr, index, FALSE );
2103 LISTBOX_SetSelection( descr, index,
2104 TRUE, (descr->style & LBS_NOTIFY) != 0 );
2109 if (GetWindowLongW( descr->self, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
2116 if (DragDetect( descr->self, pt ))
2117 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
2124 /*************************************************************************
2125 * LISTBOX_HandleLButtonDownCombo [Internal]
2127 * Process LButtonDown message for the ComboListBox
2130 * pWnd [I] The windows internal structure
2131 * pDescr [I] The ListBox internal structure
2132 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2133 * x [I] X Mouse Coordinate
2134 * y [I] Y Mouse Coordinate
2137 * 0 since we are processing the WM_LBUTTONDOWN Message
2140 * This function is only to be used when a ListBox is a ComboListBox
2143 static LRESULT LISTBOX_HandleLButtonDownCombo( LB_DESCR *descr, UINT msg, DWORD keys, INT x, INT y)
2145 RECT clientRect, screenRect;
2151 GetClientRect(descr->self, &clientRect);
2153 if(PtInRect(&clientRect, mousePos))
2155 /* MousePos is in client, resume normal processing */
2156 if (msg == WM_LBUTTONDOWN)
2158 descr->lphc->droppedIndex = descr->nb_items ? descr->selected_item : -1;
2159 return LISTBOX_HandleLButtonDown( descr, keys, x, y);
2161 else if (descr->style & LBS_NOTIFY)
2162 SEND_NOTIFICATION( descr, LBN_DBLCLK );
2166 POINT screenMousePos;
2167 HWND hWndOldCapture;
2169 /* Check the Non-Client Area */
2170 screenMousePos = mousePos;
2171 hWndOldCapture = GetCapture();
2173 GetWindowRect(descr->self, &screenRect);
2174 ClientToScreen(descr->self, &screenMousePos);
2176 if(!PtInRect(&screenRect, screenMousePos))
2178 LISTBOX_SetCaretIndex( descr, descr->lphc->droppedIndex, FALSE );
2179 LISTBOX_SetSelection( descr, descr->lphc->droppedIndex, FALSE, FALSE );
2180 COMBO_FlipListbox( descr->lphc, FALSE, FALSE );
2184 /* Check to see the NC is a scrollbar */
2186 LONG style = GetWindowLongW( descr->self, GWL_STYLE );
2187 /* Check Vertical scroll bar */
2188 if (style & WS_VSCROLL)
2190 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2191 if (PtInRect( &clientRect, mousePos ))
2192 nHitTestType = HTVSCROLL;
2194 /* Check horizontal scroll bar */
2195 if (style & WS_HSCROLL)
2197 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2198 if (PtInRect( &clientRect, mousePos ))
2199 nHitTestType = HTHSCROLL;
2201 /* Windows sends this message when a scrollbar is clicked
2204 if(nHitTestType != 0)
2206 SendMessageW(descr->self, WM_NCLBUTTONDOWN, nHitTestType,
2207 MAKELONG(screenMousePos.x, screenMousePos.y));
2209 /* Resume the Capture after scrolling is complete
2211 if(hWndOldCapture != 0)
2212 SetCapture(hWndOldCapture);
2218 /***********************************************************************
2219 * LISTBOX_HandleLButtonUp
2221 static LRESULT LISTBOX_HandleLButtonUp( LB_DESCR *descr )
2223 if (LISTBOX_Timer != LB_TIMER_NONE)
2224 KillSystemTimer( descr->self, LB_TIMER_ID );
2225 LISTBOX_Timer = LB_TIMER_NONE;
2226 if (descr->captured)
2228 descr->captured = FALSE;
2229 if (GetCapture() == descr->self) ReleaseCapture();
2230 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2231 SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2237 /***********************************************************************
2238 * LISTBOX_HandleTimer
2240 * Handle scrolling upon a timer event.
2241 * Return TRUE if scrolling should continue.
2243 static LRESULT LISTBOX_HandleTimer( LB_DESCR *descr, INT index, TIMER_DIRECTION dir )
2248 if (descr->top_item) index = descr->top_item - 1;
2252 if (descr->top_item) index -= descr->page_size;
2255 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2256 if (index == descr->focus_item) index++;
2257 if (index >= descr->nb_items) index = descr->nb_items - 1;
2259 case LB_TIMER_RIGHT:
2260 if (index + descr->page_size < descr->nb_items)
2261 index += descr->page_size;
2266 if (index == descr->focus_item) return FALSE;
2267 LISTBOX_MoveCaret( descr, index, FALSE );
2272 /***********************************************************************
2273 * LISTBOX_HandleSystemTimer
2275 * WM_SYSTIMER handler.
2277 static LRESULT LISTBOX_HandleSystemTimer( LB_DESCR *descr )
2279 if (!LISTBOX_HandleTimer( descr, descr->focus_item, LISTBOX_Timer ))
2281 KillSystemTimer( descr->self, LB_TIMER_ID );
2282 LISTBOX_Timer = LB_TIMER_NONE;
2288 /***********************************************************************
2289 * LISTBOX_HandleMouseMove
2291 * WM_MOUSEMOVE handler.
2293 static void LISTBOX_HandleMouseMove( LB_DESCR *descr,
2297 TIMER_DIRECTION dir = LB_TIMER_NONE;
2299 if (!descr->captured) return;
2301 if (descr->style & LBS_MULTICOLUMN)
2304 else if (y >= descr->item_height * descr->page_size)
2305 y = descr->item_height * descr->page_size - 1;
2309 dir = LB_TIMER_LEFT;
2312 else if (x >= descr->width)
2314 dir = LB_TIMER_RIGHT;
2315 x = descr->width - 1;
2320 if (y < 0) dir = LB_TIMER_UP; /* above */
2321 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2324 index = LISTBOX_GetItemFromPoint( descr, x, y );
2325 if (index == -1) index = descr->focus_item;
2326 if (!LISTBOX_HandleTimer( descr, index, dir )) dir = LB_TIMER_NONE;
2328 /* Start/stop the system timer */
2330 if (dir != LB_TIMER_NONE)
2331 SetSystemTimer( descr->self, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2332 else if (LISTBOX_Timer != LB_TIMER_NONE)
2333 KillSystemTimer( descr->self, LB_TIMER_ID );
2334 LISTBOX_Timer = dir;
2338 /***********************************************************************
2339 * LISTBOX_HandleKeyDown
2341 static LRESULT LISTBOX_HandleKeyDown( LB_DESCR *descr, DWORD key )
2344 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2345 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2346 bForceSelection = FALSE; /* only for single select list */
2348 if (descr->style & LBS_WANTKEYBOARDINPUT)
2350 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2351 MAKEWPARAM(LOWORD(key), descr->focus_item),
2352 (LPARAM)descr->self );
2353 if (caret == -2) return 0;
2355 if (caret == -1) switch(key)
2358 if (descr->style & LBS_MULTICOLUMN)
2360 bForceSelection = FALSE;
2361 if (descr->focus_item >= descr->page_size)
2362 caret = descr->focus_item - descr->page_size;
2367 caret = descr->focus_item - 1;
2368 if (caret < 0) caret = 0;
2371 if (descr->style & LBS_MULTICOLUMN)
2373 bForceSelection = FALSE;
2374 if (descr->focus_item + descr->page_size < descr->nb_items)
2375 caret = descr->focus_item + descr->page_size;
2380 caret = descr->focus_item + 1;
2381 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2385 if (descr->style & LBS_MULTICOLUMN)
2387 INT page = descr->width / descr->column_width;
2388 if (page < 1) page = 1;
2389 caret = descr->focus_item - (page * descr->page_size) + 1;
2391 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2392 if (caret < 0) caret = 0;
2395 if (descr->style & LBS_MULTICOLUMN)
2397 INT page = descr->width / descr->column_width;
2398 if (page < 1) page = 1;
2399 caret = descr->focus_item + (page * descr->page_size) - 1;
2401 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2402 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2408 caret = descr->nb_items - 1;
2411 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2412 else if (descr->style & LBS_MULTIPLESEL)
2414 LISTBOX_SetSelection( descr, descr->focus_item,
2415 !descr->items[descr->focus_item].selected,
2416 (descr->style & LBS_NOTIFY) != 0 );
2420 bForceSelection = FALSE;
2422 if (bForceSelection) /* focused item is used instead of key */
2423 caret = descr->focus_item;
2426 if (((descr->style & LBS_EXTENDEDSEL) &&
2427 !(GetKeyState( VK_SHIFT ) & 0x8000)) ||
2428 !IS_MULTISELECT(descr))
2429 descr->anchor_item = caret;
2430 LISTBOX_MoveCaret( descr, caret, TRUE );
2432 if (descr->style & LBS_MULTIPLESEL)
2433 descr->selected_item = caret;
2435 LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2436 if (descr->style & LBS_NOTIFY)
2438 if (descr->lphc && IsWindowVisible( descr->self ))
2440 /* make sure that combo parent doesn't hide us */
2441 descr->lphc->wState |= CBF_NOROLLUP;
2443 if (descr->nb_items) SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2450 /***********************************************************************
2451 * LISTBOX_HandleChar
2453 static LRESULT LISTBOX_HandleChar( LB_DESCR *descr, WCHAR charW )
2461 if (descr->style & LBS_WANTKEYBOARDINPUT)
2463 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2464 MAKEWPARAM(charW, descr->focus_item),
2465 (LPARAM)descr->self );
2466 if (caret == -2) return 0;
2469 caret = LISTBOX_FindString( descr, descr->focus_item, str, FALSE);
2472 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2473 LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2474 LISTBOX_MoveCaret( descr, caret, TRUE );
2475 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2476 SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2482 /***********************************************************************
2485 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2488 MEASUREITEMSTRUCT mis;
2491 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2494 GetClientRect( hwnd, &rect );
2496 descr->owner = GetParent( descr->self );
2497 descr->style = GetWindowLongW( descr->self, GWL_STYLE );
2498 descr->width = rect.right - rect.left;
2499 descr->height = rect.bottom - rect.top;
2500 descr->items = NULL;
2501 descr->nb_items = 0;
2502 descr->top_item = 0;
2503 descr->selected_item = -1;
2504 descr->focus_item = 0;
2505 descr->anchor_item = -1;
2506 descr->item_height = 1;
2507 descr->page_size = 1;
2508 descr->column_width = 150;
2509 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0;
2510 descr->horz_pos = 0;
2513 descr->caret_on = lphc ? FALSE : TRUE;
2514 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2515 descr->in_focus = FALSE;
2516 descr->captured = FALSE;
2518 descr->locale = GetUserDefaultLCID();
2521 if (is_old_app(descr) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2523 /* Win95 document "List Box Differences" from MSDN:
2524 If a list box in a version 3.x application has either the
2525 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2526 horizontal and vertical scroll bars.
2528 descr->style |= WS_VSCROLL | WS_HSCROLL;
2533 TRACE("[%p]: resetting owner %p -> %p\n", descr->self, descr->owner, lphc->self );
2534 descr->owner = lphc->self;
2537 SetWindowLongPtrW( descr->self, 0, (LONG_PTR)descr );
2539 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2541 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2542 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2543 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2544 descr->item_height = LISTBOX_SetFont( descr, 0 );
2546 if (descr->style & LBS_OWNERDRAWFIXED)
2548 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2550 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2551 descr->item_height = lphc->fixedOwnerDrawHeight;
2555 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
2556 mis.CtlType = ODT_LISTBOX;
2561 mis.itemHeight = descr->item_height;
2562 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2563 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2567 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2572 /***********************************************************************
2575 static BOOL LISTBOX_Destroy( LB_DESCR *descr )
2577 LISTBOX_ResetContent( descr );
2578 SetWindowLongPtrW( descr->self, 0, 0 );
2579 HeapFree( GetProcessHeap(), 0, descr );
2584 /***********************************************************************
2585 * ListBoxWndProc_common
2587 LRESULT ListBoxWndProc_common( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, BOOL unicode )
2589 LB_DESCR *descr = (LB_DESCR *)GetWindowLongPtrW( hwnd, 0 );
2590 LPHEADCOMBO lphc = 0;
2595 if (!IsWindow(hwnd)) return 0;
2597 if (msg == WM_CREATE)
2599 CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam;
2600 if (lpcs->style & LBS_COMBOBOX) lphc = lpcs->lpCreateParams;
2601 if (!LISTBOX_Create( hwnd, lphc )) return -1;
2602 TRACE("creating hwnd %p descr %p\n", hwnd, (void *)GetWindowLongPtrW( hwnd, 0 ) );
2605 /* Ignore all other messages before we get a WM_CREATE */
2606 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2607 DefWindowProcA( hwnd, msg, wParam, lParam );
2609 if (descr->style & LBS_COMBOBOX) lphc = descr->lphc;
2611 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2612 descr->self, SPY_GetMsgName(msg, descr->self), wParam, lParam );
2616 case LB_RESETCONTENT:
2617 LISTBOX_ResetContent( descr );
2618 LISTBOX_UpdateScroll( descr );
2619 InvalidateRect( descr->self, NULL, TRUE );
2626 if(unicode || !HAS_STRINGS(descr))
2627 textW = (LPWSTR)lParam;
2630 LPSTR textA = (LPSTR)lParam;
2631 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2632 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2633 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2637 wParam = LISTBOX_FindStringPos( descr, textW, FALSE );
2638 ret = LISTBOX_InsertString( descr, wParam, textW );
2639 if (!unicode && HAS_STRINGS(descr))
2640 HeapFree(GetProcessHeap(), 0, textW);
2644 case LB_INSERTSTRING:
2648 if(unicode || !HAS_STRINGS(descr))
2649 textW = (LPWSTR)lParam;
2652 LPSTR textA = (LPSTR)lParam;
2653 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2654 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2655 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2659 ret = LISTBOX_InsertString( descr, wParam, textW );
2660 if(!unicode && HAS_STRINGS(descr))
2661 HeapFree(GetProcessHeap(), 0, textW);
2669 if(unicode || !HAS_STRINGS(descr))
2670 textW = (LPWSTR)lParam;
2673 LPSTR textA = (LPSTR)lParam;
2674 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2675 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2676 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2680 wParam = LISTBOX_FindFileStrPos( descr, textW );
2681 ret = LISTBOX_InsertString( descr, wParam, textW );
2682 if(!unicode && HAS_STRINGS(descr))
2683 HeapFree(GetProcessHeap(), 0, textW);
2687 case LB_DELETESTRING:
2688 if (LISTBOX_RemoveItem( descr, wParam) != LB_ERR)
2689 return descr->nb_items;
2692 SetLastError(ERROR_INVALID_INDEX);
2696 case LB_GETITEMDATA:
2697 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2699 SetLastError(ERROR_INVALID_INDEX);
2702 return descr->items[wParam].data;
2704 case LB_SETITEMDATA:
2705 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2707 SetLastError(ERROR_INVALID_INDEX);
2710 descr->items[wParam].data = lParam;
2711 /* undocumented: returns TRUE, not LB_OKAY (0) */
2715 return descr->nb_items;
2718 return LISTBOX_GetText( descr, wParam, (LPWSTR)lParam, unicode );
2721 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2723 SetLastError(ERROR_INVALID_INDEX);
2726 if (!HAS_STRINGS(descr)) return sizeof(DWORD);
2727 if (unicode) return strlenW( descr->items[wParam].str );
2728 return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
2729 strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
2732 if (descr->nb_items == 0)
2734 if (!IS_MULTISELECT(descr))
2735 return descr->selected_item;
2736 if (descr->selected_item != -1)
2737 return descr->selected_item;
2738 return descr->focus_item;
2739 /* otherwise, if the user tries to move the selection with the */
2740 /* arrow keys, we will give the application something to choke on */
2741 case LB_GETTOPINDEX:
2742 return descr->top_item;
2744 case LB_GETITEMHEIGHT:
2745 return LISTBOX_GetItemHeight( descr, wParam );
2747 case LB_SETITEMHEIGHT:
2748 return LISTBOX_SetItemHeight( descr, wParam, lParam, TRUE );
2750 case LB_ITEMFROMPOINT:
2757 /* The hiword of the return value is not a client area
2758 hittest as suggested by MSDN, but rather a hittest on
2759 the returned listbox item. */
2761 if(descr->nb_items == 0)
2762 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2764 pt.x = (short)LOWORD(lParam);
2765 pt.y = (short)HIWORD(lParam);
2767 SetRect(&rect, 0, 0, descr->width, descr->height);
2769 if(!PtInRect(&rect, pt))
2771 pt.x = min(pt.x, rect.right - 1);
2772 pt.x = max(pt.x, 0);
2773 pt.y = min(pt.y, rect.bottom - 1);
2774 pt.y = max(pt.y, 0);
2778 index = LISTBOX_GetItemFromPoint(descr, pt.x, pt.y);
2782 index = descr->nb_items - 1;
2785 return MAKELONG(index, hit ? 0 : 1);
2788 case LB_SETCARETINDEX:
2789 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2790 if (LISTBOX_SetCaretIndex( descr, wParam, !lParam ) == LB_ERR)
2797 case LB_GETCARETINDEX:
2798 return descr->focus_item;
2800 case LB_SETTOPINDEX:
2801 return LISTBOX_SetTopItem( descr, wParam, TRUE );
2803 case LB_SETCOLUMNWIDTH:
2804 return LISTBOX_SetColumnWidth( descr, wParam );
2806 case LB_GETITEMRECT:
2807 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2813 if(unicode || !HAS_STRINGS(descr))
2814 textW = (LPWSTR)lParam;
2817 LPSTR textA = (LPSTR)lParam;
2818 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2819 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2820 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2822 ret = LISTBOX_FindString( descr, wParam, textW, FALSE );
2823 if(!unicode && HAS_STRINGS(descr))
2824 HeapFree(GetProcessHeap(), 0, textW);
2828 case LB_FINDSTRINGEXACT:
2832 if(unicode || !HAS_STRINGS(descr))
2833 textW = (LPWSTR)lParam;
2836 LPSTR textA = (LPSTR)lParam;
2837 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2838 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2839 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2841 ret = LISTBOX_FindString( descr, wParam, textW, TRUE );
2842 if(!unicode && HAS_STRINGS(descr))
2843 HeapFree(GetProcessHeap(), 0, textW);
2847 case LB_SELECTSTRING:
2852 if(HAS_STRINGS(descr))
2853 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2854 debugstr_a((LPSTR)lParam));
2855 if(unicode || !HAS_STRINGS(descr))
2856 textW = (LPWSTR)lParam;
2859 LPSTR textA = (LPSTR)lParam;
2860 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2861 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2862 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2864 index = LISTBOX_FindString( descr, wParam, textW, FALSE );
2865 if(!unicode && HAS_STRINGS(descr))
2866 HeapFree(GetProcessHeap(), 0, textW);
2867 if (index != LB_ERR)
2869 LISTBOX_MoveCaret( descr, index, TRUE );
2870 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
2876 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2878 return descr->items[wParam].selected;
2881 return LISTBOX_SetSelection( descr, lParam, wParam, FALSE );
2884 if (IS_MULTISELECT(descr)) return LB_ERR;
2885 LISTBOX_SetCaretIndex( descr, wParam, FALSE );
2886 ret = LISTBOX_SetSelection( descr, wParam, TRUE, FALSE );
2887 if (ret != LB_ERR) ret = descr->selected_item;
2890 case LB_GETSELCOUNT:
2891 return LISTBOX_GetSelCount( descr );
2893 case LB_GETSELITEMS:
2894 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2896 case LB_SELITEMRANGE:
2897 if (LOWORD(lParam) <= HIWORD(lParam))
2898 return LISTBOX_SelectItemRange( descr, LOWORD(lParam),
2899 HIWORD(lParam), wParam );
2901 return LISTBOX_SelectItemRange( descr, HIWORD(lParam),
2902 LOWORD(lParam), wParam );
2904 case LB_SELITEMRANGEEX:
2905 if ((INT)lParam >= (INT)wParam)
2906 return LISTBOX_SelectItemRange( descr, wParam, lParam, TRUE );
2908 return LISTBOX_SelectItemRange( descr, lParam, wParam, FALSE);
2910 case LB_GETHORIZONTALEXTENT:
2911 return descr->horz_extent;
2913 case LB_SETHORIZONTALEXTENT:
2914 return LISTBOX_SetHorizontalExtent( descr, wParam );
2916 case LB_GETANCHORINDEX:
2917 return descr->anchor_item;
2919 case LB_SETANCHORINDEX:
2920 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2922 SetLastError(ERROR_INVALID_INDEX);
2925 descr->anchor_item = (INT)wParam;
2933 textW = (LPWSTR)lParam;
2936 LPSTR textA = (LPSTR)lParam;
2937 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2938 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2939 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2941 ret = LISTBOX_Directory( descr, wParam, textW, msg == LB_DIR );
2943 HeapFree(GetProcessHeap(), 0, textW);
2948 return descr->locale;
2953 if (!IsValidLocale((LCID)wParam, LCID_INSTALLED))
2955 ret = descr->locale;
2956 descr->locale = (LCID)wParam;
2960 case LB_INITSTORAGE:
2961 return LISTBOX_InitStorage( descr, wParam );
2964 return LISTBOX_SetCount( descr, (INT)wParam );
2966 case LB_SETTABSTOPS:
2967 return LISTBOX_SetTabStops( descr, wParam, (LPINT)lParam );
2970 if (descr->caret_on)
2972 descr->caret_on = TRUE;
2973 if ((descr->focus_item != -1) && (descr->in_focus))
2974 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2978 if (!descr->caret_on)
2980 descr->caret_on = FALSE;
2981 if ((descr->focus_item != -1) && (descr->in_focus))
2982 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2985 case LB_GETLISTBOXINFO:
2986 FIXME("LB_GETLISTBOXINFO: stub!\n");
2990 return LISTBOX_Destroy( descr );
2993 InvalidateRect( descr->self, NULL, TRUE );
2997 LISTBOX_SetRedraw( descr, wParam != 0 );
3001 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3003 case WM_PRINTCLIENT:
3007 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( descr->self, &ps );
3008 ret = LISTBOX_Paint( descr, hdc );
3009 if( !wParam ) EndPaint( descr->self, &ps );
3013 LISTBOX_UpdateSize( descr );
3016 return (LRESULT)descr->font;
3018 LISTBOX_SetFont( descr, (HFONT)wParam );
3019 if (lParam) InvalidateRect( descr->self, 0, TRUE );
3022 descr->in_focus = TRUE;
3023 descr->caret_on = TRUE;
3024 if (descr->focus_item != -1)
3025 LISTBOX_DrawFocusRect( descr, TRUE );
3026 SEND_NOTIFICATION( descr, LBN_SETFOCUS );
3029 descr->in_focus = FALSE;
3030 if ((descr->focus_item != -1) && descr->caret_on)
3031 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3032 SEND_NOTIFICATION( descr, LBN_KILLFOCUS );
3035 return LISTBOX_HandleHScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3037 return LISTBOX_HandleVScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3039 if (wParam & (MK_SHIFT | MK_CONTROL))
3040 return DefWindowProcW( descr->self, msg, wParam, lParam );
3041 return LISTBOX_HandleMouseWheel( descr, (SHORT)HIWORD(wParam) );
3042 case WM_LBUTTONDOWN:
3044 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3045 (INT16)LOWORD(lParam),
3046 (INT16)HIWORD(lParam) );
3047 return LISTBOX_HandleLButtonDown( descr, wParam,
3048 (INT16)LOWORD(lParam),
3049 (INT16)HIWORD(lParam) );
3050 case WM_LBUTTONDBLCLK:
3052 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3053 (INT16)LOWORD(lParam),
3054 (INT16)HIWORD(lParam) );
3055 if (descr->style & LBS_NOTIFY)
3056 SEND_NOTIFICATION( descr, LBN_DBLCLK );
3059 if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) )
3061 BOOL captured = descr->captured;
3065 mousePos.x = (INT16)LOWORD(lParam);
3066 mousePos.y = (INT16)HIWORD(lParam);
3069 * If we are in a dropdown combobox, we simulate that
3070 * the mouse is captured to show the tracking of the item.
3072 if (GetClientRect(descr->self, &clientRect) && PtInRect( &clientRect, mousePos ))
3073 descr->captured = TRUE;
3075 LISTBOX_HandleMouseMove( descr, mousePos.x, mousePos.y);
3077 descr->captured = captured;
3079 else if (GetCapture() == descr->self)
3081 LISTBOX_HandleMouseMove( descr, (INT16)LOWORD(lParam),
3082 (INT16)HIWORD(lParam) );
3092 * If the mouse button "up" is not in the listbox,
3093 * we make sure there is no selection by re-selecting the
3094 * item that was selected when the listbox was made visible.
3096 mousePos.x = (INT16)LOWORD(lParam);
3097 mousePos.y = (INT16)HIWORD(lParam);
3099 GetClientRect(descr->self, &clientRect);
3102 * When the user clicks outside the combobox and the focus
3103 * is lost, the owning combobox will send a fake buttonup with
3104 * 0xFFFFFFF as the mouse location, we must also revert the
3105 * selection to the original selection.
3107 if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) )
3108 LISTBOX_MoveCaret( descr, lphc->droppedIndex, FALSE );
3110 return LISTBOX_HandleLButtonUp( descr );
3112 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3114 /* for some reason Windows makes it possible to
3115 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3117 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3118 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3119 && (wParam == VK_DOWN || wParam == VK_UP)) )
3121 COMBO_FlipListbox( lphc, FALSE, FALSE );
3125 return LISTBOX_HandleKeyDown( descr, wParam );
3130 charW = (WCHAR)wParam;
3133 CHAR charA = (CHAR)wParam;
3134 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
3136 return LISTBOX_HandleChar( descr, charW );
3139 return LISTBOX_HandleSystemTimer( descr );
3141 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
3144 HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
3145 wParam, (LPARAM)descr->self );
3146 TRACE("hbrush = %p\n", hbrush);
3148 hbrush = GetSysColorBrush(COLOR_WINDOW);
3151 GetClientRect(descr->self, &rect);
3152 FillRect((HDC)wParam, &rect, hbrush);
3157 if( lphc ) return 0;
3158 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3159 SendMessageA( descr->owner, msg, wParam, lParam );
3162 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3171 if ((msg >= WM_USER) && (msg < 0xc000))
3172 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3173 hwnd, msg, wParam, lParam );
3176 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3177 DefWindowProcA( hwnd, msg, wParam, lParam );