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;
1368 /* selected_item reflects last selected/unselected item on multiple sel */
1369 descr->selected_item = last;
1371 if (on) /* Turn selection on */
1373 for (i = first; i <= last; i++)
1375 if (descr->items[i].selected) continue;
1376 descr->items[i].selected = TRUE;
1377 LISTBOX_InvalidateItemRect(descr, i);
1379 LISTBOX_SetCaretIndex( descr, last, TRUE );
1381 else /* Turn selection off */
1383 for (i = first; i <= last; i++)
1385 if (!descr->items[i].selected) continue;
1386 descr->items[i].selected = FALSE;
1387 LISTBOX_InvalidateItemRect(descr, i);
1393 /***********************************************************************
1394 * LISTBOX_SetSelection
1396 static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index,
1397 BOOL on, BOOL send_notify )
1399 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1401 if (descr->style & LBS_NOSEL)
1403 descr->selected_item = index;
1406 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1407 if (descr->style & LBS_MULTIPLESEL)
1409 if (index == -1) /* Select all items */
1410 return LISTBOX_SelectItemRange( descr, 0, -1, on );
1411 else /* Only one item */
1412 return LISTBOX_SelectItemRange( descr, index, index, on );
1416 INT oldsel = descr->selected_item;
1417 if (index == oldsel) return LB_OKAY;
1418 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1419 if (index != -1) descr->items[index].selected = TRUE;
1420 descr->selected_item = index;
1421 if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT );
1422 if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT );
1423 if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr,
1424 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1426 if( descr->lphc ) /* set selection change flag for parent combo */
1427 descr->lphc->wState |= CBF_SELCHANGE;
1433 /***********************************************************************
1436 * Change the caret position and extend the selection to the new caret.
1438 static void LISTBOX_MoveCaret( LB_DESCR *descr, INT index, BOOL fully_visible )
1440 INT oldfocus = descr->focus_item;
1442 if ((index < 0) || (index >= descr->nb_items))
1445 /* Important, repaint needs to be done in this order if
1446 you want to mimic Windows behavior:
1447 1. Remove the focus and paint the item
1448 2. Remove the selection and paint the item(s)
1449 3. Set the selection and repaint the item(s)
1450 4. Set the focus to 'index' and repaint the item */
1452 /* 1. remove the focus and repaint the item */
1453 descr->focus_item = -1;
1454 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1455 LISTBOX_RepaintItem( descr, oldfocus, ODA_FOCUS );
1457 /* 2. then turn off the previous selection */
1458 /* 3. repaint the new selected item */
1459 if (descr->style & LBS_EXTENDEDSEL)
1461 if (descr->anchor_item != -1)
1463 INT first = min( index, descr->anchor_item );
1464 INT last = max( index, descr->anchor_item );
1466 LISTBOX_SelectItemRange( descr, 0, first - 1, FALSE );
1467 LISTBOX_SelectItemRange( descr, last + 1, -1, FALSE );
1468 LISTBOX_SelectItemRange( descr, first, last, TRUE );
1471 else if (!(descr->style & LBS_MULTIPLESEL))
1473 /* Set selection to new caret item */
1474 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
1477 /* 4. repaint the new item with the focus */
1478 descr->focus_item = index;
1479 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1480 if (descr->caret_on && (descr->in_focus))
1481 LISTBOX_RepaintItem( descr, index, ODA_FOCUS );
1485 /***********************************************************************
1486 * LISTBOX_InsertItem
1488 static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index,
1489 LPWSTR str, DWORD data )
1493 INT oldfocus = descr->focus_item;
1495 if (index == -1) index = descr->nb_items;
1496 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1497 if (!descr->items) max_items = 0;
1498 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1499 if (descr->nb_items == max_items)
1501 /* We need to grow the array */
1502 max_items += LB_ARRAY_GRANULARITY;
1504 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1505 max_items * sizeof(LB_ITEMDATA) );
1507 item = HeapAlloc( GetProcessHeap(), 0,
1508 max_items * sizeof(LB_ITEMDATA) );
1511 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1514 descr->items = item;
1517 /* Insert the item structure */
1519 item = &descr->items[index];
1520 if (index < descr->nb_items)
1521 RtlMoveMemory( item + 1, item,
1522 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1526 item->selected = FALSE;
1529 /* Get item height */
1531 if (descr->style & LBS_OWNERDRAWVARIABLE)
1533 MEASUREITEMSTRUCT mis;
1534 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1536 mis.CtlType = ODT_LISTBOX;
1539 mis.itemData = descr->items[index].data;
1540 mis.itemHeight = descr->item_height;
1541 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1542 item->height = mis.itemHeight ? mis.itemHeight : 1;
1543 TRACE("[%p]: measure item %d (%s) = %d\n",
1544 descr->self, index, str ? debugstr_w(str) : "", item->height );
1547 /* Repaint the items */
1549 LISTBOX_UpdateScroll( descr );
1550 LISTBOX_InvalidateItems( descr, index );
1552 /* Move selection and focused item */
1553 /* If listbox was empty, set focus to the first item */
1554 if (descr->nb_items == 1)
1555 LISTBOX_SetCaretIndex( descr, 0, FALSE );
1556 /* single select don't change selection index in win31 */
1557 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1559 descr->selected_item++;
1560 LISTBOX_SetSelection( descr, descr->selected_item-1, TRUE, FALSE );
1564 if (index <= descr->selected_item)
1566 descr->selected_item++;
1567 descr->focus_item = oldfocus; /* focus not changed */
1574 /***********************************************************************
1575 * LISTBOX_InsertString
1577 static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index, LPCWSTR str )
1579 LPWSTR new_str = NULL;
1583 if (HAS_STRINGS(descr))
1585 static const WCHAR empty_stringW[] = { 0 };
1586 if (!str) str = empty_stringW;
1587 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1589 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1592 strcpyW(new_str, str);
1594 else data = (DWORD)str;
1596 if (index == -1) index = descr->nb_items;
1597 if ((ret = LISTBOX_InsertItem( descr, index, new_str, data )) != 0)
1599 HeapFree( GetProcessHeap(), 0, new_str );
1603 TRACE("[%p]: added item %d %s\n",
1604 descr->self, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1609 /***********************************************************************
1610 * LISTBOX_DeleteItem
1612 * Delete the content of an item. 'index' must be a valid index.
1614 static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index )
1616 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1617 * while Win95 sends it for all items with user data.
1618 * It's probably better to send it too often than not
1619 * often enough, so this is what we do here.
1621 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1623 DELETEITEMSTRUCT dis;
1624 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1626 dis.CtlType = ODT_LISTBOX;
1629 dis.hwndItem = descr->self;
1630 dis.itemData = descr->items[index].data;
1631 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1633 if (HAS_STRINGS(descr) && descr->items[index].str)
1634 HeapFree( GetProcessHeap(), 0, descr->items[index].str );
1638 /***********************************************************************
1639 * LISTBOX_RemoveItem
1641 * Remove an item from the listbox and delete its content.
1643 static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index )
1648 if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1649 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1651 /* We need to invalidate the original rect instead of the updated one. */
1652 LISTBOX_InvalidateItems( descr, index );
1654 LISTBOX_DeleteItem( descr, index );
1656 /* Remove the item */
1658 item = &descr->items[index];
1659 if (index < descr->nb_items-1)
1660 RtlMoveMemory( item, item + 1,
1661 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1663 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1665 /* Shrink the item array if possible */
1667 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1668 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1670 max_items -= LB_ARRAY_GRANULARITY;
1671 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1672 max_items * sizeof(LB_ITEMDATA) );
1673 if (item) descr->items = item;
1675 /* Repaint the items */
1677 LISTBOX_UpdateScroll( descr );
1678 /* if we removed the scrollbar, reset the top of the list
1679 (correct for owner-drawn ???) */
1680 if (descr->nb_items == descr->page_size)
1681 LISTBOX_SetTopItem( descr, 0, TRUE );
1683 /* Move selection and focused item */
1684 if (!IS_MULTISELECT(descr))
1686 if (index == descr->selected_item)
1687 descr->selected_item = -1;
1688 else if (index < descr->selected_item)
1690 descr->selected_item--;
1691 if (ISWIN31) /* win 31 do not change the selected item number */
1692 LISTBOX_SetSelection( descr, descr->selected_item + 1, TRUE, FALSE);
1696 if (descr->focus_item >= descr->nb_items)
1698 descr->focus_item = descr->nb_items - 1;
1699 if (descr->focus_item < 0) descr->focus_item = 0;
1705 /***********************************************************************
1706 * LISTBOX_ResetContent
1708 static void LISTBOX_ResetContent( LB_DESCR *descr )
1712 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( descr, i );
1713 HeapFree( GetProcessHeap(), 0, descr->items );
1714 descr->nb_items = 0;
1715 descr->top_item = 0;
1716 descr->selected_item = -1;
1717 descr->focus_item = 0;
1718 descr->anchor_item = -1;
1719 descr->items = NULL;
1723 /***********************************************************************
1726 static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count )
1730 if (HAS_STRINGS(descr)) return LB_ERR;
1731 /* FIXME: this is far from optimal... */
1732 if (count > descr->nb_items)
1734 while (count > descr->nb_items)
1735 if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0)
1738 else if (count < descr->nb_items)
1740 while (count < descr->nb_items)
1741 if ((ret = LISTBOX_RemoveItem( descr, -1 )) < 0)
1748 /***********************************************************************
1751 static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib,
1752 LPCWSTR filespec, BOOL long_names )
1755 LRESULT ret = LB_OKAY;
1756 WIN32_FIND_DATAW entry;
1759 /* don't scan directory if we just want drives exclusively */
1760 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1761 /* scan directory */
1762 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1764 int le = GetLastError();
1765 if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR;
1772 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1774 static const WCHAR bracketW[] = { ']',0 };
1775 static const WCHAR dotW[] = { '.',0 };
1776 if (!(attrib & DDL_DIRECTORY) ||
1777 !strcmpW( entry.cFileName, dotW )) continue;
1779 if (!long_names && entry.cAlternateFileName[0])
1780 strcpyW( buffer + 1, entry.cAlternateFileName );
1782 strcpyW( buffer + 1, entry.cFileName );
1783 strcatW(buffer, bracketW);
1785 else /* not a directory */
1787 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1788 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1790 if ((attrib & DDL_EXCLUSIVE) &&
1791 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1794 if (!long_names && entry.cAlternateFileName[0])
1795 strcpyW( buffer, entry.cAlternateFileName );
1797 strcpyW( buffer, entry.cFileName );
1799 if (!long_names) CharLowerW( buffer );
1800 pos = LISTBOX_FindFileStrPos( descr, buffer );
1801 if ((ret = LISTBOX_InsertString( descr, pos, buffer )) < 0)
1803 } while (FindNextFileW( handle, &entry ));
1804 FindClose( handle );
1809 if ((ret >= 0) && (attrib & DDL_DRIVES))
1811 WCHAR buffer[] = {'[','-','a','-',']',0};
1812 WCHAR root[] = {'A',':','\\',0};
1814 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1816 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1817 if ((ret = LISTBOX_InsertString( descr, -1, buffer )) < 0)
1825 /***********************************************************************
1826 * LISTBOX_HandleVScroll
1828 static LRESULT LISTBOX_HandleVScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1832 if (descr->style & LBS_MULTICOLUMN) return 0;
1836 LISTBOX_SetTopItem( descr, descr->top_item - 1, TRUE );
1839 LISTBOX_SetTopItem( descr, descr->top_item + 1, TRUE );
1842 LISTBOX_SetTopItem( descr, descr->top_item -
1843 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1846 LISTBOX_SetTopItem( descr, descr->top_item +
1847 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1849 case SB_THUMBPOSITION:
1850 LISTBOX_SetTopItem( descr, pos, TRUE );
1853 info.cbSize = sizeof(info);
1854 info.fMask = SIF_TRACKPOS;
1855 GetScrollInfo( descr->self, SB_VERT, &info );
1856 LISTBOX_SetTopItem( descr, info.nTrackPos, TRUE );
1859 LISTBOX_SetTopItem( descr, 0, TRUE );
1862 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1869 /***********************************************************************
1870 * LISTBOX_HandleHScroll
1872 static LRESULT LISTBOX_HandleHScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1877 if (descr->style & LBS_MULTICOLUMN)
1882 LISTBOX_SetTopItem( descr, descr->top_item-descr->page_size,
1886 LISTBOX_SetTopItem( descr, descr->top_item+descr->page_size,
1890 page = descr->width / descr->column_width;
1891 if (page < 1) page = 1;
1892 LISTBOX_SetTopItem( descr,
1893 descr->top_item - page * descr->page_size, TRUE );
1896 page = descr->width / descr->column_width;
1897 if (page < 1) page = 1;
1898 LISTBOX_SetTopItem( descr,
1899 descr->top_item + page * descr->page_size, TRUE );
1901 case SB_THUMBPOSITION:
1902 LISTBOX_SetTopItem( descr, pos*descr->page_size, TRUE );
1905 info.cbSize = sizeof(info);
1906 info.fMask = SIF_TRACKPOS;
1907 GetScrollInfo( descr->self, SB_VERT, &info );
1908 LISTBOX_SetTopItem( descr, info.nTrackPos*descr->page_size,
1912 LISTBOX_SetTopItem( descr, 0, TRUE );
1915 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1919 else if (descr->horz_extent)
1924 LISTBOX_SetHorizontalPos( descr, descr->horz_pos - 1 );
1927 LISTBOX_SetHorizontalPos( descr, descr->horz_pos + 1 );
1930 LISTBOX_SetHorizontalPos( descr,
1931 descr->horz_pos - descr->width );
1934 LISTBOX_SetHorizontalPos( descr,
1935 descr->horz_pos + descr->width );
1937 case SB_THUMBPOSITION:
1938 LISTBOX_SetHorizontalPos( descr, pos );
1941 info.cbSize = sizeof(info);
1942 info.fMask = SIF_TRACKPOS;
1943 GetScrollInfo( descr->self, SB_HORZ, &info );
1944 LISTBOX_SetHorizontalPos( descr, info.nTrackPos );
1947 LISTBOX_SetHorizontalPos( descr, 0 );
1950 LISTBOX_SetHorizontalPos( descr,
1951 descr->horz_extent - descr->width );
1958 static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta )
1960 short gcWheelDelta = 0;
1961 UINT pulScrollLines = 3;
1963 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1965 gcWheelDelta -= delta;
1967 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1969 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1970 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1971 LISTBOX_SetTopItem( descr, descr->top_item + cLineScroll, TRUE );
1976 /***********************************************************************
1977 * LISTBOX_HandleLButtonDown
1979 static LRESULT LISTBOX_HandleLButtonDown( LB_DESCR *descr, DWORD keys, INT x, INT y )
1981 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
1982 TRACE("[%p]: lbuttondown %d,%d item %d\n", descr->self, x, y, index );
1983 if (!descr->caret_on && (descr->in_focus)) return 0;
1985 if (!descr->in_focus)
1987 if( !descr->lphc ) SetFocus( descr->self );
1988 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
1991 if (index == -1) return 0;
1993 if (descr->style & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL))
1995 /* we should perhaps make sure that all items are deselected
1996 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1997 if (!(keys & (MK_SHIFT|MK_CONTROL)))
1998 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2001 if (!(keys & MK_SHIFT)) descr->anchor_item = index;
2002 if (keys & MK_CONTROL)
2004 LISTBOX_SetCaretIndex( descr, index, FALSE );
2005 LISTBOX_SetSelection( descr, index,
2006 !descr->items[index].selected,
2007 (descr->style & LBS_NOTIFY) != 0);
2011 LISTBOX_MoveCaret( descr, index, FALSE );
2013 if (descr->style & LBS_EXTENDEDSEL)
2015 LISTBOX_SetSelection( descr, index,
2016 descr->items[index].selected,
2017 (descr->style & LBS_NOTIFY) != 0 );
2021 LISTBOX_SetSelection( descr, index,
2022 !descr->items[index].selected,
2023 (descr->style & LBS_NOTIFY) != 0 );
2029 descr->anchor_item = index;
2030 LISTBOX_MoveCaret( descr, index, FALSE );
2031 LISTBOX_SetSelection( descr, index,
2032 TRUE, (descr->style & LBS_NOTIFY) != 0 );
2035 descr->captured = TRUE;
2036 SetCapture( descr->self );
2040 if (descr->style & LBS_NOTIFY )
2041 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
2042 MAKELPARAM( x, y ) );
2043 if (GetWindowLongW( descr->self, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
2050 if (DragDetect( descr->self, pt ))
2051 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
2058 /*************************************************************************
2059 * LISTBOX_HandleLButtonDownCombo [Internal]
2061 * Process LButtonDown message for the ComboListBox
2064 * pWnd [I] The windows internal structure
2065 * pDescr [I] The ListBox internal structure
2066 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2067 * x [I] X Mouse Coordinate
2068 * y [I] Y Mouse Coordinate
2071 * 0 since we are processing the WM_LBUTTONDOWN Message
2074 * This function is only to be used when a ListBox is a ComboListBox
2077 static LRESULT LISTBOX_HandleLButtonDownCombo( LB_DESCR *descr, UINT msg, DWORD keys, INT x, INT y)
2079 RECT clientRect, screenRect;
2085 GetClientRect(descr->self, &clientRect);
2087 if(PtInRect(&clientRect, mousePos))
2089 /* MousePos is in client, resume normal processing */
2090 if (msg == WM_LBUTTONDOWN)
2092 descr->lphc->droppedIndex = descr->nb_items ? descr->selected_item : -1;
2093 return LISTBOX_HandleLButtonDown( descr, keys, x, y);
2095 else if (descr->style & LBS_NOTIFY)
2096 SEND_NOTIFICATION( descr, LBN_DBLCLK );
2100 POINT screenMousePos;
2101 HWND hWndOldCapture;
2103 /* Check the Non-Client Area */
2104 screenMousePos = mousePos;
2105 hWndOldCapture = GetCapture();
2107 GetWindowRect(descr->self, &screenRect);
2108 ClientToScreen(descr->self, &screenMousePos);
2110 if(!PtInRect(&screenRect, screenMousePos))
2112 LISTBOX_SetCaretIndex( descr, descr->lphc->droppedIndex, FALSE );
2113 LISTBOX_SetSelection( descr, descr->lphc->droppedIndex, FALSE, FALSE );
2114 COMBO_FlipListbox( descr->lphc, FALSE, FALSE );
2118 /* Check to see the NC is a scrollbar */
2120 LONG style = GetWindowLongW( descr->self, GWL_STYLE );
2121 /* Check Vertical scroll bar */
2122 if (style & WS_VSCROLL)
2124 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2125 if (PtInRect( &clientRect, mousePos ))
2126 nHitTestType = HTVSCROLL;
2128 /* Check horizontal scroll bar */
2129 if (style & WS_HSCROLL)
2131 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2132 if (PtInRect( &clientRect, mousePos ))
2133 nHitTestType = HTHSCROLL;
2135 /* Windows sends this message when a scrollbar is clicked
2138 if(nHitTestType != 0)
2140 SendMessageW(descr->self, WM_NCLBUTTONDOWN, nHitTestType,
2141 MAKELONG(screenMousePos.x, screenMousePos.y));
2143 /* Resume the Capture after scrolling is complete
2145 if(hWndOldCapture != 0)
2146 SetCapture(hWndOldCapture);
2152 /***********************************************************************
2153 * LISTBOX_HandleLButtonUp
2155 static LRESULT LISTBOX_HandleLButtonUp( LB_DESCR *descr )
2157 if (LISTBOX_Timer != LB_TIMER_NONE)
2158 KillSystemTimer( descr->self, LB_TIMER_ID );
2159 LISTBOX_Timer = LB_TIMER_NONE;
2160 if (descr->captured)
2162 descr->captured = FALSE;
2163 if (GetCapture() == descr->self) ReleaseCapture();
2164 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2165 SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2171 /***********************************************************************
2172 * LISTBOX_HandleTimer
2174 * Handle scrolling upon a timer event.
2175 * Return TRUE if scrolling should continue.
2177 static LRESULT LISTBOX_HandleTimer( LB_DESCR *descr, INT index, TIMER_DIRECTION dir )
2182 if (descr->top_item) index = descr->top_item - 1;
2186 if (descr->top_item) index -= descr->page_size;
2189 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2190 if (index == descr->focus_item) index++;
2191 if (index >= descr->nb_items) index = descr->nb_items - 1;
2193 case LB_TIMER_RIGHT:
2194 if (index + descr->page_size < descr->nb_items)
2195 index += descr->page_size;
2200 if (index == descr->focus_item) return FALSE;
2201 LISTBOX_MoveCaret( descr, index, FALSE );
2206 /***********************************************************************
2207 * LISTBOX_HandleSystemTimer
2209 * WM_SYSTIMER handler.
2211 static LRESULT LISTBOX_HandleSystemTimer( LB_DESCR *descr )
2213 if (!LISTBOX_HandleTimer( descr, descr->focus_item, LISTBOX_Timer ))
2215 KillSystemTimer( descr->self, LB_TIMER_ID );
2216 LISTBOX_Timer = LB_TIMER_NONE;
2222 /***********************************************************************
2223 * LISTBOX_HandleMouseMove
2225 * WM_MOUSEMOVE handler.
2227 static void LISTBOX_HandleMouseMove( LB_DESCR *descr,
2231 TIMER_DIRECTION dir = LB_TIMER_NONE;
2233 if (!descr->captured) return;
2235 if (descr->style & LBS_MULTICOLUMN)
2238 else if (y >= descr->item_height * descr->page_size)
2239 y = descr->item_height * descr->page_size - 1;
2243 dir = LB_TIMER_LEFT;
2246 else if (x >= descr->width)
2248 dir = LB_TIMER_RIGHT;
2249 x = descr->width - 1;
2254 if (y < 0) dir = LB_TIMER_UP; /* above */
2255 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2258 index = LISTBOX_GetItemFromPoint( descr, x, y );
2259 if (index == -1) index = descr->focus_item;
2260 if (!LISTBOX_HandleTimer( descr, index, dir )) dir = LB_TIMER_NONE;
2262 /* Start/stop the system timer */
2264 if (dir != LB_TIMER_NONE)
2265 SetSystemTimer( descr->self, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2266 else if (LISTBOX_Timer != LB_TIMER_NONE)
2267 KillSystemTimer( descr->self, LB_TIMER_ID );
2268 LISTBOX_Timer = dir;
2272 /***********************************************************************
2273 * LISTBOX_HandleKeyDown
2275 static LRESULT LISTBOX_HandleKeyDown( LB_DESCR *descr, DWORD key )
2278 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2279 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2280 bForceSelection = FALSE; /* only for single select list */
2282 if (descr->style & LBS_WANTKEYBOARDINPUT)
2284 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2285 MAKEWPARAM(LOWORD(key), descr->focus_item),
2286 (LPARAM)descr->self );
2287 if (caret == -2) return 0;
2289 if (caret == -1) switch(key)
2292 if (descr->style & LBS_MULTICOLUMN)
2294 bForceSelection = FALSE;
2295 if (descr->focus_item >= descr->page_size)
2296 caret = descr->focus_item - descr->page_size;
2301 caret = descr->focus_item - 1;
2302 if (caret < 0) caret = 0;
2305 if (descr->style & LBS_MULTICOLUMN)
2307 bForceSelection = FALSE;
2308 if (descr->focus_item + descr->page_size < descr->nb_items)
2309 caret = descr->focus_item + descr->page_size;
2314 caret = descr->focus_item + 1;
2315 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2319 if (descr->style & LBS_MULTICOLUMN)
2321 INT page = descr->width / descr->column_width;
2322 if (page < 1) page = 1;
2323 caret = descr->focus_item - (page * descr->page_size) + 1;
2325 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2326 if (caret < 0) caret = 0;
2329 if (descr->style & LBS_MULTICOLUMN)
2331 INT page = descr->width / descr->column_width;
2332 if (page < 1) page = 1;
2333 caret = descr->focus_item + (page * descr->page_size) - 1;
2335 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2336 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2342 caret = descr->nb_items - 1;
2345 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2346 else if (descr->style & LBS_MULTIPLESEL)
2348 LISTBOX_SetSelection( descr, descr->focus_item,
2349 !descr->items[descr->focus_item].selected,
2350 (descr->style & LBS_NOTIFY) != 0 );
2354 bForceSelection = FALSE;
2356 if (bForceSelection) /* focused item is used instead of key */
2357 caret = descr->focus_item;
2360 if (((descr->style & LBS_EXTENDEDSEL) &&
2361 !(GetKeyState( VK_SHIFT ) & 0x8000)) ||
2362 !IS_MULTISELECT(descr))
2363 descr->anchor_item = caret;
2364 LISTBOX_MoveCaret( descr, caret, TRUE );
2366 if (descr->style & LBS_MULTIPLESEL)
2367 descr->selected_item = caret;
2369 LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2370 if (descr->style & LBS_NOTIFY)
2374 /* make sure that combo parent doesn't hide us */
2375 descr->lphc->wState |= CBF_NOROLLUP;
2377 if (descr->nb_items) SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2384 /***********************************************************************
2385 * LISTBOX_HandleChar
2387 static LRESULT LISTBOX_HandleChar( LB_DESCR *descr, WCHAR charW )
2395 if (descr->style & LBS_WANTKEYBOARDINPUT)
2397 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2398 MAKEWPARAM(charW, descr->focus_item),
2399 (LPARAM)descr->self );
2400 if (caret == -2) return 0;
2403 caret = LISTBOX_FindString( descr, descr->focus_item, str, FALSE);
2406 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2407 LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2408 LISTBOX_MoveCaret( descr, caret, TRUE );
2409 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2410 SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2416 /***********************************************************************
2419 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2422 MEASUREITEMSTRUCT mis;
2425 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2428 GetClientRect( hwnd, &rect );
2430 descr->owner = GetParent( descr->self );
2431 descr->style = GetWindowLongW( descr->self, GWL_STYLE );
2432 descr->width = rect.right - rect.left;
2433 descr->height = rect.bottom - rect.top;
2434 descr->items = NULL;
2435 descr->nb_items = 0;
2436 descr->top_item = 0;
2437 descr->selected_item = -1;
2438 descr->focus_item = 0;
2439 descr->anchor_item = -1;
2440 descr->item_height = 1;
2441 descr->page_size = 1;
2442 descr->column_width = 150;
2443 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0;
2444 descr->horz_pos = 0;
2447 descr->caret_on = lphc ? FALSE : TRUE;
2448 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2449 descr->in_focus = FALSE;
2450 descr->captured = FALSE;
2452 descr->locale = 0; /* FIXME */
2455 if (is_old_app(descr) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2457 /* Win95 document "List Box Differences" from MSDN:
2458 If a list box in a version 3.x application has either the
2459 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2460 horizontal and vertical scroll bars.
2462 descr->style |= WS_VSCROLL | WS_HSCROLL;
2467 TRACE("[%p]: resetting owner %p -> %p\n", descr->self, descr->owner, lphc->self );
2468 descr->owner = lphc->self;
2471 SetWindowLongW( descr->self, 0, (LONG)descr );
2473 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2475 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2476 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2477 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2478 descr->item_height = LISTBOX_SetFont( descr, 0 );
2480 if (descr->style & LBS_OWNERDRAWFIXED)
2482 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2484 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2485 descr->item_height = lphc->fixedOwnerDrawHeight;
2489 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
2490 mis.CtlType = ODT_LISTBOX;
2495 mis.itemHeight = descr->item_height;
2496 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2497 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2501 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2506 /***********************************************************************
2509 static BOOL LISTBOX_Destroy( LB_DESCR *descr )
2511 LISTBOX_ResetContent( descr );
2512 SetWindowLongW( descr->self, 0, 0 );
2513 HeapFree( GetProcessHeap(), 0, descr );
2518 /***********************************************************************
2519 * ListBoxWndProc_common
2521 static LRESULT WINAPI ListBoxWndProc_common( HWND hwnd, UINT msg,
2522 WPARAM wParam, LPARAM lParam, BOOL unicode )
2524 LB_DESCR *descr = (LB_DESCR *)GetWindowLongW( hwnd, 0 );
2525 LPHEADCOMBO lphc = 0;
2530 if (!IsWindow(hwnd)) return 0;
2532 if (msg == WM_CREATE)
2534 CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam;
2535 if (lpcs->style & LBS_COMBOBOX) lphc = (LPHEADCOMBO)lpcs->lpCreateParams;
2536 if (!LISTBOX_Create( hwnd, lphc )) return -1;
2537 TRACE("creating wnd=%p descr=%lx\n", hwnd, GetWindowLongW( hwnd, 0 ) );
2540 /* Ignore all other messages before we get a WM_CREATE */
2541 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2542 DefWindowProcA( hwnd, msg, wParam, lParam );
2544 if (descr->style & LBS_COMBOBOX) lphc = descr->lphc;
2546 TRACE("[%p]: msg %s wp %08x lp %08lx\n",
2547 descr->self, SPY_GetMsgName(msg, descr->self), wParam, lParam );
2551 case LB_RESETCONTENT16:
2552 case LB_RESETCONTENT:
2553 LISTBOX_ResetContent( descr );
2554 LISTBOX_UpdateScroll( descr );
2555 InvalidateRect( descr->self, NULL, TRUE );
2558 case LB_ADDSTRING16:
2559 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2565 if(unicode || !HAS_STRINGS(descr))
2566 textW = (LPWSTR)lParam;
2569 LPSTR textA = (LPSTR)lParam;
2570 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2571 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2572 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2574 wParam = LISTBOX_FindStringPos( descr, textW, FALSE );
2575 ret = LISTBOX_InsertString( descr, wParam, textW );
2576 if (!unicode && HAS_STRINGS(descr))
2577 HeapFree(GetProcessHeap(), 0, textW);
2581 case LB_INSERTSTRING16:
2582 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2583 wParam = (INT)(INT16)wParam;
2585 case LB_INSERTSTRING:
2589 if(unicode || !HAS_STRINGS(descr))
2590 textW = (LPWSTR)lParam;
2593 LPSTR textA = (LPSTR)lParam;
2594 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2595 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2596 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2598 ret = LISTBOX_InsertString( descr, wParam, textW );
2599 if(!unicode && HAS_STRINGS(descr))
2600 HeapFree(GetProcessHeap(), 0, textW);
2605 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2611 if(unicode || !HAS_STRINGS(descr))
2612 textW = (LPWSTR)lParam;
2615 LPSTR textA = (LPSTR)lParam;
2616 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2617 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2618 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2620 wParam = LISTBOX_FindFileStrPos( descr, textW );
2621 ret = LISTBOX_InsertString( descr, wParam, textW );
2622 if(!unicode && HAS_STRINGS(descr))
2623 HeapFree(GetProcessHeap(), 0, textW);
2627 case LB_DELETESTRING16:
2628 case LB_DELETESTRING:
2629 if (LISTBOX_RemoveItem( descr, wParam) != LB_ERR)
2630 return descr->nb_items;
2634 case LB_GETITEMDATA16:
2635 case LB_GETITEMDATA:
2636 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2638 return descr->items[wParam].data;
2640 case LB_SETITEMDATA16:
2641 case LB_SETITEMDATA:
2642 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2644 descr->items[wParam].data = (DWORD)lParam;
2649 return descr->nb_items;
2652 lParam = (LPARAM)MapSL(lParam);
2655 return LISTBOX_GetText( descr, wParam, (LPWSTR)lParam, unicode );
2657 case LB_GETTEXTLEN16:
2660 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2662 if (!HAS_STRINGS(descr)) return sizeof(DWORD);
2663 if (unicode) return strlenW( descr->items[wParam].str );
2664 return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
2665 strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
2667 case LB_GETCURSEL16:
2669 if (descr->nb_items == 0)
2671 if (!IS_MULTISELECT(descr))
2672 return descr->selected_item;
2673 if (descr->selected_item != -1)
2674 return descr->selected_item;
2675 return descr->focus_item;
2676 /* otherwise, if the user tries to move the selection with the */
2677 /* arrow keys, we will give the application something to choke on */
2678 case LB_GETTOPINDEX16:
2679 case LB_GETTOPINDEX:
2680 return descr->top_item;
2682 case LB_GETITEMHEIGHT16:
2683 case LB_GETITEMHEIGHT:
2684 return LISTBOX_GetItemHeight( descr, wParam );
2686 case LB_SETITEMHEIGHT16:
2687 lParam = LOWORD(lParam);
2689 case LB_SETITEMHEIGHT:
2690 return LISTBOX_SetItemHeight( descr, wParam, lParam, TRUE );
2692 case LB_ITEMFROMPOINT:
2697 pt.x = LOWORD(lParam);
2698 pt.y = HIWORD(lParam);
2701 rect.right = descr->width;
2702 rect.bottom = descr->height;
2704 return MAKELONG( LISTBOX_GetItemFromPoint(descr, pt.x, pt.y),
2705 !PtInRect( &rect, pt ) );
2708 case LB_SETCARETINDEX16:
2709 case LB_SETCARETINDEX:
2710 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2711 if (LISTBOX_SetCaretIndex( descr, wParam, !lParam ) == LB_ERR)
2718 case LB_GETCARETINDEX16:
2719 case LB_GETCARETINDEX:
2720 return descr->focus_item;
2722 case LB_SETTOPINDEX16:
2723 case LB_SETTOPINDEX:
2724 return LISTBOX_SetTopItem( descr, wParam, TRUE );
2726 case LB_SETCOLUMNWIDTH16:
2727 case LB_SETCOLUMNWIDTH:
2728 return LISTBOX_SetColumnWidth( descr, wParam );
2730 case LB_GETITEMRECT16:
2733 RECT16 *r16 = MapSL(lParam);
2734 ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2735 r16->left = rect.left;
2736 r16->top = rect.top;
2737 r16->right = rect.right;
2738 r16->bottom = rect.bottom;
2742 case LB_GETITEMRECT:
2743 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2745 case LB_FINDSTRING16:
2746 wParam = (INT)(INT16)wParam;
2747 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2753 if(unicode || !HAS_STRINGS(descr))
2754 textW = (LPWSTR)lParam;
2757 LPSTR textA = (LPSTR)lParam;
2758 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2759 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2760 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2762 ret = LISTBOX_FindString( descr, wParam, textW, FALSE );
2763 if(!unicode && HAS_STRINGS(descr))
2764 HeapFree(GetProcessHeap(), 0, textW);
2768 case LB_FINDSTRINGEXACT16:
2769 wParam = (INT)(INT16)wParam;
2770 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2772 case LB_FINDSTRINGEXACT:
2776 if(unicode || !HAS_STRINGS(descr))
2777 textW = (LPWSTR)lParam;
2780 LPSTR textA = (LPSTR)lParam;
2781 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2782 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2783 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2785 ret = LISTBOX_FindString( descr, wParam, textW, TRUE );
2786 if(!unicode && HAS_STRINGS(descr))
2787 HeapFree(GetProcessHeap(), 0, textW);
2791 case LB_SELECTSTRING16:
2792 wParam = (INT)(INT16)wParam;
2793 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2795 case LB_SELECTSTRING:
2800 if(HAS_STRINGS(descr))
2801 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2802 debugstr_a((LPSTR)lParam));
2803 if(unicode || !HAS_STRINGS(descr))
2804 textW = (LPWSTR)lParam;
2807 LPSTR textA = (LPSTR)lParam;
2808 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2809 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2810 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2812 index = LISTBOX_FindString( descr, wParam, textW, FALSE );
2813 if(!unicode && HAS_STRINGS(descr))
2814 HeapFree(GetProcessHeap(), 0, textW);
2815 if (index != LB_ERR)
2817 LISTBOX_MoveCaret( descr, index, TRUE );
2818 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
2824 wParam = (INT)(INT16)wParam;
2827 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2829 return descr->items[wParam].selected;
2832 lParam = (INT)(INT16)lParam;
2835 return LISTBOX_SetSelection( descr, lParam, wParam, FALSE );
2837 case LB_SETCURSEL16:
2838 wParam = (INT)(INT16)wParam;
2841 if (IS_MULTISELECT(descr)) return LB_ERR;
2842 LISTBOX_SetCaretIndex( descr, wParam, TRUE );
2843 ret = LISTBOX_SetSelection( descr, wParam, TRUE, FALSE );
2844 if (lphc && ret != LB_ERR) ret = descr->selected_item;
2847 case LB_GETSELCOUNT16:
2848 case LB_GETSELCOUNT:
2849 return LISTBOX_GetSelCount( descr );
2851 case LB_GETSELITEMS16:
2852 return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2854 case LB_GETSELITEMS:
2855 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2857 case LB_SELITEMRANGE16:
2858 case LB_SELITEMRANGE:
2859 if (LOWORD(lParam) <= HIWORD(lParam))
2860 return LISTBOX_SelectItemRange( descr, LOWORD(lParam),
2861 HIWORD(lParam), wParam );
2863 return LISTBOX_SelectItemRange( descr, HIWORD(lParam),
2864 LOWORD(lParam), wParam );
2866 case LB_SELITEMRANGEEX16:
2867 case LB_SELITEMRANGEEX:
2868 if ((INT)lParam >= (INT)wParam)
2869 return LISTBOX_SelectItemRange( descr, wParam, lParam, TRUE );
2871 return LISTBOX_SelectItemRange( descr, lParam, wParam, FALSE);
2873 case LB_GETHORIZONTALEXTENT16:
2874 case LB_GETHORIZONTALEXTENT:
2875 return descr->horz_extent;
2877 case LB_SETHORIZONTALEXTENT16:
2878 case LB_SETHORIZONTALEXTENT:
2879 return LISTBOX_SetHorizontalExtent( descr, wParam );
2881 case LB_GETANCHORINDEX16:
2882 case LB_GETANCHORINDEX:
2883 return descr->anchor_item;
2885 case LB_SETANCHORINDEX16:
2886 wParam = (INT)(INT16)wParam;
2888 case LB_SETANCHORINDEX:
2889 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2891 descr->anchor_item = (INT)wParam;
2895 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2896 * be set automatically (this is different in Win32) */
2897 if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2898 lParam = (LPARAM)MapSL(lParam);
2905 textW = (LPWSTR)lParam;
2908 LPSTR textA = (LPSTR)lParam;
2909 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2910 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2911 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2913 ret = LISTBOX_Directory( descr, wParam, textW, msg == LB_DIR );
2915 HeapFree(GetProcessHeap(), 0, textW);
2920 return descr->locale;
2923 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2926 case LB_INITSTORAGE:
2927 return LISTBOX_InitStorage( descr, wParam );
2930 return LISTBOX_SetCount( descr, (INT)wParam );
2932 case LB_SETTABSTOPS16:
2933 return LISTBOX_SetTabStops( descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2935 case LB_SETTABSTOPS:
2936 return LISTBOX_SetTabStops( descr, wParam, (LPINT)lParam, FALSE );
2940 if (descr->caret_on)
2942 descr->caret_on = TRUE;
2943 if ((descr->focus_item != -1) && (descr->in_focus))
2944 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2949 if (!descr->caret_on)
2951 descr->caret_on = FALSE;
2952 if ((descr->focus_item != -1) && (descr->in_focus))
2953 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2957 return LISTBOX_Destroy( descr );
2960 InvalidateRect( descr->self, NULL, TRUE );
2964 LISTBOX_SetRedraw( descr, wParam != 0 );
2968 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2973 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( descr->self, &ps );
2974 ret = LISTBOX_Paint( descr, hdc );
2975 if( !wParam ) EndPaint( descr->self, &ps );
2979 LISTBOX_UpdateSize( descr );
2982 return (LRESULT)descr->font;
2984 LISTBOX_SetFont( descr, (HFONT)wParam );
2985 if (lParam) InvalidateRect( descr->self, 0, TRUE );
2988 descr->in_focus = TRUE;
2989 descr->caret_on = TRUE;
2990 if (descr->focus_item != -1)
2991 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2992 SEND_NOTIFICATION( descr, LBN_SETFOCUS );
2995 descr->in_focus = FALSE;
2996 if ((descr->focus_item != -1) && descr->caret_on)
2997 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2998 SEND_NOTIFICATION( descr, LBN_KILLFOCUS );
3001 return LISTBOX_HandleHScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3003 return LISTBOX_HandleVScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3005 if (wParam & (MK_SHIFT | MK_CONTROL))
3006 return DefWindowProcW( descr->self, msg, wParam, lParam );
3007 return LISTBOX_HandleMouseWheel( descr, (SHORT)HIWORD(wParam) );
3008 case WM_LBUTTONDOWN:
3010 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3011 (INT16)LOWORD(lParam),
3012 (INT16)HIWORD(lParam) );
3013 return LISTBOX_HandleLButtonDown( descr, wParam,
3014 (INT16)LOWORD(lParam),
3015 (INT16)HIWORD(lParam) );
3016 case WM_LBUTTONDBLCLK:
3018 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3019 (INT16)LOWORD(lParam),
3020 (INT16)HIWORD(lParam) );
3021 if (descr->style & LBS_NOTIFY)
3022 SEND_NOTIFICATION( descr, LBN_DBLCLK );
3025 if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) )
3027 BOOL captured = descr->captured;
3031 mousePos.x = (INT16)LOWORD(lParam);
3032 mousePos.y = (INT16)HIWORD(lParam);
3035 * If we are in a dropdown combobox, we simulate that
3036 * the mouse is captured to show the tracking of the item.
3038 if (GetClientRect(descr->self, &clientRect) && PtInRect( &clientRect, mousePos ))
3039 descr->captured = TRUE;
3041 LISTBOX_HandleMouseMove( descr, mousePos.x, mousePos.y);
3043 descr->captured = captured;
3045 else if (GetCapture() == descr->self)
3047 LISTBOX_HandleMouseMove( descr, (INT16)LOWORD(lParam),
3048 (INT16)HIWORD(lParam) );
3058 * If the mouse button "up" is not in the listbox,
3059 * we make sure there is no selection by re-selecting the
3060 * item that was selected when the listbox was made visible.
3062 mousePos.x = (INT16)LOWORD(lParam);
3063 mousePos.y = (INT16)HIWORD(lParam);
3065 GetClientRect(descr->self, &clientRect);
3068 * When the user clicks outside the combobox and the focus
3069 * is lost, the owning combobox will send a fake buttonup with
3070 * 0xFFFFFFF as the mouse location, we must also revert the
3071 * selection to the original selection.
3073 if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) )
3074 LISTBOX_MoveCaret( descr, lphc->droppedIndex, FALSE );
3076 return LISTBOX_HandleLButtonUp( descr );
3078 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3080 /* for some reason Windows makes it possible to
3081 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3083 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3084 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3085 && (wParam == VK_DOWN || wParam == VK_UP)) )
3087 COMBO_FlipListbox( lphc, FALSE, FALSE );
3091 return LISTBOX_HandleKeyDown( descr, wParam );
3096 charW = (WCHAR)wParam;
3099 CHAR charA = (CHAR)wParam;
3100 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
3102 return LISTBOX_HandleChar( descr, charW );
3105 return LISTBOX_HandleSystemTimer( descr );
3107 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
3110 HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
3111 wParam, (LPARAM)descr->self );
3112 TRACE("hbrush = %p\n", hbrush);
3114 hbrush = GetSysColorBrush(COLOR_WINDOW);
3117 GetClientRect(descr->self, &rect);
3118 FillRect((HDC)wParam, &rect, hbrush);
3123 if( lphc ) return 0;
3124 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3125 SendMessageA( descr->owner, msg, wParam, lParam );
3128 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3137 if ((msg >= WM_USER) && (msg < 0xc000))
3138 WARN("[%p]: unknown msg %04x wp %08x lp %08lx\n",
3139 hwnd, msg, wParam, lParam );
3142 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3143 DefWindowProcA( hwnd, msg, wParam, lParam );
3146 /***********************************************************************
3149 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3151 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3154 /***********************************************************************
3157 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3159 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE );