4 * Copyright 1996 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * This code was audited for completeness against the documented features
23 * of Comctl32.dll version 6.0 on Oct. 9, 2004, by Dimitrie O. Paun.
25 * Unless otherwise noted, we believe this code to be complete, as per
26 * the specification mentioned above.
27 * If you discover missing features, or bugs, please note them below.
33 * - LB_SETLOCALE: some FIXMEs remain
34 * - LBS_USETABSTOPS: some FIXMEs remain
44 #include "wine/winuser16.h"
45 #include "wine/winbase16.h"
46 #include "wine/unicode.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(listbox);
53 /* Items array granularity */
54 #define LB_ARRAY_GRANULARITY 16
56 /* Scrolling timeout in ms */
57 #define LB_SCROLL_TIMEOUT 50
59 /* Listbox system timer id */
62 /* flag listbox changed while setredraw false - internal style */
63 #define LBS_DISPLAYCHANGED 0x80000000
68 LPWSTR str; /* Item text */
69 BOOL selected; /* Is item selected? */
70 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
71 DWORD data; /* User data */
74 /* Listbox structure */
77 HWND self; /* Our own window handle */
78 HWND owner; /* Owner window to send notifications to */
79 UINT style; /* Window style */
80 INT width; /* Window width */
81 INT height; /* Window height */
82 LB_ITEMDATA *items; /* Array of items */
83 INT nb_items; /* Number of items */
84 INT top_item; /* Top visible item */
85 INT selected_item; /* Selected item */
86 INT focus_item; /* Item that has the focus */
87 INT anchor_item; /* Anchor item for extended selection */
88 INT item_height; /* Default item height */
89 INT page_size; /* Items per listbox page */
90 INT column_width; /* Column width for multi-column listboxes */
91 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
92 INT horz_pos; /* Horizontal position */
93 INT nb_tabs; /* Number of tabs in array */
94 INT *tabs; /* Array of tabs */
95 BOOL caret_on; /* Is caret on? */
96 BOOL captured; /* Is mouse captured? */
98 HFONT font; /* Current font */
99 LCID locale; /* Current locale for string comparisons */
100 LPHEADCOMBO lphc; /* ComboLBox */
104 #define IS_OWNERDRAW(descr) \
105 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
107 #define HAS_STRINGS(descr) \
108 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
111 #define IS_MULTISELECT(descr) \
112 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
113 !((descr)->style & LBS_NOSEL))
115 #define SEND_NOTIFICATION(descr,code) \
116 (SendMessageW( (descr)->owner, WM_COMMAND, \
117 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
119 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
121 /* Current timer status */
131 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
133 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
134 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
136 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect );
138 /*********************************************************************
139 * listbox class descriptor
141 const struct builtin_class_descr LISTBOX_builtin_class =
143 "ListBox", /* name */
144 CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
145 ListBoxWndProcA, /* procA */
146 ListBoxWndProcW, /* procW */
147 sizeof(LB_DESCR *), /* extra */
148 IDC_ARROW, /* cursor */
153 /*********************************************************************
154 * combolbox class descriptor
156 const struct builtin_class_descr COMBOLBOX_builtin_class =
158 "ComboLBox", /* name */
159 CS_DBLCLKS | CS_SAVEBITS, /* style */
160 ListBoxWndProcA, /* procA */
161 ListBoxWndProcW, /* procW */
162 sizeof(LB_DESCR *), /* extra */
163 IDC_ARROW, /* cursor */
168 /* check whether app is a Win 3.1 app */
169 inline static BOOL is_old_app( LB_DESCR *descr )
171 return (GetExpWinVer16( GetWindowLongPtrW(descr->self, GWLP_HINSTANCE) ) & 0xFF00 ) == 0x0300;
175 /***********************************************************************
178 void LISTBOX_Dump( LB_DESCR *descr )
183 TRACE( "Listbox:\n" );
184 TRACE( "hwnd=%p descr=%08x items=%d top=%d\n",
185 descr->self, (UINT)descr, descr->nb_items, descr->top_item );
186 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
188 TRACE( "%4d: %-40s %d %08lx %3d\n",
189 i, debugstr_w(item->str), item->selected, item->data, item->height );
194 /***********************************************************************
195 * LISTBOX_GetCurrentPageSize
197 * Return the current page size
199 static INT LISTBOX_GetCurrentPageSize( LB_DESCR *descr )
202 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
203 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
205 if ((height += descr->items[i].height) > descr->height) break;
207 if (i == descr->top_item) return 1;
208 else return i - descr->top_item;
212 /***********************************************************************
213 * LISTBOX_GetMaxTopIndex
215 * Return the maximum possible index for the top of the listbox.
217 static INT LISTBOX_GetMaxTopIndex( LB_DESCR *descr )
221 if (descr->style & LBS_OWNERDRAWVARIABLE)
223 page = descr->height;
224 for (max = descr->nb_items - 1; max >= 0; max--)
225 if ((page -= descr->items[max].height) < 0) break;
226 if (max < descr->nb_items - 1) max++;
228 else if (descr->style & LBS_MULTICOLUMN)
230 if ((page = descr->width / descr->column_width) < 1) page = 1;
231 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
232 max = (max - page) * descr->page_size;
236 max = descr->nb_items - descr->page_size;
238 if (max < 0) max = 0;
243 /***********************************************************************
244 * LISTBOX_UpdateScroll
246 * Update the scrollbars. Should be called whenever the content
247 * of the listbox changes.
249 static void LISTBOX_UpdateScroll( LB_DESCR *descr )
253 /* Check the listbox scroll bar flags individually before we call
254 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
255 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
256 scroll bar when we do not need one.
257 if (!(descr->style & WS_VSCROLL)) return;
260 /* It is important that we check descr->style, and not wnd->dwStyle,
261 for WS_VSCROLL, as the former is exactly the one passed in
262 argument to CreateWindow.
263 In Windows (and from now on in Wine :) a listbox created
264 with such a style (no WS_SCROLL) does not update
265 the scrollbar with listbox-related data, thus letting
266 the programmer use it for his/her own purposes. */
268 if (descr->style & LBS_NOREDRAW) return;
269 info.cbSize = sizeof(info);
271 if (descr->style & LBS_MULTICOLUMN)
274 info.nMax = (descr->nb_items - 1) / descr->page_size;
275 info.nPos = descr->top_item / descr->page_size;
276 info.nPage = descr->width / descr->column_width;
277 if (info.nPage < 1) info.nPage = 1;
278 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
279 if (descr->style & LBS_DISABLENOSCROLL)
280 info.fMask |= SIF_DISABLENOSCROLL;
281 if (descr->style & WS_HSCROLL)
282 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
284 info.fMask = SIF_RANGE;
285 if (descr->style & WS_VSCROLL)
286 SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
291 info.nMax = descr->nb_items - 1;
292 info.nPos = descr->top_item;
293 info.nPage = LISTBOX_GetCurrentPageSize( descr );
294 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
295 if (descr->style & LBS_DISABLENOSCROLL)
296 info.fMask |= SIF_DISABLENOSCROLL;
297 if (descr->style & WS_VSCROLL)
298 SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
300 if (descr->horz_extent)
303 info.nMax = descr->horz_extent - 1;
304 info.nPos = descr->horz_pos;
305 info.nPage = descr->width;
306 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
307 if (descr->style & LBS_DISABLENOSCROLL)
308 info.fMask |= SIF_DISABLENOSCROLL;
309 if (descr->style & WS_HSCROLL)
310 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
316 /***********************************************************************
319 * Set the top item of the listbox, scrolling up or down if necessary.
321 static LRESULT LISTBOX_SetTopItem( LB_DESCR *descr, INT index, BOOL scroll )
323 INT max = LISTBOX_GetMaxTopIndex( descr );
324 if (index > max) index = max;
325 if (index < 0) index = 0;
326 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
327 if (descr->top_item == index) return LB_OKAY;
328 if (descr->style & LBS_MULTICOLUMN)
330 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
331 if (scroll && (abs(diff) < descr->width))
332 ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
333 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
341 if (descr->style & LBS_OWNERDRAWVARIABLE)
345 if (index > descr->top_item)
347 for (i = index - 1; i >= descr->top_item; i--)
348 diff -= descr->items[i].height;
352 for (i = index; i < descr->top_item; i++)
353 diff += descr->items[i].height;
357 diff = (descr->top_item - index) * descr->item_height;
359 if (abs(diff) < descr->height)
360 ScrollWindowEx( descr->self, 0, diff, NULL, NULL, 0, NULL,
361 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
365 if (!scroll) InvalidateRect( descr->self, NULL, TRUE );
366 descr->top_item = index;
367 LISTBOX_UpdateScroll( descr );
372 /***********************************************************************
375 * Update the page size. Should be called when the size of
376 * the client area or the item height changes.
378 static void LISTBOX_UpdatePage( LB_DESCR *descr )
382 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
384 if (page_size == descr->page_size) return;
385 descr->page_size = page_size;
386 if (descr->style & LBS_MULTICOLUMN)
387 InvalidateRect( descr->self, NULL, TRUE );
388 LISTBOX_SetTopItem( descr, descr->top_item, FALSE );
392 /***********************************************************************
395 * Update the size of the listbox. Should be called when the size of
396 * the client area changes.
398 static void LISTBOX_UpdateSize( LB_DESCR *descr )
402 GetClientRect( descr->self, &rect );
403 descr->width = rect.right - rect.left;
404 descr->height = rect.bottom - rect.top;
405 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
410 GetWindowRect( descr->self, &rect );
411 if(descr->item_height != 0)
412 remaining = descr->height % descr->item_height;
415 if ((descr->height > descr->item_height) && remaining)
417 if (is_old_app(descr))
418 { /* give a margin for error to 16 bits programs - if we need
419 less than the height of the nonclient area, round to the
420 *next* number of items */
421 int ncheight = rect.bottom - rect.top - descr->height;
422 if ((descr->item_height - remaining) <= ncheight)
423 remaining = remaining - descr->item_height;
425 TRACE("[%p]: changing height %d -> %d\n",
426 descr->self, descr->height, descr->height - remaining );
427 SetWindowPos( descr->self, 0, 0, 0, rect.right - rect.left,
428 rect.bottom - rect.top - remaining,
429 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
433 TRACE("[%p]: new size = %d,%d\n", descr->self, descr->width, descr->height );
434 LISTBOX_UpdatePage( descr );
435 LISTBOX_UpdateScroll( descr );
437 /* Invalidate the focused item so it will be repainted correctly */
438 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
440 InvalidateRect( descr->self, &rect, FALSE );
445 /***********************************************************************
446 * LISTBOX_GetItemRect
448 * Get the rectangle enclosing an item, in listbox client coordinates.
449 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
451 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect )
453 /* Index <= 0 is legal even on empty listboxes */
454 if (index && (index >= descr->nb_items)) return -1;
455 SetRect( rect, 0, 0, descr->width, descr->height );
456 if (descr->style & LBS_MULTICOLUMN)
458 INT col = (index / descr->page_size) -
459 (descr->top_item / descr->page_size);
460 rect->left += col * descr->column_width;
461 rect->right = rect->left + descr->column_width;
462 rect->top += (index % descr->page_size) * descr->item_height;
463 rect->bottom = rect->top + descr->item_height;
465 else if (descr->style & LBS_OWNERDRAWVARIABLE)
468 rect->right += descr->horz_pos;
469 if ((index >= 0) && (index < descr->nb_items))
471 if (index < descr->top_item)
473 for (i = descr->top_item-1; i >= index; i--)
474 rect->top -= descr->items[i].height;
478 for (i = descr->top_item; i < index; i++)
479 rect->top += descr->items[i].height;
481 rect->bottom = rect->top + descr->items[index].height;
487 rect->top += (index - descr->top_item) * descr->item_height;
488 rect->bottom = rect->top + descr->item_height;
489 rect->right += descr->horz_pos;
492 return ((rect->left < descr->width) && (rect->right > 0) &&
493 (rect->top < descr->height) && (rect->bottom > 0));
497 /***********************************************************************
498 * LISTBOX_GetItemFromPoint
500 * Return the item nearest from point (x,y) (in client coordinates).
502 static INT LISTBOX_GetItemFromPoint( LB_DESCR *descr, INT x, INT y )
504 INT index = descr->top_item;
506 if (!descr->nb_items) return -1; /* No items */
507 if (descr->style & LBS_OWNERDRAWVARIABLE)
512 while (index < descr->nb_items)
514 if ((pos += descr->items[index].height) > y) break;
523 if ((pos -= descr->items[index].height) <= y) break;
527 else if (descr->style & LBS_MULTICOLUMN)
529 if (y >= descr->item_height * descr->page_size) return -1;
530 if (y >= 0) index += y / descr->item_height;
531 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
532 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
536 index += (y / descr->item_height);
538 if (index < 0) return 0;
539 if (index >= descr->nb_items) return -1;
544 /***********************************************************************
549 static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect,
550 INT index, UINT action, BOOL ignoreFocus )
552 LB_ITEMDATA *item = NULL;
553 if (index < descr->nb_items) item = &descr->items[index];
555 if (IS_OWNERDRAW(descr))
560 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
564 if (action == ODA_FOCUS)
565 DrawFocusRect( hdc, rect );
567 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
571 /* some programs mess with the clipping region when
572 drawing the item, *and* restore the previous region
573 after they are done, so a region has better to exist
574 else everything ends clipped */
575 GetClientRect(descr->self, &r);
576 hrgn = CreateRectRgnIndirect(&r);
577 SelectClipRgn( hdc, hrgn);
578 DeleteObject( hrgn );
580 dis.CtlType = ODT_LISTBOX;
582 dis.hwndItem = descr->self;
583 dis.itemAction = action;
587 if (item && item->selected) dis.itemState |= ODS_SELECTED;
588 if (!ignoreFocus && (descr->focus_item == index) &&
590 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
591 if (!IsWindowEnabled(descr->self)) dis.itemState |= ODS_DISABLED;
592 dis.itemData = item ? item->data : 0;
594 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%ld,%ld-%ld,%ld\n",
595 descr->self, index, item ? debugstr_w(item->str) : "", action,
596 dis.itemState, rect->left, rect->top, rect->right, rect->bottom );
597 SendMessageW(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
601 COLORREF oldText = 0, oldBk = 0;
603 if (action == ODA_FOCUS)
605 DrawFocusRect( hdc, rect );
608 if (item && item->selected)
610 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
611 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
614 TRACE("[%p]: painting %d (%s) action=%02x rect=%ld,%ld-%ld,%ld\n",
615 descr->self, index, item ? debugstr_w(item->str) : "", action,
616 rect->left, rect->top, rect->right, rect->bottom );
618 ExtTextOutW( hdc, rect->left + 1, rect->top,
619 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
620 else if (!(descr->style & LBS_USETABSTOPS))
621 ExtTextOutW( hdc, rect->left + 1, rect->top,
622 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
623 strlenW(item->str), NULL );
626 /* Output empty string to paint background in the full width. */
627 ExtTextOutW( hdc, rect->left + 1, rect->top,
628 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
629 TabbedTextOutW( hdc, rect->left + 1 , rect->top,
630 item->str, strlenW(item->str),
631 descr->nb_tabs, descr->tabs, 0);
633 if (item && item->selected)
635 SetBkColor( hdc, oldBk );
636 SetTextColor( hdc, oldText );
638 if (!ignoreFocus && (descr->focus_item == index) &&
640 (descr->in_focus)) DrawFocusRect( hdc, rect );
645 /***********************************************************************
648 * Change the redraw flag.
650 static void LISTBOX_SetRedraw( LB_DESCR *descr, BOOL on )
654 if (!(descr->style & LBS_NOREDRAW)) return;
655 descr->style &= ~LBS_NOREDRAW;
656 if (descr->style & LBS_DISPLAYCHANGED)
657 { /* page was changed while setredraw false, refresh automatically */
658 InvalidateRect(descr->self, NULL, TRUE);
659 if ((descr->top_item + descr->page_size) > descr->nb_items)
660 { /* reset top of page if less than number of items/page */
661 descr->top_item = descr->nb_items - descr->page_size;
662 if (descr->top_item < 0) descr->top_item = 0;
664 descr->style &= ~LBS_DISPLAYCHANGED;
666 LISTBOX_UpdateScroll( descr );
668 else descr->style |= LBS_NOREDRAW;
672 /***********************************************************************
673 * LISTBOX_RepaintItem
675 * Repaint a single item synchronously.
677 static void LISTBOX_RepaintItem( LB_DESCR *descr, INT index, UINT action )
682 HBRUSH hbrush, oldBrush = 0;
684 /* Do not repaint the item if the item is not visible */
685 if (!IsWindowVisible(descr->self)) return;
686 if (descr->style & LBS_NOREDRAW)
688 descr->style |= LBS_DISPLAYCHANGED;
691 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
692 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
693 if (descr->font) oldFont = SelectObject( hdc, descr->font );
694 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
695 (WPARAM)hdc, (LPARAM)descr->self );
696 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
697 if (!IsWindowEnabled(descr->self))
698 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
699 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
700 LISTBOX_PaintItem( descr, hdc, &rect, index, action, FALSE );
701 if (oldFont) SelectObject( hdc, oldFont );
702 if (oldBrush) SelectObject( hdc, oldBrush );
703 ReleaseDC( descr->self, hdc );
707 /***********************************************************************
708 * LISTBOX_InitStorage
710 static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items )
714 nb_items += LB_ARRAY_GRANULARITY - 1;
715 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
717 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
718 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
719 nb_items * sizeof(LB_ITEMDATA));
722 item = HeapAlloc( GetProcessHeap(), 0,
723 nb_items * sizeof(LB_ITEMDATA));
728 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
736 /***********************************************************************
737 * LISTBOX_SetTabStops
739 static BOOL LISTBOX_SetTabStops( LB_DESCR *descr, INT count, LPINT tabs, BOOL short_ints )
741 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
742 HeapFree( GetProcessHeap(), 0, descr->tabs );
743 if (!(descr->nb_tabs = count))
748 /* FIXME: count = 1 */
749 if (!(descr->tabs = (INT *)HeapAlloc( GetProcessHeap(), 0,
750 descr->nb_tabs * sizeof(INT) )))
755 LPINT16 p = (LPINT16)tabs;
757 TRACE("[%p]: settabstops ", descr->self );
758 for (i = 0; i < descr->nb_tabs; i++) {
759 descr->tabs[i] = *p++<<1; /* FIXME */
760 TRACE("%hd ", descr->tabs[i]);
764 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
765 /* FIXME: repaint the window? */
770 /***********************************************************************
773 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPWSTR buffer, BOOL unicode )
775 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
776 if (HAS_STRINGS(descr))
780 DWORD len = strlenW(descr->items[index].str);
783 return WideCharToMultiByte( CP_ACP, 0, descr->items[index].str, len,
784 NULL, 0, NULL, NULL );
787 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
791 strcpyW( buffer, descr->items[index].str );
792 return strlenW(buffer);
796 return WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, (LPSTR)buffer, 0x7FFFFFFF, NULL, NULL) - 1;
800 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
801 return sizeof(DWORD);
806 /***********************************************************************
807 * LISTBOX_FindStringPos
809 * Find the nearest string located before a given string in sort order.
810 * If 'exact' is TRUE, return an error if we don't get an exact match.
812 static INT LISTBOX_FindStringPos( LB_DESCR *descr, LPCWSTR str, BOOL exact )
814 INT index, min, max, res = -1;
816 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
818 max = descr->nb_items;
821 index = (min + max) / 2;
822 if (HAS_STRINGS(descr))
823 res = lstrcmpiW( str, descr->items[index].str);
826 COMPAREITEMSTRUCT cis;
827 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
829 cis.CtlType = ODT_LISTBOX;
831 cis.hwndItem = descr->self;
832 /* note that some application (MetaStock) expects the second item
833 * to be in the listbox */
835 cis.itemData1 = (DWORD)str;
837 cis.itemData2 = descr->items[index].data;
838 cis.dwLocaleId = descr->locale;
839 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
841 if (!res) return index;
842 if (res < 0) max = index;
843 else min = index + 1;
845 return exact ? -1 : max;
849 /***********************************************************************
850 * LISTBOX_FindFileStrPos
852 * Find the nearest string located before a given string in directory
853 * sort order (i.e. first files, then directories, then drives).
855 static INT LISTBOX_FindFileStrPos( LB_DESCR *descr, LPCWSTR str )
857 INT min, max, res = -1;
859 if (!HAS_STRINGS(descr))
860 return LISTBOX_FindStringPos( descr, str, FALSE );
862 max = descr->nb_items;
865 INT index = (min + max) / 2;
866 LPCWSTR p = descr->items[index].str;
867 if (*p == '[') /* drive or directory */
869 if (*str != '[') res = -1;
870 else if (p[1] == '-') /* drive */
872 if (str[1] == '-') res = str[2] - p[2];
877 if (str[1] == '-') res = 1;
878 else res = lstrcmpiW( str, p );
883 if (*str == '[') res = 1;
884 else res = lstrcmpiW( str, p );
886 if (!res) return index;
887 if (res < 0) max = index;
888 else min = index + 1;
894 /***********************************************************************
897 * Find the item beginning with a given string.
899 static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exact )
904 if (start >= descr->nb_items) start = -1;
905 item = descr->items + start + 1;
906 if (HAS_STRINGS(descr))
908 if (!str || ! str[0] ) return LB_ERR;
911 for (i = start + 1; i < descr->nb_items; i++, item++)
912 if (!lstrcmpiW( str, item->str )) return i;
913 for (i = 0, item = descr->items; i <= start; i++, item++)
914 if (!lstrcmpiW( str, item->str )) return i;
918 /* Special case for drives and directories: ignore prefix */
919 #define CHECK_DRIVE(item) \
920 if ((item)->str[0] == '[') \
922 if (!strncmpiW( str, (item)->str+1, len )) return i; \
923 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
927 INT len = strlenW(str);
928 for (i = start + 1; i < descr->nb_items; i++, item++)
930 if (!strncmpiW( str, item->str, len )) return i;
933 for (i = 0, item = descr->items; i <= start; i++, item++)
935 if (!strncmpiW( str, item->str, len )) return i;
943 if (exact && (descr->style & LBS_SORT))
944 /* If sorted, use a WM_COMPAREITEM binary search */
945 return LISTBOX_FindStringPos( descr, str, TRUE );
947 /* Otherwise use a linear search */
948 for (i = start + 1; i < descr->nb_items; i++, item++)
949 if (item->data == (DWORD)str) return i;
950 for (i = 0, item = descr->items; i <= start; i++, item++)
951 if (item->data == (DWORD)str) return i;
957 /***********************************************************************
958 * LISTBOX_GetSelCount
960 static LRESULT LISTBOX_GetSelCount( LB_DESCR *descr )
963 LB_ITEMDATA *item = descr->items;
965 if (!(descr->style & LBS_MULTIPLESEL) ||
966 (descr->style & LBS_NOSEL))
968 for (i = count = 0; i < descr->nb_items; i++, item++)
969 if (item->selected) count++;
974 /***********************************************************************
975 * LISTBOX_GetSelItems16
977 static LRESULT LISTBOX_GetSelItems16( LB_DESCR *descr, INT16 max, LPINT16 array )
980 LB_ITEMDATA *item = descr->items;
982 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
983 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
984 if (item->selected) array[count++] = (INT16)i;
989 /***********************************************************************
990 * LISTBOX_GetSelItems
992 static LRESULT LISTBOX_GetSelItems( LB_DESCR *descr, INT max, LPINT array )
995 LB_ITEMDATA *item = descr->items;
997 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
998 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
999 if (item->selected) array[count++] = i;
1004 /***********************************************************************
1007 static LRESULT LISTBOX_Paint( LB_DESCR *descr, HDC hdc )
1009 INT i, col_pos = descr->page_size - 1;
1011 RECT focusRect = {-1, -1, -1, -1};
1013 HBRUSH hbrush, oldBrush = 0;
1015 if (descr->style & LBS_NOREDRAW) return 0;
1017 SetRect( &rect, 0, 0, descr->width, descr->height );
1018 if (descr->style & LBS_MULTICOLUMN)
1019 rect.right = rect.left + descr->column_width;
1020 else if (descr->horz_pos)
1022 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
1023 rect.right += descr->horz_pos;
1026 if (descr->font) oldFont = SelectObject( hdc, descr->font );
1027 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1028 (WPARAM)hdc, (LPARAM)descr->self );
1029 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1030 if (!IsWindowEnabled(descr->self)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1032 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1035 /* Special case for empty listbox: paint focus rect */
1036 rect.bottom = rect.top + descr->item_height;
1037 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1038 &rect, NULL, 0, NULL );
1039 LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, FALSE );
1040 rect.top = rect.bottom;
1043 /* Paint all the item, regarding the selection
1044 Focus state will be painted after */
1046 for (i = descr->top_item; i < descr->nb_items; i++)
1048 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1049 rect.bottom = rect.top + descr->item_height;
1051 rect.bottom = rect.top + descr->items[i].height;
1053 if (i == descr->focus_item)
1055 /* keep the focus rect, to paint the focus item after */
1056 focusRect.left = rect.left;
1057 focusRect.right = rect.right;
1058 focusRect.top = rect.top;
1059 focusRect.bottom = rect.bottom;
1061 LISTBOX_PaintItem( descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1062 rect.top = rect.bottom;
1064 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1066 if (!IS_OWNERDRAW(descr))
1068 /* Clear the bottom of the column */
1069 if (rect.top < descr->height)
1071 rect.bottom = descr->height;
1072 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1073 &rect, NULL, 0, NULL );
1077 /* Go to the next column */
1078 rect.left += descr->column_width;
1079 rect.right += descr->column_width;
1081 col_pos = descr->page_size - 1;
1086 if (rect.top >= descr->height) break;
1090 /* Paint the focus item now */
1091 if (focusRect.top != focusRect.bottom &&
1092 descr->caret_on && descr->in_focus)
1093 LISTBOX_PaintItem( descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1095 if (!IS_OWNERDRAW(descr))
1097 /* Clear the remainder of the client area */
1098 if (rect.top < descr->height)
1100 rect.bottom = descr->height;
1101 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1102 &rect, NULL, 0, NULL );
1104 if (rect.right < descr->width)
1106 rect.left = rect.right;
1107 rect.right = descr->width;
1109 rect.bottom = descr->height;
1110 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1111 &rect, NULL, 0, NULL );
1114 if (oldFont) SelectObject( hdc, oldFont );
1115 if (oldBrush) SelectObject( hdc, oldBrush );
1120 /***********************************************************************
1121 * LISTBOX_InvalidateItems
1123 * Invalidate all items from a given item. If the specified item is not
1124 * visible, nothing happens.
1126 static void LISTBOX_InvalidateItems( LB_DESCR *descr, INT index )
1130 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1132 if (descr->style & LBS_NOREDRAW)
1134 descr->style |= LBS_DISPLAYCHANGED;
1137 rect.bottom = descr->height;
1138 InvalidateRect( descr->self, &rect, TRUE );
1139 if (descr->style & LBS_MULTICOLUMN)
1141 /* Repaint the other columns */
1142 rect.left = rect.right;
1143 rect.right = descr->width;
1145 InvalidateRect( descr->self, &rect, TRUE );
1150 static void LISTBOX_InvalidateItemRect( LB_DESCR *descr, INT index )
1154 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1155 InvalidateRect( descr->self, &rect, TRUE );
1158 /***********************************************************************
1159 * LISTBOX_GetItemHeight
1161 static LRESULT LISTBOX_GetItemHeight( LB_DESCR *descr, INT index )
1163 if (descr->style & LBS_OWNERDRAWVARIABLE)
1165 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1166 return descr->items[index].height;
1168 else return descr->item_height;
1172 /***********************************************************************
1173 * LISTBOX_SetItemHeight
1175 static LRESULT LISTBOX_SetItemHeight( LB_DESCR *descr, INT index, INT height, BOOL repaint )
1177 if (!height) height = 1;
1179 if (descr->style & LBS_OWNERDRAWVARIABLE)
1181 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1182 TRACE("[%p]: item %d height = %d\n", descr->self, index, height );
1183 descr->items[index].height = height;
1184 LISTBOX_UpdateScroll( descr );
1186 LISTBOX_InvalidateItems( descr, index );
1188 else if (height != descr->item_height)
1190 TRACE("[%p]: new height = %d\n", descr->self, height );
1191 descr->item_height = height;
1192 LISTBOX_UpdatePage( descr );
1193 LISTBOX_UpdateScroll( descr );
1195 InvalidateRect( descr->self, 0, TRUE );
1201 /***********************************************************************
1202 * LISTBOX_SetHorizontalPos
1204 static void LISTBOX_SetHorizontalPos( LB_DESCR *descr, INT pos )
1208 if (pos > descr->horz_extent - descr->width)
1209 pos = descr->horz_extent - descr->width;
1210 if (pos < 0) pos = 0;
1211 if (!(diff = descr->horz_pos - pos)) return;
1212 TRACE("[%p]: new horz pos = %d\n", descr->self, pos );
1213 descr->horz_pos = pos;
1214 LISTBOX_UpdateScroll( descr );
1215 if (abs(diff) < descr->width)
1218 /* Invalidate the focused item so it will be repainted correctly */
1219 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
1220 InvalidateRect( descr->self, &rect, TRUE );
1221 ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
1222 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1225 InvalidateRect( descr->self, NULL, TRUE );
1229 /***********************************************************************
1230 * LISTBOX_SetHorizontalExtent
1232 static LRESULT LISTBOX_SetHorizontalExtent( LB_DESCR *descr, INT extent )
1234 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1236 if (extent <= 0) extent = 1;
1237 if (extent == descr->horz_extent) return LB_OKAY;
1238 TRACE("[%p]: new horz extent = %d\n", descr->self, extent );
1239 descr->horz_extent = extent;
1240 if (descr->horz_pos > extent - descr->width)
1241 LISTBOX_SetHorizontalPos( descr, extent - descr->width );
1243 LISTBOX_UpdateScroll( descr );
1248 /***********************************************************************
1249 * LISTBOX_SetColumnWidth
1251 static LRESULT LISTBOX_SetColumnWidth( LB_DESCR *descr, INT width)
1253 if (width == descr->column_width) return LB_OKAY;
1254 TRACE("[%p]: new column width = %d\n", descr->self, width );
1255 descr->column_width = width;
1256 LISTBOX_UpdatePage( descr );
1261 /***********************************************************************
1264 * Returns the item height.
1266 static INT LISTBOX_SetFont( LB_DESCR *descr, HFONT font )
1274 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE )))
1276 ERR("unable to get DC.\n" );
1279 if (font) oldFont = SelectObject( hdc, font );
1280 GetTextMetricsW( hdc, &tm );
1281 if (oldFont) SelectObject( hdc, oldFont );
1282 ReleaseDC( descr->self, hdc );
1283 if (!IS_OWNERDRAW(descr))
1284 LISTBOX_SetItemHeight( descr, 0, tm.tmHeight, FALSE );
1289 /***********************************************************************
1290 * LISTBOX_MakeItemVisible
1292 * Make sure that a given item is partially or fully visible.
1294 static void LISTBOX_MakeItemVisible( LB_DESCR *descr, INT index, BOOL fully )
1298 if (index <= descr->top_item) top = index;
1299 else if (descr->style & LBS_MULTICOLUMN)
1301 INT cols = descr->width;
1302 if (!fully) cols += descr->column_width - 1;
1303 if (cols >= descr->column_width) cols /= descr->column_width;
1305 if (index < descr->top_item + (descr->page_size * cols)) return;
1306 top = index - descr->page_size * (cols - 1);
1308 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1310 INT height = fully ? descr->items[index].height : 1;
1311 for (top = index; top > descr->top_item; top--)
1312 if ((height += descr->items[top-1].height) > descr->height) break;
1316 if (index < descr->top_item + descr->page_size) return;
1317 if (!fully && (index == descr->top_item + descr->page_size) &&
1318 (descr->height > (descr->page_size * descr->item_height))) return;
1319 top = index - descr->page_size + 1;
1321 LISTBOX_SetTopItem( descr, top, TRUE );
1324 /***********************************************************************
1325 * LISTBOX_SetCaretIndex
1328 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1331 static LRESULT LISTBOX_SetCaretIndex( LB_DESCR *descr, INT index, BOOL fully_visible )
1333 INT oldfocus = descr->focus_item;
1335 if (descr->style & LBS_NOSEL) return LB_ERR;
1336 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1337 if (index == oldfocus) return LB_OKAY;
1338 descr->focus_item = index;
1339 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1340 LISTBOX_RepaintItem( descr, oldfocus, ODA_FOCUS );
1342 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1343 if (descr->caret_on && (descr->in_focus))
1344 LISTBOX_RepaintItem( descr, index, ODA_FOCUS );
1350 /***********************************************************************
1351 * LISTBOX_SelectItemRange
1353 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1355 static LRESULT LISTBOX_SelectItemRange( LB_DESCR *descr, INT first,
1360 /* A few sanity checks */
1362 if (descr->style & LBS_NOSEL) return LB_ERR;
1363 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1364 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1365 if (last == -1) last = descr->nb_items - 1;
1366 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1367 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1369 if (on) /* Turn selection on */
1371 for (i = first; i <= last; i++)
1373 if (descr->items[i].selected) continue;
1374 descr->items[i].selected = TRUE;
1375 LISTBOX_InvalidateItemRect(descr, i);
1378 else /* Turn selection off */
1380 for (i = first; i <= last; i++)
1382 if (!descr->items[i].selected) continue;
1383 descr->items[i].selected = FALSE;
1384 LISTBOX_InvalidateItemRect(descr, i);
1390 /***********************************************************************
1391 * LISTBOX_SetSelection
1393 static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index,
1394 BOOL on, BOOL send_notify )
1396 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1398 if (descr->style & LBS_NOSEL)
1400 descr->selected_item = index;
1403 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1404 if (descr->style & LBS_MULTIPLESEL)
1406 if (index == -1) /* Select all items */
1407 return LISTBOX_SelectItemRange( descr, 0, -1, on );
1408 else /* Only one item */
1409 return LISTBOX_SelectItemRange( descr, index, index, on );
1413 INT oldsel = descr->selected_item;
1414 if (index == oldsel) return LB_OKAY;
1415 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1416 if (index != -1) descr->items[index].selected = TRUE;
1417 descr->selected_item = index;
1418 if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT );
1419 if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT );
1420 if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr,
1421 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1423 if( descr->lphc ) /* set selection change flag for parent combo */
1424 descr->lphc->wState |= CBF_SELCHANGE;
1430 /***********************************************************************
1433 * Change the caret position and extend the selection to the new caret.
1435 static void LISTBOX_MoveCaret( LB_DESCR *descr, INT index, BOOL fully_visible )
1437 INT oldfocus = descr->focus_item;
1439 if ((index < 0) || (index >= descr->nb_items))
1442 /* Important, repaint needs to be done in this order if
1443 you want to mimic Windows behavior:
1444 1. Remove the focus and paint the item
1445 2. Remove the selection and paint the item(s)
1446 3. Set the selection and repaint the item(s)
1447 4. Set the focus to 'index' and repaint the item */
1449 /* 1. remove the focus and repaint the item */
1450 descr->focus_item = -1;
1451 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1452 LISTBOX_RepaintItem( descr, oldfocus, ODA_FOCUS );
1454 /* 2. then turn off the previous selection */
1455 /* 3. repaint the new selected item */
1456 if (descr->style & LBS_EXTENDEDSEL)
1458 if (descr->anchor_item != -1)
1460 INT first = min( index, descr->anchor_item );
1461 INT last = max( index, descr->anchor_item );
1463 LISTBOX_SelectItemRange( descr, 0, first - 1, FALSE );
1464 LISTBOX_SelectItemRange( descr, last + 1, -1, FALSE );
1465 LISTBOX_SelectItemRange( descr, first, last, TRUE );
1468 else if (!(descr->style & LBS_MULTIPLESEL))
1470 /* Set selection to new caret item */
1471 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
1474 /* 4. repaint the new item with the focus */
1475 descr->focus_item = index;
1476 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1477 if (descr->caret_on && (descr->in_focus))
1478 LISTBOX_RepaintItem( descr, index, ODA_FOCUS );
1482 /***********************************************************************
1483 * LISTBOX_InsertItem
1485 static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index,
1486 LPWSTR str, DWORD data )
1490 INT oldfocus = descr->focus_item;
1492 if (index == -1) index = descr->nb_items;
1493 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1494 if (!descr->items) max_items = 0;
1495 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1496 if (descr->nb_items == max_items)
1498 /* We need to grow the array */
1499 max_items += LB_ARRAY_GRANULARITY;
1501 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1502 max_items * sizeof(LB_ITEMDATA) );
1504 item = HeapAlloc( GetProcessHeap(), 0,
1505 max_items * sizeof(LB_ITEMDATA) );
1508 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1511 descr->items = item;
1514 /* Insert the item structure */
1516 item = &descr->items[index];
1517 if (index < descr->nb_items)
1518 RtlMoveMemory( item + 1, item,
1519 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1523 item->selected = FALSE;
1526 /* Get item height */
1528 if (descr->style & LBS_OWNERDRAWVARIABLE)
1530 MEASUREITEMSTRUCT mis;
1531 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1533 mis.CtlType = ODT_LISTBOX;
1536 mis.itemData = descr->items[index].data;
1537 mis.itemHeight = descr->item_height;
1538 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1539 item->height = mis.itemHeight ? mis.itemHeight : 1;
1540 TRACE("[%p]: measure item %d (%s) = %d\n",
1541 descr->self, index, str ? debugstr_w(str) : "", item->height );
1544 /* Repaint the items */
1546 LISTBOX_UpdateScroll( descr );
1547 LISTBOX_InvalidateItems( descr, index );
1549 /* Move selection and focused item */
1550 /* If listbox was empty, set focus to the first item */
1551 if (descr->nb_items == 1)
1552 LISTBOX_SetCaretIndex( descr, 0, FALSE );
1553 /* single select don't change selection index in win31 */
1554 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1556 descr->selected_item++;
1557 LISTBOX_SetSelection( descr, descr->selected_item-1, TRUE, FALSE );
1561 if (index <= descr->selected_item)
1563 descr->selected_item++;
1564 descr->focus_item = oldfocus; /* focus not changed */
1571 /***********************************************************************
1572 * LISTBOX_InsertString
1574 static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index, LPCWSTR str )
1576 LPWSTR new_str = NULL;
1580 if (HAS_STRINGS(descr))
1582 static const WCHAR empty_stringW[] = { 0 };
1583 if (!str) str = empty_stringW;
1584 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1586 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1589 strcpyW(new_str, str);
1591 else data = (DWORD)str;
1593 if (index == -1) index = descr->nb_items;
1594 if ((ret = LISTBOX_InsertItem( descr, index, new_str, data )) != 0)
1596 HeapFree( GetProcessHeap(), 0, new_str );
1600 TRACE("[%p]: added item %d %s\n",
1601 descr->self, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1606 /***********************************************************************
1607 * LISTBOX_DeleteItem
1609 * Delete the content of an item. 'index' must be a valid index.
1611 static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index )
1613 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1614 * while Win95 sends it for all items with user data.
1615 * It's probably better to send it too often than not
1616 * often enough, so this is what we do here.
1618 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1620 DELETEITEMSTRUCT dis;
1621 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1623 dis.CtlType = ODT_LISTBOX;
1626 dis.hwndItem = descr->self;
1627 dis.itemData = descr->items[index].data;
1628 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1630 if (HAS_STRINGS(descr) && descr->items[index].str)
1631 HeapFree( GetProcessHeap(), 0, descr->items[index].str );
1635 /***********************************************************************
1636 * LISTBOX_RemoveItem
1638 * Remove an item from the listbox and delete its content.
1640 static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index )
1645 if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1646 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1648 /* We need to invalidate the original rect instead of the updated one. */
1649 LISTBOX_InvalidateItems( descr, index );
1651 LISTBOX_DeleteItem( descr, index );
1653 /* Remove the item */
1655 item = &descr->items[index];
1656 if (index < descr->nb_items-1)
1657 RtlMoveMemory( item, item + 1,
1658 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1660 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1662 /* Shrink the item array if possible */
1664 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1665 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1667 max_items -= LB_ARRAY_GRANULARITY;
1668 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1669 max_items * sizeof(LB_ITEMDATA) );
1670 if (item) descr->items = item;
1672 /* Repaint the items */
1674 LISTBOX_UpdateScroll( descr );
1675 /* if we removed the scrollbar, reset the top of the list
1676 (correct for owner-drawn ???) */
1677 if (descr->nb_items == descr->page_size)
1678 LISTBOX_SetTopItem( descr, 0, TRUE );
1680 /* Move selection and focused item */
1681 if (!IS_MULTISELECT(descr))
1683 if (index == descr->selected_item)
1684 descr->selected_item = -1;
1685 else if (index < descr->selected_item)
1687 descr->selected_item--;
1688 if (ISWIN31) /* win 31 do not change the selected item number */
1689 LISTBOX_SetSelection( descr, descr->selected_item + 1, TRUE, FALSE);
1693 if (descr->focus_item >= descr->nb_items)
1695 descr->focus_item = descr->nb_items - 1;
1696 if (descr->focus_item < 0) descr->focus_item = 0;
1702 /***********************************************************************
1703 * LISTBOX_ResetContent
1705 static void LISTBOX_ResetContent( LB_DESCR *descr )
1709 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( descr, i );
1710 HeapFree( GetProcessHeap(), 0, descr->items );
1711 descr->nb_items = 0;
1712 descr->top_item = 0;
1713 descr->selected_item = -1;
1714 descr->focus_item = 0;
1715 descr->anchor_item = -1;
1716 descr->items = NULL;
1720 /***********************************************************************
1723 static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count )
1727 if (HAS_STRINGS(descr)) return LB_ERR;
1728 /* FIXME: this is far from optimal... */
1729 if (count > descr->nb_items)
1731 while (count > descr->nb_items)
1732 if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0)
1735 else if (count < descr->nb_items)
1737 while (count < descr->nb_items)
1738 if ((ret = LISTBOX_RemoveItem( descr, -1 )) < 0)
1745 /***********************************************************************
1748 static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib,
1749 LPCWSTR filespec, BOOL long_names )
1752 LRESULT ret = LB_OKAY;
1753 WIN32_FIND_DATAW entry;
1756 /* don't scan directory if we just want drives exclusively */
1757 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1758 /* scan directory */
1759 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1761 int le = GetLastError();
1762 if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR;
1769 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1771 static const WCHAR bracketW[] = { ']',0 };
1772 static const WCHAR dotW[] = { '.',0 };
1773 if (!(attrib & DDL_DIRECTORY) ||
1774 !strcmpW( entry.cFileName, dotW )) continue;
1776 if (!long_names && entry.cAlternateFileName[0])
1777 strcpyW( buffer + 1, entry.cAlternateFileName );
1779 strcpyW( buffer + 1, entry.cFileName );
1780 strcatW(buffer, bracketW);
1782 else /* not a directory */
1784 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1785 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1787 if ((attrib & DDL_EXCLUSIVE) &&
1788 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1791 if (!long_names && entry.cAlternateFileName[0])
1792 strcpyW( buffer, entry.cAlternateFileName );
1794 strcpyW( buffer, entry.cFileName );
1796 if (!long_names) CharLowerW( buffer );
1797 pos = LISTBOX_FindFileStrPos( descr, buffer );
1798 if ((ret = LISTBOX_InsertString( descr, pos, buffer )) < 0)
1800 } while (FindNextFileW( handle, &entry ));
1801 FindClose( handle );
1806 if ((ret >= 0) && (attrib & DDL_DRIVES))
1808 WCHAR buffer[] = {'[','-','a','-',']',0};
1809 WCHAR root[] = {'A',':','\\',0};
1811 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1813 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1814 if ((ret = LISTBOX_InsertString( descr, -1, buffer )) < 0)
1822 /***********************************************************************
1823 * LISTBOX_HandleVScroll
1825 static LRESULT LISTBOX_HandleVScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1829 if (descr->style & LBS_MULTICOLUMN) return 0;
1833 LISTBOX_SetTopItem( descr, descr->top_item - 1, TRUE );
1836 LISTBOX_SetTopItem( descr, descr->top_item + 1, TRUE );
1839 LISTBOX_SetTopItem( descr, descr->top_item -
1840 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1843 LISTBOX_SetTopItem( descr, descr->top_item +
1844 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1846 case SB_THUMBPOSITION:
1847 LISTBOX_SetTopItem( descr, pos, TRUE );
1850 info.cbSize = sizeof(info);
1851 info.fMask = SIF_TRACKPOS;
1852 GetScrollInfo( descr->self, SB_VERT, &info );
1853 LISTBOX_SetTopItem( descr, info.nTrackPos, TRUE );
1856 LISTBOX_SetTopItem( descr, 0, TRUE );
1859 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1866 /***********************************************************************
1867 * LISTBOX_HandleHScroll
1869 static LRESULT LISTBOX_HandleHScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1874 if (descr->style & LBS_MULTICOLUMN)
1879 LISTBOX_SetTopItem( descr, descr->top_item-descr->page_size,
1883 LISTBOX_SetTopItem( descr, descr->top_item+descr->page_size,
1887 page = descr->width / descr->column_width;
1888 if (page < 1) page = 1;
1889 LISTBOX_SetTopItem( descr,
1890 descr->top_item - page * descr->page_size, TRUE );
1893 page = descr->width / descr->column_width;
1894 if (page < 1) page = 1;
1895 LISTBOX_SetTopItem( descr,
1896 descr->top_item + page * descr->page_size, TRUE );
1898 case SB_THUMBPOSITION:
1899 LISTBOX_SetTopItem( descr, pos*descr->page_size, TRUE );
1902 info.cbSize = sizeof(info);
1903 info.fMask = SIF_TRACKPOS;
1904 GetScrollInfo( descr->self, SB_VERT, &info );
1905 LISTBOX_SetTopItem( descr, info.nTrackPos*descr->page_size,
1909 LISTBOX_SetTopItem( descr, 0, TRUE );
1912 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1916 else if (descr->horz_extent)
1921 LISTBOX_SetHorizontalPos( descr, descr->horz_pos - 1 );
1924 LISTBOX_SetHorizontalPos( descr, descr->horz_pos + 1 );
1927 LISTBOX_SetHorizontalPos( descr,
1928 descr->horz_pos - descr->width );
1931 LISTBOX_SetHorizontalPos( descr,
1932 descr->horz_pos + descr->width );
1934 case SB_THUMBPOSITION:
1935 LISTBOX_SetHorizontalPos( descr, pos );
1938 info.cbSize = sizeof(info);
1939 info.fMask = SIF_TRACKPOS;
1940 GetScrollInfo( descr->self, SB_HORZ, &info );
1941 LISTBOX_SetHorizontalPos( descr, info.nTrackPos );
1944 LISTBOX_SetHorizontalPos( descr, 0 );
1947 LISTBOX_SetHorizontalPos( descr,
1948 descr->horz_extent - descr->width );
1955 static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta )
1957 short gcWheelDelta = 0;
1958 UINT pulScrollLines = 3;
1960 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1962 gcWheelDelta -= delta;
1964 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1966 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1967 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1968 LISTBOX_SetTopItem( descr, descr->top_item + cLineScroll, TRUE );
1973 /***********************************************************************
1974 * LISTBOX_HandleLButtonDown
1976 static LRESULT LISTBOX_HandleLButtonDown( LB_DESCR *descr, DWORD keys, INT x, INT y )
1978 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
1979 TRACE("[%p]: lbuttondown %d,%d item %d\n", descr->self, x, y, index );
1980 if (!descr->caret_on && (descr->in_focus)) return 0;
1982 if (!descr->in_focus)
1984 if( !descr->lphc ) SetFocus( descr->self );
1985 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
1988 if (index == -1) return 0;
1990 if (descr->style & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL))
1992 /* we should perhaps make sure that all items are deselected
1993 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1994 if (!(keys & (MK_SHIFT|MK_CONTROL)))
1995 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
1998 if (!(keys & MK_SHIFT)) descr->anchor_item = index;
1999 if (keys & MK_CONTROL)
2001 LISTBOX_SetCaretIndex( descr, index, FALSE );
2002 LISTBOX_SetSelection( descr, index,
2003 !descr->items[index].selected,
2004 (descr->style & LBS_NOTIFY) != 0);
2008 LISTBOX_MoveCaret( descr, index, FALSE );
2010 if (descr->style & LBS_EXTENDEDSEL)
2012 LISTBOX_SetSelection( descr, index,
2013 descr->items[index].selected,
2014 (descr->style & LBS_NOTIFY) != 0 );
2018 LISTBOX_SetSelection( descr, index,
2019 !descr->items[index].selected,
2020 (descr->style & LBS_NOTIFY) != 0 );
2026 descr->anchor_item = index;
2027 LISTBOX_MoveCaret( descr, index, FALSE );
2028 LISTBOX_SetSelection( descr, index,
2029 TRUE, (descr->style & LBS_NOTIFY) != 0 );
2032 descr->captured = TRUE;
2033 SetCapture( descr->self );
2037 if (descr->style & LBS_NOTIFY )
2038 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
2039 MAKELPARAM( x, y ) );
2040 if (GetWindowLongW( descr->self, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
2047 if (DragDetect( descr->self, pt ))
2048 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
2055 /*************************************************************************
2056 * LISTBOX_HandleLButtonDownCombo [Internal]
2058 * Process LButtonDown message for the ComboListBox
2061 * pWnd [I] The windows internal structure
2062 * pDescr [I] The ListBox internal structure
2063 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2064 * x [I] X Mouse Coordinate
2065 * y [I] Y Mouse Coordinate
2068 * 0 since we are processing the WM_LBUTTONDOWN Message
2071 * This function is only to be used when a ListBox is a ComboListBox
2074 static LRESULT LISTBOX_HandleLButtonDownCombo( LB_DESCR *descr, UINT msg, DWORD keys, INT x, INT y)
2076 RECT clientRect, screenRect;
2082 GetClientRect(descr->self, &clientRect);
2084 if(PtInRect(&clientRect, mousePos))
2086 /* MousePos is in client, resume normal processing */
2087 if (msg == WM_LBUTTONDOWN)
2089 descr->lphc->droppedIndex = descr->nb_items ? descr->selected_item : -1;
2090 return LISTBOX_HandleLButtonDown( descr, keys, x, y);
2092 else if (descr->style & LBS_NOTIFY)
2093 SEND_NOTIFICATION( descr, LBN_DBLCLK );
2097 POINT screenMousePos;
2098 HWND hWndOldCapture;
2100 /* Check the Non-Client Area */
2101 screenMousePos = mousePos;
2102 hWndOldCapture = GetCapture();
2104 GetWindowRect(descr->self, &screenRect);
2105 ClientToScreen(descr->self, &screenMousePos);
2107 if(!PtInRect(&screenRect, screenMousePos))
2109 LISTBOX_SetCaretIndex( descr, descr->lphc->droppedIndex, FALSE );
2110 LISTBOX_SetSelection( descr, descr->lphc->droppedIndex, FALSE, FALSE );
2111 COMBO_FlipListbox( descr->lphc, FALSE, FALSE );
2115 /* Check to see the NC is a scrollbar */
2117 LONG style = GetWindowLongW( descr->self, GWL_STYLE );
2118 /* Check Vertical scroll bar */
2119 if (style & WS_VSCROLL)
2121 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2122 if (PtInRect( &clientRect, mousePos ))
2123 nHitTestType = HTVSCROLL;
2125 /* Check horizontal scroll bar */
2126 if (style & WS_HSCROLL)
2128 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2129 if (PtInRect( &clientRect, mousePos ))
2130 nHitTestType = HTHSCROLL;
2132 /* Windows sends this message when a scrollbar is clicked
2135 if(nHitTestType != 0)
2137 SendMessageW(descr->self, WM_NCLBUTTONDOWN, nHitTestType,
2138 MAKELONG(screenMousePos.x, screenMousePos.y));
2140 /* Resume the Capture after scrolling is complete
2142 if(hWndOldCapture != 0)
2143 SetCapture(hWndOldCapture);
2149 /***********************************************************************
2150 * LISTBOX_HandleLButtonUp
2152 static LRESULT LISTBOX_HandleLButtonUp( LB_DESCR *descr )
2154 if (LISTBOX_Timer != LB_TIMER_NONE)
2155 KillSystemTimer( descr->self, LB_TIMER_ID );
2156 LISTBOX_Timer = LB_TIMER_NONE;
2157 if (descr->captured)
2159 descr->captured = FALSE;
2160 if (GetCapture() == descr->self) ReleaseCapture();
2161 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2162 SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2168 /***********************************************************************
2169 * LISTBOX_HandleTimer
2171 * Handle scrolling upon a timer event.
2172 * Return TRUE if scrolling should continue.
2174 static LRESULT LISTBOX_HandleTimer( LB_DESCR *descr, INT index, TIMER_DIRECTION dir )
2179 if (descr->top_item) index = descr->top_item - 1;
2183 if (descr->top_item) index -= descr->page_size;
2186 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2187 if (index == descr->focus_item) index++;
2188 if (index >= descr->nb_items) index = descr->nb_items - 1;
2190 case LB_TIMER_RIGHT:
2191 if (index + descr->page_size < descr->nb_items)
2192 index += descr->page_size;
2197 if (index == descr->focus_item) return FALSE;
2198 LISTBOX_MoveCaret( descr, index, FALSE );
2203 /***********************************************************************
2204 * LISTBOX_HandleSystemTimer
2206 * WM_SYSTIMER handler.
2208 static LRESULT LISTBOX_HandleSystemTimer( LB_DESCR *descr )
2210 if (!LISTBOX_HandleTimer( descr, descr->focus_item, LISTBOX_Timer ))
2212 KillSystemTimer( descr->self, LB_TIMER_ID );
2213 LISTBOX_Timer = LB_TIMER_NONE;
2219 /***********************************************************************
2220 * LISTBOX_HandleMouseMove
2222 * WM_MOUSEMOVE handler.
2224 static void LISTBOX_HandleMouseMove( LB_DESCR *descr,
2228 TIMER_DIRECTION dir = LB_TIMER_NONE;
2230 if (!descr->captured) return;
2232 if (descr->style & LBS_MULTICOLUMN)
2235 else if (y >= descr->item_height * descr->page_size)
2236 y = descr->item_height * descr->page_size - 1;
2240 dir = LB_TIMER_LEFT;
2243 else if (x >= descr->width)
2245 dir = LB_TIMER_RIGHT;
2246 x = descr->width - 1;
2251 if (y < 0) dir = LB_TIMER_UP; /* above */
2252 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2255 index = LISTBOX_GetItemFromPoint( descr, x, y );
2256 if (index == -1) index = descr->focus_item;
2257 if (!LISTBOX_HandleTimer( descr, index, dir )) dir = LB_TIMER_NONE;
2259 /* Start/stop the system timer */
2261 if (dir != LB_TIMER_NONE)
2262 SetSystemTimer( descr->self, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2263 else if (LISTBOX_Timer != LB_TIMER_NONE)
2264 KillSystemTimer( descr->self, LB_TIMER_ID );
2265 LISTBOX_Timer = dir;
2269 /***********************************************************************
2270 * LISTBOX_HandleKeyDown
2272 static LRESULT LISTBOX_HandleKeyDown( LB_DESCR *descr, DWORD key )
2275 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2276 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2277 bForceSelection = FALSE; /* only for single select list */
2279 if (descr->style & LBS_WANTKEYBOARDINPUT)
2281 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2282 MAKEWPARAM(LOWORD(key), descr->focus_item),
2283 (LPARAM)descr->self );
2284 if (caret == -2) return 0;
2286 if (caret == -1) switch(key)
2289 if (descr->style & LBS_MULTICOLUMN)
2291 bForceSelection = FALSE;
2292 if (descr->focus_item >= descr->page_size)
2293 caret = descr->focus_item - descr->page_size;
2298 caret = descr->focus_item - 1;
2299 if (caret < 0) caret = 0;
2302 if (descr->style & LBS_MULTICOLUMN)
2304 bForceSelection = FALSE;
2305 if (descr->focus_item + descr->page_size < descr->nb_items)
2306 caret = descr->focus_item + descr->page_size;
2311 caret = descr->focus_item + 1;
2312 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2316 if (descr->style & LBS_MULTICOLUMN)
2318 INT page = descr->width / descr->column_width;
2319 if (page < 1) page = 1;
2320 caret = descr->focus_item - (page * descr->page_size) + 1;
2322 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2323 if (caret < 0) caret = 0;
2326 if (descr->style & LBS_MULTICOLUMN)
2328 INT page = descr->width / descr->column_width;
2329 if (page < 1) page = 1;
2330 caret = descr->focus_item + (page * descr->page_size) - 1;
2332 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2333 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2339 caret = descr->nb_items - 1;
2342 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2343 else if (descr->style & LBS_MULTIPLESEL)
2345 LISTBOX_SetSelection( descr, descr->focus_item,
2346 !descr->items[descr->focus_item].selected,
2347 (descr->style & LBS_NOTIFY) != 0 );
2351 bForceSelection = FALSE;
2353 if (bForceSelection) /* focused item is used instead of key */
2354 caret = descr->focus_item;
2357 if (((descr->style & LBS_EXTENDEDSEL) &&
2358 !(GetKeyState( VK_SHIFT ) & 0x8000)) ||
2359 !IS_MULTISELECT(descr))
2360 descr->anchor_item = caret;
2361 LISTBOX_MoveCaret( descr, caret, TRUE );
2363 if (descr->style & LBS_MULTIPLESEL)
2364 descr->selected_item = caret;
2366 LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2367 if (descr->style & LBS_NOTIFY)
2371 /* make sure that combo parent doesn't hide us */
2372 descr->lphc->wState |= CBF_NOROLLUP;
2374 if (descr->nb_items) SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2381 /***********************************************************************
2382 * LISTBOX_HandleChar
2384 static LRESULT LISTBOX_HandleChar( LB_DESCR *descr, WCHAR charW )
2392 if (descr->style & LBS_WANTKEYBOARDINPUT)
2394 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2395 MAKEWPARAM(charW, descr->focus_item),
2396 (LPARAM)descr->self );
2397 if (caret == -2) return 0;
2400 caret = LISTBOX_FindString( descr, descr->focus_item, str, FALSE);
2403 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2404 LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2405 LISTBOX_MoveCaret( descr, caret, TRUE );
2406 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2407 SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2413 /***********************************************************************
2416 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2419 MEASUREITEMSTRUCT mis;
2422 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2425 GetClientRect( hwnd, &rect );
2427 descr->owner = GetParent( descr->self );
2428 descr->style = GetWindowLongW( descr->self, GWL_STYLE );
2429 descr->width = rect.right - rect.left;
2430 descr->height = rect.bottom - rect.top;
2431 descr->items = NULL;
2432 descr->nb_items = 0;
2433 descr->top_item = 0;
2434 descr->selected_item = -1;
2435 descr->focus_item = 0;
2436 descr->anchor_item = -1;
2437 descr->item_height = 1;
2438 descr->page_size = 1;
2439 descr->column_width = 150;
2440 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0;
2441 descr->horz_pos = 0;
2444 descr->caret_on = lphc ? FALSE : TRUE;
2445 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2446 descr->in_focus = FALSE;
2447 descr->captured = FALSE;
2449 descr->locale = 0; /* FIXME */
2452 if (is_old_app(descr) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2454 /* Win95 document "List Box Differences" from MSDN:
2455 If a list box in a version 3.x application has either the
2456 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2457 horizontal and vertical scroll bars.
2459 descr->style |= WS_VSCROLL | WS_HSCROLL;
2464 TRACE("[%p]: resetting owner %p -> %p\n", descr->self, descr->owner, lphc->self );
2465 descr->owner = lphc->self;
2468 SetWindowLongW( descr->self, 0, (LONG)descr );
2470 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2472 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2473 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2474 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2475 descr->item_height = LISTBOX_SetFont( descr, 0 );
2477 if (descr->style & LBS_OWNERDRAWFIXED)
2479 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2481 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2482 descr->item_height = lphc->fixedOwnerDrawHeight;
2486 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
2487 mis.CtlType = ODT_LISTBOX;
2492 mis.itemHeight = descr->item_height;
2493 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2494 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2498 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2503 /***********************************************************************
2506 static BOOL LISTBOX_Destroy( LB_DESCR *descr )
2508 LISTBOX_ResetContent( descr );
2509 SetWindowLongW( descr->self, 0, 0 );
2510 HeapFree( GetProcessHeap(), 0, descr );
2515 /***********************************************************************
2516 * ListBoxWndProc_common
2518 static LRESULT WINAPI ListBoxWndProc_common( HWND hwnd, UINT msg,
2519 WPARAM wParam, LPARAM lParam, BOOL unicode )
2521 LB_DESCR *descr = (LB_DESCR *)GetWindowLongW( hwnd, 0 );
2522 LPHEADCOMBO lphc = 0;
2527 if (!IsWindow(hwnd)) return 0;
2529 if (msg == WM_CREATE)
2531 CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam;
2532 if (lpcs->style & LBS_COMBOBOX) lphc = (LPHEADCOMBO)lpcs->lpCreateParams;
2533 if (!LISTBOX_Create( hwnd, lphc )) return -1;
2534 TRACE("creating wnd=%p descr=%lx\n", hwnd, GetWindowLongW( hwnd, 0 ) );
2537 /* Ignore all other messages before we get a WM_CREATE */
2538 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2539 DefWindowProcA( hwnd, msg, wParam, lParam );
2541 if (descr->style & LBS_COMBOBOX) lphc = descr->lphc;
2543 TRACE("[%p]: msg %s wp %08x lp %08lx\n",
2544 descr->self, SPY_GetMsgName(msg, descr->self), wParam, lParam );
2548 case LB_RESETCONTENT16:
2549 case LB_RESETCONTENT:
2550 LISTBOX_ResetContent( descr );
2551 LISTBOX_UpdateScroll( descr );
2552 InvalidateRect( descr->self, NULL, TRUE );
2555 case LB_ADDSTRING16:
2556 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2562 if(unicode || !HAS_STRINGS(descr))
2563 textW = (LPWSTR)lParam;
2566 LPSTR textA = (LPSTR)lParam;
2567 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2568 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2569 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2571 wParam = LISTBOX_FindStringPos( descr, textW, FALSE );
2572 ret = LISTBOX_InsertString( descr, wParam, textW );
2573 if (!unicode && HAS_STRINGS(descr))
2574 HeapFree(GetProcessHeap(), 0, textW);
2578 case LB_INSERTSTRING16:
2579 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2580 wParam = (INT)(INT16)wParam;
2582 case LB_INSERTSTRING:
2586 if(unicode || !HAS_STRINGS(descr))
2587 textW = (LPWSTR)lParam;
2590 LPSTR textA = (LPSTR)lParam;
2591 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2592 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2593 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2595 ret = LISTBOX_InsertString( descr, wParam, textW );
2596 if(!unicode && HAS_STRINGS(descr))
2597 HeapFree(GetProcessHeap(), 0, textW);
2602 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2608 if(unicode || !HAS_STRINGS(descr))
2609 textW = (LPWSTR)lParam;
2612 LPSTR textA = (LPSTR)lParam;
2613 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2614 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2615 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2617 wParam = LISTBOX_FindFileStrPos( descr, textW );
2618 ret = LISTBOX_InsertString( descr, wParam, textW );
2619 if(!unicode && HAS_STRINGS(descr))
2620 HeapFree(GetProcessHeap(), 0, textW);
2624 case LB_DELETESTRING16:
2625 case LB_DELETESTRING:
2626 if (LISTBOX_RemoveItem( descr, wParam) != LB_ERR)
2627 return descr->nb_items;
2631 case LB_GETITEMDATA16:
2632 case LB_GETITEMDATA:
2633 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2635 return descr->items[wParam].data;
2637 case LB_SETITEMDATA16:
2638 case LB_SETITEMDATA:
2639 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2641 descr->items[wParam].data = (DWORD)lParam;
2646 return descr->nb_items;
2649 lParam = (LPARAM)MapSL(lParam);
2652 return LISTBOX_GetText( descr, wParam, (LPWSTR)lParam, unicode );
2654 case LB_GETTEXTLEN16:
2657 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2659 if (!HAS_STRINGS(descr)) return sizeof(DWORD);
2660 if (unicode) return strlenW( descr->items[wParam].str );
2661 return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
2662 strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
2664 case LB_GETCURSEL16:
2666 if (descr->nb_items == 0)
2668 if (!IS_MULTISELECT(descr))
2669 return descr->selected_item;
2670 if (descr->selected_item != -1)
2671 return descr->selected_item;
2672 return descr->focus_item;
2673 /* otherwise, if the user tries to move the selection with the */
2674 /* arrow keys, we will give the application something to choke on */
2675 case LB_GETTOPINDEX16:
2676 case LB_GETTOPINDEX:
2677 return descr->top_item;
2679 case LB_GETITEMHEIGHT16:
2680 case LB_GETITEMHEIGHT:
2681 return LISTBOX_GetItemHeight( descr, wParam );
2683 case LB_SETITEMHEIGHT16:
2684 lParam = LOWORD(lParam);
2686 case LB_SETITEMHEIGHT:
2687 return LISTBOX_SetItemHeight( descr, wParam, lParam, TRUE );
2689 case LB_ITEMFROMPOINT:
2694 pt.x = LOWORD(lParam);
2695 pt.y = HIWORD(lParam);
2698 rect.right = descr->width;
2699 rect.bottom = descr->height;
2701 return MAKELONG( LISTBOX_GetItemFromPoint(descr, pt.x, pt.y),
2702 !PtInRect( &rect, pt ) );
2705 case LB_SETCARETINDEX16:
2706 case LB_SETCARETINDEX:
2707 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2708 if (LISTBOX_SetCaretIndex( descr, wParam, !lParam ) == LB_ERR)
2715 case LB_GETCARETINDEX16:
2716 case LB_GETCARETINDEX:
2717 return descr->focus_item;
2719 case LB_SETTOPINDEX16:
2720 case LB_SETTOPINDEX:
2721 return LISTBOX_SetTopItem( descr, wParam, TRUE );
2723 case LB_SETCOLUMNWIDTH16:
2724 case LB_SETCOLUMNWIDTH:
2725 return LISTBOX_SetColumnWidth( descr, wParam );
2727 case LB_GETITEMRECT16:
2730 RECT16 *r16 = MapSL(lParam);
2731 ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2732 r16->left = rect.left;
2733 r16->top = rect.top;
2734 r16->right = rect.right;
2735 r16->bottom = rect.bottom;
2739 case LB_GETITEMRECT:
2740 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2742 case LB_FINDSTRING16:
2743 wParam = (INT)(INT16)wParam;
2744 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2750 if(unicode || !HAS_STRINGS(descr))
2751 textW = (LPWSTR)lParam;
2754 LPSTR textA = (LPSTR)lParam;
2755 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2756 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2757 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2759 ret = LISTBOX_FindString( descr, wParam, textW, FALSE );
2760 if(!unicode && HAS_STRINGS(descr))
2761 HeapFree(GetProcessHeap(), 0, textW);
2765 case LB_FINDSTRINGEXACT16:
2766 wParam = (INT)(INT16)wParam;
2767 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2769 case LB_FINDSTRINGEXACT:
2773 if(unicode || !HAS_STRINGS(descr))
2774 textW = (LPWSTR)lParam;
2777 LPSTR textA = (LPSTR)lParam;
2778 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2779 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2780 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2782 ret = LISTBOX_FindString( descr, wParam, textW, TRUE );
2783 if(!unicode && HAS_STRINGS(descr))
2784 HeapFree(GetProcessHeap(), 0, textW);
2788 case LB_SELECTSTRING16:
2789 wParam = (INT)(INT16)wParam;
2790 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2792 case LB_SELECTSTRING:
2797 if(HAS_STRINGS(descr))
2798 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2799 debugstr_a((LPSTR)lParam));
2800 if(unicode || !HAS_STRINGS(descr))
2801 textW = (LPWSTR)lParam;
2804 LPSTR textA = (LPSTR)lParam;
2805 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2806 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2807 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2809 index = LISTBOX_FindString( descr, wParam, textW, FALSE );
2810 if(!unicode && HAS_STRINGS(descr))
2811 HeapFree(GetProcessHeap(), 0, textW);
2812 if (index != LB_ERR)
2814 LISTBOX_MoveCaret( descr, index, TRUE );
2815 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
2821 wParam = (INT)(INT16)wParam;
2824 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2826 return descr->items[wParam].selected;
2829 lParam = (INT)(INT16)lParam;
2832 return LISTBOX_SetSelection( descr, lParam, wParam, FALSE );
2834 case LB_SETCURSEL16:
2835 wParam = (INT)(INT16)wParam;
2838 if (IS_MULTISELECT(descr)) return LB_ERR;
2839 LISTBOX_SetCaretIndex( descr, wParam, TRUE );
2840 ret = LISTBOX_SetSelection( descr, wParam, TRUE, FALSE );
2841 if (lphc && ret != LB_ERR) ret = descr->selected_item;
2844 case LB_GETSELCOUNT16:
2845 case LB_GETSELCOUNT:
2846 return LISTBOX_GetSelCount( descr );
2848 case LB_GETSELITEMS16:
2849 return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2851 case LB_GETSELITEMS:
2852 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2854 case LB_SELITEMRANGE16:
2855 case LB_SELITEMRANGE:
2856 if (LOWORD(lParam) <= HIWORD(lParam))
2857 return LISTBOX_SelectItemRange( descr, LOWORD(lParam),
2858 HIWORD(lParam), wParam );
2860 return LISTBOX_SelectItemRange( descr, HIWORD(lParam),
2861 LOWORD(lParam), wParam );
2863 case LB_SELITEMRANGEEX16:
2864 case LB_SELITEMRANGEEX:
2865 if ((INT)lParam >= (INT)wParam)
2866 return LISTBOX_SelectItemRange( descr, wParam, lParam, TRUE );
2868 return LISTBOX_SelectItemRange( descr, lParam, wParam, FALSE);
2870 case LB_GETHORIZONTALEXTENT16:
2871 case LB_GETHORIZONTALEXTENT:
2872 return descr->horz_extent;
2874 case LB_SETHORIZONTALEXTENT16:
2875 case LB_SETHORIZONTALEXTENT:
2876 return LISTBOX_SetHorizontalExtent( descr, wParam );
2878 case LB_GETANCHORINDEX16:
2879 case LB_GETANCHORINDEX:
2880 return descr->anchor_item;
2882 case LB_SETANCHORINDEX16:
2883 wParam = (INT)(INT16)wParam;
2885 case LB_SETANCHORINDEX:
2886 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2888 descr->anchor_item = (INT)wParam;
2892 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2893 * be set automatically (this is different in Win32) */
2894 if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2895 lParam = (LPARAM)MapSL(lParam);
2902 textW = (LPWSTR)lParam;
2905 LPSTR textA = (LPSTR)lParam;
2906 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2907 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2908 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2910 ret = LISTBOX_Directory( descr, wParam, textW, msg == LB_DIR );
2912 HeapFree(GetProcessHeap(), 0, textW);
2917 return descr->locale;
2920 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2923 case LB_INITSTORAGE:
2924 return LISTBOX_InitStorage( descr, wParam );
2927 return LISTBOX_SetCount( descr, (INT)wParam );
2929 case LB_SETTABSTOPS16:
2930 return LISTBOX_SetTabStops( descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2932 case LB_SETTABSTOPS:
2933 return LISTBOX_SetTabStops( descr, wParam, (LPINT)lParam, FALSE );
2937 if (descr->caret_on)
2939 descr->caret_on = TRUE;
2940 if ((descr->focus_item != -1) && (descr->in_focus))
2941 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2946 if (!descr->caret_on)
2948 descr->caret_on = FALSE;
2949 if ((descr->focus_item != -1) && (descr->in_focus))
2950 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2954 return LISTBOX_Destroy( descr );
2957 InvalidateRect( descr->self, NULL, TRUE );
2961 LISTBOX_SetRedraw( descr, wParam != 0 );
2965 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2970 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( descr->self, &ps );
2971 ret = LISTBOX_Paint( descr, hdc );
2972 if( !wParam ) EndPaint( descr->self, &ps );
2976 LISTBOX_UpdateSize( descr );
2979 return (LRESULT)descr->font;
2981 LISTBOX_SetFont( descr, (HFONT)wParam );
2982 if (lParam) InvalidateRect( descr->self, 0, TRUE );
2985 descr->in_focus = TRUE;
2986 descr->caret_on = TRUE;
2987 if (descr->focus_item != -1)
2988 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2989 SEND_NOTIFICATION( descr, LBN_SETFOCUS );
2992 descr->in_focus = FALSE;
2993 if ((descr->focus_item != -1) && descr->caret_on)
2994 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2995 SEND_NOTIFICATION( descr, LBN_KILLFOCUS );
2998 return LISTBOX_HandleHScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3000 return LISTBOX_HandleVScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3002 if (wParam & (MK_SHIFT | MK_CONTROL))
3003 return DefWindowProcW( descr->self, msg, wParam, lParam );
3004 return LISTBOX_HandleMouseWheel( descr, (SHORT)HIWORD(wParam) );
3005 case WM_LBUTTONDOWN:
3007 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3008 (INT16)LOWORD(lParam),
3009 (INT16)HIWORD(lParam) );
3010 return LISTBOX_HandleLButtonDown( descr, wParam,
3011 (INT16)LOWORD(lParam),
3012 (INT16)HIWORD(lParam) );
3013 case WM_LBUTTONDBLCLK:
3015 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3016 (INT16)LOWORD(lParam),
3017 (INT16)HIWORD(lParam) );
3018 if (descr->style & LBS_NOTIFY)
3019 SEND_NOTIFICATION( descr, LBN_DBLCLK );
3022 if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) )
3024 BOOL captured = descr->captured;
3028 mousePos.x = (INT16)LOWORD(lParam);
3029 mousePos.y = (INT16)HIWORD(lParam);
3032 * If we are in a dropdown combobox, we simulate that
3033 * the mouse is captured to show the tracking of the item.
3035 if (GetClientRect(descr->self, &clientRect) && PtInRect( &clientRect, mousePos ))
3036 descr->captured = TRUE;
3038 LISTBOX_HandleMouseMove( descr, mousePos.x, mousePos.y);
3040 descr->captured = captured;
3042 else if (GetCapture() == descr->self)
3044 LISTBOX_HandleMouseMove( descr, (INT16)LOWORD(lParam),
3045 (INT16)HIWORD(lParam) );
3055 * If the mouse button "up" is not in the listbox,
3056 * we make sure there is no selection by re-selecting the
3057 * item that was selected when the listbox was made visible.
3059 mousePos.x = (INT16)LOWORD(lParam);
3060 mousePos.y = (INT16)HIWORD(lParam);
3062 GetClientRect(descr->self, &clientRect);
3065 * When the user clicks outside the combobox and the focus
3066 * is lost, the owning combobox will send a fake buttonup with
3067 * 0xFFFFFFF as the mouse location, we must also revert the
3068 * selection to the original selection.
3070 if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) )
3071 LISTBOX_MoveCaret( descr, lphc->droppedIndex, FALSE );
3073 return LISTBOX_HandleLButtonUp( descr );
3075 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3077 /* for some reason Windows makes it possible to
3078 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3080 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3081 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3082 && (wParam == VK_DOWN || wParam == VK_UP)) )
3084 COMBO_FlipListbox( lphc, FALSE, FALSE );
3088 return LISTBOX_HandleKeyDown( descr, wParam );
3093 charW = (WCHAR)wParam;
3096 CHAR charA = (CHAR)wParam;
3097 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
3099 return LISTBOX_HandleChar( descr, charW );
3102 return LISTBOX_HandleSystemTimer( descr );
3104 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
3107 HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
3108 wParam, (LPARAM)descr->self );
3109 TRACE("hbrush = %p\n", hbrush);
3111 hbrush = GetSysColorBrush(COLOR_WINDOW);
3114 GetClientRect(descr->self, &rect);
3115 FillRect((HDC)wParam, &rect, hbrush);
3120 if( lphc ) return 0;
3121 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3122 SendMessageA( descr->owner, msg, wParam, lParam );
3125 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3134 if ((msg >= WM_USER) && (msg < 0xc000))
3135 WARN("[%p]: unknown msg %04x wp %08x lp %08lx\n",
3136 hwnd, msg, wParam, lParam );
3139 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3140 DefWindowProcA( hwnd, msg, wParam, lParam );
3143 /***********************************************************************
3146 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3148 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3151 /***********************************************************************
3154 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3156 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE );