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"
47 #include "wine/unicode.h"
53 #include "wine/debug.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(listbox);
58 /* Items array granularity */
59 #define LB_ARRAY_GRANULARITY 16
61 /* Scrolling timeout in ms */
62 #define LB_SCROLL_TIMEOUT 50
64 /* Listbox system timer id */
67 /* flag listbox changed while setredraw false - internal style */
68 #define LBS_DISPLAYCHANGED 0x80000000
73 LPWSTR str; /* Item text */
74 BOOL selected; /* Is item selected? */
75 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
76 DWORD data; /* User data */
79 /* Listbox structure */
82 HWND self; /* Our own window handle */
83 HWND owner; /* Owner window to send notifications to */
84 UINT style; /* Window style */
85 INT width; /* Window width */
86 INT height; /* Window height */
87 LB_ITEMDATA *items; /* Array of items */
88 INT nb_items; /* Number of items */
89 INT top_item; /* Top visible item */
90 INT selected_item; /* Selected item */
91 INT focus_item; /* Item that has the focus */
92 INT anchor_item; /* Anchor item for extended selection */
93 INT item_height; /* Default item height */
94 INT page_size; /* Items per listbox page */
95 INT column_width; /* Column width for multi-column listboxes */
96 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
97 INT horz_pos; /* Horizontal position */
98 INT nb_tabs; /* Number of tabs in array */
99 INT *tabs; /* Array of tabs */
100 BOOL caret_on; /* Is caret on? */
101 BOOL captured; /* Is mouse captured? */
103 HFONT font; /* Current font */
104 LCID locale; /* Current locale for string comparisons */
105 LPHEADCOMBO lphc; /* ComboLBox */
109 #define IS_OWNERDRAW(descr) \
110 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
112 #define HAS_STRINGS(descr) \
113 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
116 #define IS_MULTISELECT(descr) \
117 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
118 !((descr)->style & LBS_NOSEL))
120 #define SEND_NOTIFICATION(descr,code) \
121 (SendMessageW( (descr)->owner, WM_COMMAND, \
122 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
124 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
126 /* Current timer status */
136 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
138 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
139 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
141 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect );
143 /*********************************************************************
144 * listbox class descriptor
146 const struct builtin_class_descr LISTBOX_builtin_class =
148 "ListBox", /* name */
149 CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
150 ListBoxWndProcA, /* procA */
151 ListBoxWndProcW, /* procW */
152 sizeof(LB_DESCR *), /* extra */
153 IDC_ARROW, /* cursor */
158 /*********************************************************************
159 * combolbox class descriptor
161 const struct builtin_class_descr COMBOLBOX_builtin_class =
163 "ComboLBox", /* name */
164 CS_DBLCLKS | CS_SAVEBITS, /* style */
165 ListBoxWndProcA, /* procA */
166 ListBoxWndProcW, /* procW */
167 sizeof(LB_DESCR *), /* extra */
168 IDC_ARROW, /* cursor */
173 /* check whether app is a Win 3.1 app */
174 inline static BOOL is_old_app( LB_DESCR *descr )
176 return (GetExpWinVer16( GetWindowLongPtrW(descr->self, GWLP_HINSTANCE) ) & 0xFF00 ) == 0x0300;
180 /***********************************************************************
183 void LISTBOX_Dump( LB_DESCR *descr )
188 TRACE( "Listbox:\n" );
189 TRACE( "hwnd=%p descr=%08x items=%d top=%d\n",
190 descr->self, (UINT)descr, descr->nb_items, descr->top_item );
191 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
193 TRACE( "%4d: %-40s %d %08lx %3d\n",
194 i, debugstr_w(item->str), item->selected, item->data, item->height );
199 /***********************************************************************
200 * LISTBOX_GetCurrentPageSize
202 * Return the current page size
204 static INT LISTBOX_GetCurrentPageSize( LB_DESCR *descr )
207 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
208 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
210 if ((height += descr->items[i].height) > descr->height) break;
212 if (i == descr->top_item) return 1;
213 else return i - descr->top_item;
217 /***********************************************************************
218 * LISTBOX_GetMaxTopIndex
220 * Return the maximum possible index for the top of the listbox.
222 static INT LISTBOX_GetMaxTopIndex( LB_DESCR *descr )
226 if (descr->style & LBS_OWNERDRAWVARIABLE)
228 page = descr->height;
229 for (max = descr->nb_items - 1; max >= 0; max--)
230 if ((page -= descr->items[max].height) < 0) break;
231 if (max < descr->nb_items - 1) max++;
233 else if (descr->style & LBS_MULTICOLUMN)
235 if ((page = descr->width / descr->column_width) < 1) page = 1;
236 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
237 max = (max - page) * descr->page_size;
241 max = descr->nb_items - descr->page_size;
243 if (max < 0) max = 0;
248 /***********************************************************************
249 * LISTBOX_UpdateScroll
251 * Update the scrollbars. Should be called whenever the content
252 * of the listbox changes.
254 static void LISTBOX_UpdateScroll( LB_DESCR *descr )
258 /* Check the listbox scroll bar flags individually before we call
259 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
260 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
261 scroll bar when we do not need one.
262 if (!(descr->style & WS_VSCROLL)) return;
265 /* It is important that we check descr->style, and not wnd->dwStyle,
266 for WS_VSCROLL, as the former is exactly the one passed in
267 argument to CreateWindow.
268 In Windows (and from now on in Wine :) a listbox created
269 with such a style (no WS_SCROLL) does not update
270 the scrollbar with listbox-related data, thus letting
271 the programmer use it for his/her own purposes. */
273 if (descr->style & LBS_NOREDRAW) return;
274 info.cbSize = sizeof(info);
276 if (descr->style & LBS_MULTICOLUMN)
279 info.nMax = (descr->nb_items - 1) / descr->page_size;
280 info.nPos = descr->top_item / descr->page_size;
281 info.nPage = descr->width / descr->column_width;
282 if (info.nPage < 1) info.nPage = 1;
283 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
284 if (descr->style & LBS_DISABLENOSCROLL)
285 info.fMask |= SIF_DISABLENOSCROLL;
286 if (descr->style & WS_HSCROLL)
287 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
289 info.fMask = SIF_RANGE;
290 if (descr->style & WS_VSCROLL)
291 SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
296 info.nMax = descr->nb_items - 1;
297 info.nPos = descr->top_item;
298 info.nPage = LISTBOX_GetCurrentPageSize( descr );
299 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
300 if (descr->style & LBS_DISABLENOSCROLL)
301 info.fMask |= SIF_DISABLENOSCROLL;
302 if (descr->style & WS_VSCROLL)
303 SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
305 if (descr->horz_extent)
308 info.nMax = descr->horz_extent - 1;
309 info.nPos = descr->horz_pos;
310 info.nPage = descr->width;
311 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
312 if (descr->style & LBS_DISABLENOSCROLL)
313 info.fMask |= SIF_DISABLENOSCROLL;
314 if (descr->style & WS_HSCROLL)
315 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
321 /***********************************************************************
324 * Set the top item of the listbox, scrolling up or down if necessary.
326 static LRESULT LISTBOX_SetTopItem( LB_DESCR *descr, INT index, BOOL scroll )
328 INT max = LISTBOX_GetMaxTopIndex( descr );
329 if (index > max) index = max;
330 if (index < 0) index = 0;
331 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
332 if (descr->top_item == index) return LB_OKAY;
333 if (descr->style & LBS_MULTICOLUMN)
335 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
336 if (scroll && (abs(diff) < descr->width))
337 ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
338 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
346 if (descr->style & LBS_OWNERDRAWVARIABLE)
350 if (index > descr->top_item)
352 for (i = index - 1; i >= descr->top_item; i--)
353 diff -= descr->items[i].height;
357 for (i = index; i < descr->top_item; i++)
358 diff += descr->items[i].height;
362 diff = (descr->top_item - index) * descr->item_height;
364 if (abs(diff) < descr->height)
365 ScrollWindowEx( descr->self, 0, diff, NULL, NULL, 0, NULL,
366 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
370 if (!scroll) InvalidateRect( descr->self, NULL, TRUE );
371 descr->top_item = index;
372 LISTBOX_UpdateScroll( descr );
377 /***********************************************************************
380 * Update the page size. Should be called when the size of
381 * the client area or the item height changes.
383 static void LISTBOX_UpdatePage( LB_DESCR *descr )
387 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
389 if (page_size == descr->page_size) return;
390 descr->page_size = page_size;
391 if (descr->style & LBS_MULTICOLUMN)
392 InvalidateRect( descr->self, NULL, TRUE );
393 LISTBOX_SetTopItem( descr, descr->top_item, FALSE );
397 /***********************************************************************
400 * Update the size of the listbox. Should be called when the size of
401 * the client area changes.
403 static void LISTBOX_UpdateSize( LB_DESCR *descr )
407 GetClientRect( descr->self, &rect );
408 descr->width = rect.right - rect.left;
409 descr->height = rect.bottom - rect.top;
410 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
415 GetWindowRect( descr->self, &rect );
416 if(descr->item_height != 0)
417 remaining = descr->height % descr->item_height;
420 if ((descr->height > descr->item_height) && remaining)
422 if (is_old_app(descr))
423 { /* give a margin for error to 16 bits programs - if we need
424 less than the height of the nonclient area, round to the
425 *next* number of items */
426 int ncheight = rect.bottom - rect.top - descr->height;
427 if ((descr->item_height - remaining) <= ncheight)
428 remaining = remaining - descr->item_height;
430 TRACE("[%p]: changing height %d -> %d\n",
431 descr->self, descr->height, descr->height - remaining );
432 SetWindowPos( descr->self, 0, 0, 0, rect.right - rect.left,
433 rect.bottom - rect.top - remaining,
434 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
438 TRACE("[%p]: new size = %d,%d\n", descr->self, descr->width, descr->height );
439 LISTBOX_UpdatePage( descr );
440 LISTBOX_UpdateScroll( descr );
442 /* Invalidate the focused item so it will be repainted correctly */
443 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
445 InvalidateRect( descr->self, &rect, FALSE );
450 /***********************************************************************
451 * LISTBOX_GetItemRect
453 * Get the rectangle enclosing an item, in listbox client coordinates.
454 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
456 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect )
458 /* Index <= 0 is legal even on empty listboxes */
459 if (index && (index >= descr->nb_items)) return -1;
460 SetRect( rect, 0, 0, descr->width, descr->height );
461 if (descr->style & LBS_MULTICOLUMN)
463 INT col = (index / descr->page_size) -
464 (descr->top_item / descr->page_size);
465 rect->left += col * descr->column_width;
466 rect->right = rect->left + descr->column_width;
467 rect->top += (index % descr->page_size) * descr->item_height;
468 rect->bottom = rect->top + descr->item_height;
470 else if (descr->style & LBS_OWNERDRAWVARIABLE)
473 rect->right += descr->horz_pos;
474 if ((index >= 0) && (index < descr->nb_items))
476 if (index < descr->top_item)
478 for (i = descr->top_item-1; i >= index; i--)
479 rect->top -= descr->items[i].height;
483 for (i = descr->top_item; i < index; i++)
484 rect->top += descr->items[i].height;
486 rect->bottom = rect->top + descr->items[index].height;
492 rect->top += (index - descr->top_item) * descr->item_height;
493 rect->bottom = rect->top + descr->item_height;
494 rect->right += descr->horz_pos;
497 return ((rect->left < descr->width) && (rect->right > 0) &&
498 (rect->top < descr->height) && (rect->bottom > 0));
502 /***********************************************************************
503 * LISTBOX_GetItemFromPoint
505 * Return the item nearest from point (x,y) (in client coordinates).
507 static INT LISTBOX_GetItemFromPoint( LB_DESCR *descr, INT x, INT y )
509 INT index = descr->top_item;
511 if (!descr->nb_items) return -1; /* No items */
512 if (descr->style & LBS_OWNERDRAWVARIABLE)
517 while (index < descr->nb_items)
519 if ((pos += descr->items[index].height) > y) break;
528 if ((pos -= descr->items[index].height) <= y) break;
532 else if (descr->style & LBS_MULTICOLUMN)
534 if (y >= descr->item_height * descr->page_size) return -1;
535 if (y >= 0) index += y / descr->item_height;
536 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
537 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
541 index += (y / descr->item_height);
543 if (index < 0) return 0;
544 if (index >= descr->nb_items) return -1;
549 /***********************************************************************
554 static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect,
555 INT index, UINT action, BOOL ignoreFocus )
557 LB_ITEMDATA *item = NULL;
558 if (index < descr->nb_items) item = &descr->items[index];
560 if (IS_OWNERDRAW(descr))
565 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
569 if (action == ODA_FOCUS)
570 DrawFocusRect( hdc, rect );
572 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
576 /* some programs mess with the clipping region when
577 drawing the item, *and* restore the previous region
578 after they are done, so a region has better to exist
579 else everything ends clipped */
580 GetClientRect(descr->self, &r);
581 hrgn = CreateRectRgnIndirect(&r);
582 SelectClipRgn( hdc, hrgn);
583 DeleteObject( hrgn );
585 dis.CtlType = ODT_LISTBOX;
587 dis.hwndItem = descr->self;
588 dis.itemAction = action;
592 if (item && item->selected) dis.itemState |= ODS_SELECTED;
593 if (!ignoreFocus && (descr->focus_item == index) &&
595 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
596 if (!IsWindowEnabled(descr->self)) dis.itemState |= ODS_DISABLED;
597 dis.itemData = item ? item->data : 0;
599 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%ld,%ld-%ld,%ld\n",
600 descr->self, index, item ? debugstr_w(item->str) : "", action,
601 dis.itemState, rect->left, rect->top, rect->right, rect->bottom );
602 SendMessageW(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
606 COLORREF oldText = 0, oldBk = 0;
608 if (action == ODA_FOCUS)
610 DrawFocusRect( hdc, rect );
613 if (item && item->selected)
615 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
616 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
619 TRACE("[%p]: painting %d (%s) action=%02x rect=%ld,%ld-%ld,%ld\n",
620 descr->self, index, item ? debugstr_w(item->str) : "", action,
621 rect->left, rect->top, rect->right, rect->bottom );
623 ExtTextOutW( hdc, rect->left + 1, rect->top,
624 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
625 else if (!(descr->style & LBS_USETABSTOPS))
626 ExtTextOutW( hdc, rect->left + 1, rect->top,
627 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
628 strlenW(item->str), NULL );
631 /* Output empty string to paint background in the full width. */
632 ExtTextOutW( hdc, rect->left + 1, rect->top,
633 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
634 TabbedTextOutW( hdc, rect->left + 1 , rect->top,
635 item->str, strlenW(item->str),
636 descr->nb_tabs, descr->tabs, 0);
638 if (item && item->selected)
640 SetBkColor( hdc, oldBk );
641 SetTextColor( hdc, oldText );
643 if (!ignoreFocus && (descr->focus_item == index) &&
645 (descr->in_focus)) DrawFocusRect( hdc, rect );
650 /***********************************************************************
653 * Change the redraw flag.
655 static void LISTBOX_SetRedraw( LB_DESCR *descr, BOOL on )
659 if (!(descr->style & LBS_NOREDRAW)) return;
660 descr->style &= ~LBS_NOREDRAW;
661 if (descr->style & LBS_DISPLAYCHANGED)
662 { /* page was changed while setredraw false, refresh automatically */
663 InvalidateRect(descr->self, NULL, TRUE);
664 if ((descr->top_item + descr->page_size) > descr->nb_items)
665 { /* reset top of page if less than number of items/page */
666 descr->top_item = descr->nb_items - descr->page_size;
667 if (descr->top_item < 0) descr->top_item = 0;
669 descr->style &= ~LBS_DISPLAYCHANGED;
671 LISTBOX_UpdateScroll( descr );
673 else descr->style |= LBS_NOREDRAW;
677 /***********************************************************************
678 * LISTBOX_RepaintItem
680 * Repaint a single item synchronously.
682 static void LISTBOX_RepaintItem( LB_DESCR *descr, INT index, UINT action )
687 HBRUSH hbrush, oldBrush = 0;
689 /* Do not repaint the item if the item is not visible */
690 if (!IsWindowVisible(descr->self)) return;
691 if (descr->style & LBS_NOREDRAW)
693 descr->style |= LBS_DISPLAYCHANGED;
696 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
697 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
698 if (descr->font) oldFont = SelectObject( hdc, descr->font );
699 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
700 (WPARAM)hdc, (LPARAM)descr->self );
701 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
702 if (!IsWindowEnabled(descr->self))
703 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
704 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
705 LISTBOX_PaintItem( descr, hdc, &rect, index, action, FALSE );
706 if (oldFont) SelectObject( hdc, oldFont );
707 if (oldBrush) SelectObject( hdc, oldBrush );
708 ReleaseDC( descr->self, hdc );
712 /***********************************************************************
713 * LISTBOX_InitStorage
715 static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items )
719 nb_items += LB_ARRAY_GRANULARITY - 1;
720 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
722 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
723 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
724 nb_items * sizeof(LB_ITEMDATA));
727 item = HeapAlloc( GetProcessHeap(), 0,
728 nb_items * sizeof(LB_ITEMDATA));
733 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
741 /***********************************************************************
742 * LISTBOX_SetTabStops
744 static BOOL LISTBOX_SetTabStops( LB_DESCR *descr, INT count, LPINT tabs, BOOL short_ints )
746 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
747 if (descr->tabs) HeapFree( GetProcessHeap(), 0, descr->tabs );
748 if (!(descr->nb_tabs = count))
753 /* FIXME: count = 1 */
754 if (!(descr->tabs = (INT *)HeapAlloc( GetProcessHeap(), 0,
755 descr->nb_tabs * sizeof(INT) )))
760 LPINT16 p = (LPINT16)tabs;
762 TRACE("[%p]: settabstops ", descr->self );
763 for (i = 0; i < descr->nb_tabs; i++) {
764 descr->tabs[i] = *p++<<1; /* FIXME */
765 TRACE("%hd ", descr->tabs[i]);
769 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
770 /* FIXME: repaint the window? */
775 /***********************************************************************
778 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPWSTR buffer, BOOL unicode )
780 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
781 if (HAS_STRINGS(descr))
785 DWORD len = strlenW(descr->items[index].str);
788 return WideCharToMultiByte( CP_ACP, 0, descr->items[index].str, len,
789 NULL, 0, NULL, NULL );
792 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
796 strcpyW( buffer, descr->items[index].str );
797 return strlenW(buffer);
801 return WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, (LPSTR)buffer, 0x7FFFFFFF, NULL, NULL) - 1;
805 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
806 return sizeof(DWORD);
811 /***********************************************************************
812 * LISTBOX_FindStringPos
814 * Find the nearest string located before a given string in sort order.
815 * If 'exact' is TRUE, return an error if we don't get an exact match.
817 static INT LISTBOX_FindStringPos( LB_DESCR *descr, LPCWSTR str, BOOL exact )
819 INT index, min, max, res = -1;
821 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
823 max = descr->nb_items;
826 index = (min + max) / 2;
827 if (HAS_STRINGS(descr))
828 res = lstrcmpiW( str, descr->items[index].str);
831 COMPAREITEMSTRUCT cis;
832 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
834 cis.CtlType = ODT_LISTBOX;
836 cis.hwndItem = descr->self;
837 /* note that some application (MetaStock) expects the second item
838 * to be in the listbox */
840 cis.itemData1 = (DWORD)str;
842 cis.itemData2 = descr->items[index].data;
843 cis.dwLocaleId = descr->locale;
844 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
846 if (!res) return index;
847 if (res < 0) max = index;
848 else min = index + 1;
850 return exact ? -1 : max;
854 /***********************************************************************
855 * LISTBOX_FindFileStrPos
857 * Find the nearest string located before a given string in directory
858 * sort order (i.e. first files, then directories, then drives).
860 static INT LISTBOX_FindFileStrPos( LB_DESCR *descr, LPCWSTR str )
862 INT min, max, res = -1;
864 if (!HAS_STRINGS(descr))
865 return LISTBOX_FindStringPos( descr, str, FALSE );
867 max = descr->nb_items;
870 INT index = (min + max) / 2;
871 LPCWSTR p = descr->items[index].str;
872 if (*p == '[') /* drive or directory */
874 if (*str != '[') res = -1;
875 else if (p[1] == '-') /* drive */
877 if (str[1] == '-') res = str[2] - p[2];
882 if (str[1] == '-') res = 1;
883 else res = lstrcmpiW( str, p );
888 if (*str == '[') res = 1;
889 else res = lstrcmpiW( str, p );
891 if (!res) return index;
892 if (res < 0) max = index;
893 else min = index + 1;
899 /***********************************************************************
902 * Find the item beginning with a given string.
904 static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exact )
909 if (start >= descr->nb_items) start = -1;
910 item = descr->items + start + 1;
911 if (HAS_STRINGS(descr))
913 if (!str || ! str[0] ) return LB_ERR;
916 for (i = start + 1; i < descr->nb_items; i++, item++)
917 if (!lstrcmpiW( str, item->str )) return i;
918 for (i = 0, item = descr->items; i <= start; i++, item++)
919 if (!lstrcmpiW( str, item->str )) return i;
923 /* Special case for drives and directories: ignore prefix */
924 #define CHECK_DRIVE(item) \
925 if ((item)->str[0] == '[') \
927 if (!strncmpiW( str, (item)->str+1, len )) return i; \
928 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
932 INT len = strlenW(str);
933 for (i = start + 1; i < descr->nb_items; i++, item++)
935 if (!strncmpiW( str, item->str, len )) return i;
938 for (i = 0, item = descr->items; i <= start; i++, item++)
940 if (!strncmpiW( str, item->str, len )) return i;
948 if (exact && (descr->style & LBS_SORT))
949 /* If sorted, use a WM_COMPAREITEM binary search */
950 return LISTBOX_FindStringPos( descr, str, TRUE );
952 /* Otherwise use a linear search */
953 for (i = start + 1; i < descr->nb_items; i++, item++)
954 if (item->data == (DWORD)str) return i;
955 for (i = 0, item = descr->items; i <= start; i++, item++)
956 if (item->data == (DWORD)str) return i;
962 /***********************************************************************
963 * LISTBOX_GetSelCount
965 static LRESULT LISTBOX_GetSelCount( LB_DESCR *descr )
968 LB_ITEMDATA *item = descr->items;
970 if (!(descr->style & LBS_MULTIPLESEL) ||
971 (descr->style & LBS_NOSEL))
973 for (i = count = 0; i < descr->nb_items; i++, item++)
974 if (item->selected) count++;
979 /***********************************************************************
980 * LISTBOX_GetSelItems16
982 static LRESULT LISTBOX_GetSelItems16( LB_DESCR *descr, INT16 max, LPINT16 array )
985 LB_ITEMDATA *item = descr->items;
987 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
988 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
989 if (item->selected) array[count++] = (INT16)i;
994 /***********************************************************************
995 * LISTBOX_GetSelItems
997 static LRESULT LISTBOX_GetSelItems( LB_DESCR *descr, INT max, LPINT array )
1000 LB_ITEMDATA *item = descr->items;
1002 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1003 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
1004 if (item->selected) array[count++] = i;
1009 /***********************************************************************
1012 static LRESULT LISTBOX_Paint( LB_DESCR *descr, HDC hdc )
1014 INT i, col_pos = descr->page_size - 1;
1016 RECT focusRect = {-1, -1, -1, -1};
1018 HBRUSH hbrush, oldBrush = 0;
1020 if (descr->style & LBS_NOREDRAW) return 0;
1022 SetRect( &rect, 0, 0, descr->width, descr->height );
1023 if (descr->style & LBS_MULTICOLUMN)
1024 rect.right = rect.left + descr->column_width;
1025 else if (descr->horz_pos)
1027 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
1028 rect.right += descr->horz_pos;
1031 if (descr->font) oldFont = SelectObject( hdc, descr->font );
1032 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1033 (WPARAM)hdc, (LPARAM)descr->self );
1034 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1035 if (!IsWindowEnabled(descr->self)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1037 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1040 /* Special case for empty listbox: paint focus rect */
1041 rect.bottom = rect.top + descr->item_height;
1042 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1043 &rect, NULL, 0, NULL );
1044 LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, FALSE );
1045 rect.top = rect.bottom;
1048 /* Paint all the item, regarding the selection
1049 Focus state will be painted after */
1051 for (i = descr->top_item; i < descr->nb_items; i++)
1053 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1054 rect.bottom = rect.top + descr->item_height;
1056 rect.bottom = rect.top + descr->items[i].height;
1058 if (i == descr->focus_item)
1060 /* keep the focus rect, to paint the focus item after */
1061 focusRect.left = rect.left;
1062 focusRect.right = rect.right;
1063 focusRect.top = rect.top;
1064 focusRect.bottom = rect.bottom;
1066 LISTBOX_PaintItem( descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1067 rect.top = rect.bottom;
1069 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1071 if (!IS_OWNERDRAW(descr))
1073 /* Clear the bottom of the column */
1074 if (rect.top < descr->height)
1076 rect.bottom = descr->height;
1077 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1078 &rect, NULL, 0, NULL );
1082 /* Go to the next column */
1083 rect.left += descr->column_width;
1084 rect.right += descr->column_width;
1086 col_pos = descr->page_size - 1;
1091 if (rect.top >= descr->height) break;
1095 /* Paint the focus item now */
1096 if (focusRect.top != focusRect.bottom &&
1097 descr->caret_on && descr->in_focus)
1098 LISTBOX_PaintItem( descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1100 if (!IS_OWNERDRAW(descr))
1102 /* Clear the remainder of the client area */
1103 if (rect.top < descr->height)
1105 rect.bottom = descr->height;
1106 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1107 &rect, NULL, 0, NULL );
1109 if (rect.right < descr->width)
1111 rect.left = rect.right;
1112 rect.right = descr->width;
1114 rect.bottom = descr->height;
1115 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1116 &rect, NULL, 0, NULL );
1119 if (oldFont) SelectObject( hdc, oldFont );
1120 if (oldBrush) SelectObject( hdc, oldBrush );
1125 /***********************************************************************
1126 * LISTBOX_InvalidateItems
1128 * Invalidate all items from a given item. If the specified item is not
1129 * visible, nothing happens.
1131 static void LISTBOX_InvalidateItems( LB_DESCR *descr, INT index )
1135 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1137 if (descr->style & LBS_NOREDRAW)
1139 descr->style |= LBS_DISPLAYCHANGED;
1142 rect.bottom = descr->height;
1143 InvalidateRect( descr->self, &rect, TRUE );
1144 if (descr->style & LBS_MULTICOLUMN)
1146 /* Repaint the other columns */
1147 rect.left = rect.right;
1148 rect.right = descr->width;
1150 InvalidateRect( descr->self, &rect, TRUE );
1155 static void LISTBOX_InvalidateItemRect( LB_DESCR *descr, INT index )
1159 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1160 InvalidateRect( descr->self, &rect, TRUE );
1163 /***********************************************************************
1164 * LISTBOX_GetItemHeight
1166 static LRESULT LISTBOX_GetItemHeight( LB_DESCR *descr, INT index )
1168 if (descr->style & LBS_OWNERDRAWVARIABLE)
1170 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1171 return descr->items[index].height;
1173 else return descr->item_height;
1177 /***********************************************************************
1178 * LISTBOX_SetItemHeight
1180 static LRESULT LISTBOX_SetItemHeight( LB_DESCR *descr, INT index, INT height, BOOL repaint )
1182 if (!height) height = 1;
1184 if (descr->style & LBS_OWNERDRAWVARIABLE)
1186 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1187 TRACE("[%p]: item %d height = %d\n", descr->self, index, height );
1188 descr->items[index].height = height;
1189 LISTBOX_UpdateScroll( descr );
1191 LISTBOX_InvalidateItems( descr, index );
1193 else if (height != descr->item_height)
1195 TRACE("[%p]: new height = %d\n", descr->self, height );
1196 descr->item_height = height;
1197 LISTBOX_UpdatePage( descr );
1198 LISTBOX_UpdateScroll( descr );
1200 InvalidateRect( descr->self, 0, TRUE );
1206 /***********************************************************************
1207 * LISTBOX_SetHorizontalPos
1209 static void LISTBOX_SetHorizontalPos( LB_DESCR *descr, INT pos )
1213 if (pos > descr->horz_extent - descr->width)
1214 pos = descr->horz_extent - descr->width;
1215 if (pos < 0) pos = 0;
1216 if (!(diff = descr->horz_pos - pos)) return;
1217 TRACE("[%p]: new horz pos = %d\n", descr->self, pos );
1218 descr->horz_pos = pos;
1219 LISTBOX_UpdateScroll( descr );
1220 if (abs(diff) < descr->width)
1223 /* Invalidate the focused item so it will be repainted correctly */
1224 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
1225 InvalidateRect( descr->self, &rect, TRUE );
1226 ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
1227 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1230 InvalidateRect( descr->self, NULL, TRUE );
1234 /***********************************************************************
1235 * LISTBOX_SetHorizontalExtent
1237 static LRESULT LISTBOX_SetHorizontalExtent( LB_DESCR *descr, INT extent )
1239 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1241 if (extent <= 0) extent = 1;
1242 if (extent == descr->horz_extent) return LB_OKAY;
1243 TRACE("[%p]: new horz extent = %d\n", descr->self, extent );
1244 descr->horz_extent = extent;
1245 if (descr->horz_pos > extent - descr->width)
1246 LISTBOX_SetHorizontalPos( descr, extent - descr->width );
1248 LISTBOX_UpdateScroll( descr );
1253 /***********************************************************************
1254 * LISTBOX_SetColumnWidth
1256 static LRESULT LISTBOX_SetColumnWidth( LB_DESCR *descr, INT width)
1258 if (width == descr->column_width) return LB_OKAY;
1259 TRACE("[%p]: new column width = %d\n", descr->self, width );
1260 descr->column_width = width;
1261 LISTBOX_UpdatePage( descr );
1266 /***********************************************************************
1269 * Returns the item height.
1271 static INT LISTBOX_SetFont( LB_DESCR *descr, HFONT font )
1279 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE )))
1281 ERR("unable to get DC.\n" );
1284 if (font) oldFont = SelectObject( hdc, font );
1285 GetTextMetricsW( hdc, &tm );
1286 if (oldFont) SelectObject( hdc, oldFont );
1287 ReleaseDC( descr->self, hdc );
1288 if (!IS_OWNERDRAW(descr))
1289 LISTBOX_SetItemHeight( descr, 0, tm.tmHeight, FALSE );
1294 /***********************************************************************
1295 * LISTBOX_MakeItemVisible
1297 * Make sure that a given item is partially or fully visible.
1299 static void LISTBOX_MakeItemVisible( LB_DESCR *descr, INT index, BOOL fully )
1303 if (index <= descr->top_item) top = index;
1304 else if (descr->style & LBS_MULTICOLUMN)
1306 INT cols = descr->width;
1307 if (!fully) cols += descr->column_width - 1;
1308 if (cols >= descr->column_width) cols /= descr->column_width;
1310 if (index < descr->top_item + (descr->page_size * cols)) return;
1311 top = index - descr->page_size * (cols - 1);
1313 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1315 INT height = fully ? descr->items[index].height : 1;
1316 for (top = index; top > descr->top_item; top--)
1317 if ((height += descr->items[top-1].height) > descr->height) break;
1321 if (index < descr->top_item + descr->page_size) return;
1322 if (!fully && (index == descr->top_item + descr->page_size) &&
1323 (descr->height > (descr->page_size * descr->item_height))) return;
1324 top = index - descr->page_size + 1;
1326 LISTBOX_SetTopItem( descr, top, TRUE );
1329 /***********************************************************************
1330 * LISTBOX_SetCaretIndex
1333 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1336 static LRESULT LISTBOX_SetCaretIndex( LB_DESCR *descr, INT index, BOOL fully_visible )
1338 INT oldfocus = descr->focus_item;
1340 if (descr->style & LBS_NOSEL) return LB_ERR;
1341 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1342 if (index == oldfocus) return LB_OKAY;
1343 descr->focus_item = index;
1344 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1345 LISTBOX_RepaintItem( descr, oldfocus, ODA_FOCUS );
1347 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1348 if (descr->caret_on && (descr->in_focus))
1349 LISTBOX_RepaintItem( descr, index, ODA_FOCUS );
1355 /***********************************************************************
1356 * LISTBOX_SelectItemRange
1358 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1360 static LRESULT LISTBOX_SelectItemRange( LB_DESCR *descr, INT first,
1365 /* A few sanity checks */
1367 if (descr->style & LBS_NOSEL) return LB_ERR;
1368 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1369 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1370 if (last == -1) last = descr->nb_items - 1;
1371 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1372 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1373 /* selected_item reflects last selected/unselected item on multiple sel */
1374 descr->selected_item = last;
1376 if (on) /* Turn selection on */
1378 for (i = first; i <= last; i++)
1380 if (descr->items[i].selected) continue;
1381 descr->items[i].selected = TRUE;
1382 LISTBOX_InvalidateItemRect(descr, i);
1384 LISTBOX_SetCaretIndex( descr, last, TRUE );
1386 else /* Turn selection off */
1388 for (i = first; i <= last; i++)
1390 if (!descr->items[i].selected) continue;
1391 descr->items[i].selected = FALSE;
1392 LISTBOX_InvalidateItemRect(descr, i);
1398 /***********************************************************************
1399 * LISTBOX_SetSelection
1401 static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index,
1402 BOOL on, BOOL send_notify )
1404 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1406 if (descr->style & LBS_NOSEL)
1408 descr->selected_item = index;
1411 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1412 if (descr->style & LBS_MULTIPLESEL)
1414 if (index == -1) /* Select all items */
1415 return LISTBOX_SelectItemRange( descr, 0, -1, on );
1416 else /* Only one item */
1417 return LISTBOX_SelectItemRange( descr, index, index, on );
1421 INT oldsel = descr->selected_item;
1422 if (index == oldsel) return LB_OKAY;
1423 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1424 if (index != -1) descr->items[index].selected = TRUE;
1425 descr->selected_item = index;
1426 if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT );
1427 if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT );
1428 if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr,
1429 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1431 if( descr->lphc ) /* set selection change flag for parent combo */
1432 descr->lphc->wState |= CBF_SELCHANGE;
1438 /***********************************************************************
1441 * Change the caret position and extend the selection to the new caret.
1443 static void LISTBOX_MoveCaret( LB_DESCR *descr, INT index, BOOL fully_visible )
1445 INT oldfocus = descr->focus_item;
1447 if ((index < 0) || (index >= descr->nb_items))
1450 /* Important, repaint needs to be done in this order if
1451 you want to mimic Windows behavior:
1452 1. Remove the focus and paint the item
1453 2. Remove the selection and paint the item(s)
1454 3. Set the selection and repaint the item(s)
1455 4. Set the focus to 'index' and repaint the item */
1457 /* 1. remove the focus and repaint the item */
1458 descr->focus_item = -1;
1459 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1460 LISTBOX_RepaintItem( descr, oldfocus, ODA_FOCUS );
1462 /* 2. then turn off the previous selection */
1463 /* 3. repaint the new selected item */
1464 if (descr->style & LBS_EXTENDEDSEL)
1466 if (descr->anchor_item != -1)
1468 INT first = min( index, descr->anchor_item );
1469 INT last = max( index, descr->anchor_item );
1471 LISTBOX_SelectItemRange( descr, 0, first - 1, FALSE );
1472 LISTBOX_SelectItemRange( descr, last + 1, -1, FALSE );
1473 LISTBOX_SelectItemRange( descr, first, last, TRUE );
1476 else if (!(descr->style & LBS_MULTIPLESEL))
1478 /* Set selection to new caret item */
1479 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
1482 /* 4. repaint the new item with the focus */
1483 descr->focus_item = index;
1484 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1485 if (descr->caret_on && (descr->in_focus))
1486 LISTBOX_RepaintItem( descr, index, ODA_FOCUS );
1490 /***********************************************************************
1491 * LISTBOX_InsertItem
1493 static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index,
1494 LPWSTR str, DWORD data )
1498 INT oldfocus = descr->focus_item;
1500 if (index == -1) index = descr->nb_items;
1501 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1502 if (!descr->items) max_items = 0;
1503 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1504 if (descr->nb_items == max_items)
1506 /* We need to grow the array */
1507 max_items += LB_ARRAY_GRANULARITY;
1509 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1510 max_items * sizeof(LB_ITEMDATA) );
1512 item = HeapAlloc( GetProcessHeap(), 0,
1513 max_items * sizeof(LB_ITEMDATA) );
1516 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1519 descr->items = item;
1522 /* Insert the item structure */
1524 item = &descr->items[index];
1525 if (index < descr->nb_items)
1526 RtlMoveMemory( item + 1, item,
1527 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1531 item->selected = FALSE;
1534 /* Get item height */
1536 if (descr->style & LBS_OWNERDRAWVARIABLE)
1538 MEASUREITEMSTRUCT mis;
1539 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1541 mis.CtlType = ODT_LISTBOX;
1544 mis.itemData = descr->items[index].data;
1545 mis.itemHeight = descr->item_height;
1546 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1547 item->height = mis.itemHeight ? mis.itemHeight : 1;
1548 TRACE("[%p]: measure item %d (%s) = %d\n",
1549 descr->self, index, str ? debugstr_w(str) : "", item->height );
1552 /* Repaint the items */
1554 LISTBOX_UpdateScroll( descr );
1555 LISTBOX_InvalidateItems( descr, index );
1557 /* Move selection and focused item */
1558 /* If listbox was empty, set focus to the first item */
1559 if (descr->nb_items == 1)
1560 LISTBOX_SetCaretIndex( descr, 0, FALSE );
1561 /* single select don't change selection index in win31 */
1562 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1564 descr->selected_item++;
1565 LISTBOX_SetSelection( descr, descr->selected_item-1, TRUE, FALSE );
1569 if (index <= descr->selected_item)
1571 descr->selected_item++;
1572 descr->focus_item = oldfocus; /* focus not changed */
1579 /***********************************************************************
1580 * LISTBOX_InsertString
1582 static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index, LPCWSTR str )
1584 LPWSTR new_str = NULL;
1588 if (HAS_STRINGS(descr))
1590 static const WCHAR empty_stringW[] = { 0 };
1591 if (!str) str = empty_stringW;
1592 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1594 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1597 strcpyW(new_str, str);
1599 else data = (DWORD)str;
1601 if (index == -1) index = descr->nb_items;
1602 if ((ret = LISTBOX_InsertItem( descr, index, new_str, data )) != 0)
1604 if (new_str) HeapFree( GetProcessHeap(), 0, new_str );
1608 TRACE("[%p]: added item %d %s\n",
1609 descr->self, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1614 /***********************************************************************
1615 * LISTBOX_DeleteItem
1617 * Delete the content of an item. 'index' must be a valid index.
1619 static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index )
1621 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1622 * while Win95 sends it for all items with user data.
1623 * It's probably better to send it too often than not
1624 * often enough, so this is what we do here.
1626 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1628 DELETEITEMSTRUCT dis;
1629 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1631 dis.CtlType = ODT_LISTBOX;
1634 dis.hwndItem = descr->self;
1635 dis.itemData = descr->items[index].data;
1636 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1638 if (HAS_STRINGS(descr) && descr->items[index].str)
1639 HeapFree( GetProcessHeap(), 0, descr->items[index].str );
1643 /***********************************************************************
1644 * LISTBOX_RemoveItem
1646 * Remove an item from the listbox and delete its content.
1648 static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index )
1653 if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1654 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1656 /* We need to invalidate the original rect instead of the updated one. */
1657 LISTBOX_InvalidateItems( descr, index );
1659 LISTBOX_DeleteItem( descr, index );
1661 /* Remove the item */
1663 item = &descr->items[index];
1664 if (index < descr->nb_items-1)
1665 RtlMoveMemory( item, item + 1,
1666 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1668 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1670 /* Shrink the item array if possible */
1672 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1673 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1675 max_items -= LB_ARRAY_GRANULARITY;
1676 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1677 max_items * sizeof(LB_ITEMDATA) );
1678 if (item) descr->items = item;
1680 /* Repaint the items */
1682 LISTBOX_UpdateScroll( descr );
1683 /* if we removed the scrollbar, reset the top of the list
1684 (correct for owner-drawn ???) */
1685 if (descr->nb_items == descr->page_size)
1686 LISTBOX_SetTopItem( descr, 0, TRUE );
1688 /* Move selection and focused item */
1689 if (!IS_MULTISELECT(descr))
1691 if (index == descr->selected_item)
1692 descr->selected_item = -1;
1693 else if (index < descr->selected_item)
1695 descr->selected_item--;
1696 if (ISWIN31) /* win 31 do not change the selected item number */
1697 LISTBOX_SetSelection( descr, descr->selected_item + 1, TRUE, FALSE);
1701 if (descr->focus_item >= descr->nb_items)
1703 descr->focus_item = descr->nb_items - 1;
1704 if (descr->focus_item < 0) descr->focus_item = 0;
1710 /***********************************************************************
1711 * LISTBOX_ResetContent
1713 static void LISTBOX_ResetContent( LB_DESCR *descr )
1717 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( descr, i );
1718 if (descr->items) HeapFree( GetProcessHeap(), 0, descr->items );
1719 descr->nb_items = 0;
1720 descr->top_item = 0;
1721 descr->selected_item = -1;
1722 descr->focus_item = 0;
1723 descr->anchor_item = -1;
1724 descr->items = NULL;
1728 /***********************************************************************
1731 static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count )
1735 if (HAS_STRINGS(descr)) return LB_ERR;
1736 /* FIXME: this is far from optimal... */
1737 if (count > descr->nb_items)
1739 while (count > descr->nb_items)
1740 if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0)
1743 else if (count < descr->nb_items)
1745 while (count < descr->nb_items)
1746 if ((ret = LISTBOX_RemoveItem( descr, -1 )) < 0)
1753 /***********************************************************************
1756 static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib,
1757 LPCWSTR filespec, BOOL long_names )
1760 LRESULT ret = LB_OKAY;
1761 WIN32_FIND_DATAW entry;
1764 /* don't scan directory if we just want drives exclusively */
1765 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1766 /* scan directory */
1767 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1769 int le = GetLastError();
1770 if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR;
1777 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1779 static const WCHAR bracketW[] = { ']',0 };
1780 static const WCHAR dotW[] = { '.',0 };
1781 if (!(attrib & DDL_DIRECTORY) ||
1782 !strcmpW( entry.cFileName, dotW )) continue;
1784 if (!long_names && entry.cAlternateFileName[0])
1785 strcpyW( buffer + 1, entry.cAlternateFileName );
1787 strcpyW( buffer + 1, entry.cFileName );
1788 strcatW(buffer, bracketW);
1790 else /* not a directory */
1792 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1793 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1795 if ((attrib & DDL_EXCLUSIVE) &&
1796 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1799 if (!long_names && entry.cAlternateFileName[0])
1800 strcpyW( buffer, entry.cAlternateFileName );
1802 strcpyW( buffer, entry.cFileName );
1804 if (!long_names) CharLowerW( buffer );
1805 pos = LISTBOX_FindFileStrPos( descr, buffer );
1806 if ((ret = LISTBOX_InsertString( descr, pos, buffer )) < 0)
1808 } while (FindNextFileW( handle, &entry ));
1809 FindClose( handle );
1814 if ((ret >= 0) && (attrib & DDL_DRIVES))
1816 WCHAR buffer[] = {'[','-','a','-',']',0};
1817 WCHAR root[] = {'A',':','\\',0};
1819 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1821 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1822 if ((ret = LISTBOX_InsertString( descr, -1, buffer )) < 0)
1830 /***********************************************************************
1831 * LISTBOX_HandleVScroll
1833 static LRESULT LISTBOX_HandleVScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1837 if (descr->style & LBS_MULTICOLUMN) return 0;
1841 LISTBOX_SetTopItem( descr, descr->top_item - 1, TRUE );
1844 LISTBOX_SetTopItem( descr, descr->top_item + 1, TRUE );
1847 LISTBOX_SetTopItem( descr, descr->top_item -
1848 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1851 LISTBOX_SetTopItem( descr, descr->top_item +
1852 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1854 case SB_THUMBPOSITION:
1855 LISTBOX_SetTopItem( descr, pos, TRUE );
1858 info.cbSize = sizeof(info);
1859 info.fMask = SIF_TRACKPOS;
1860 GetScrollInfo( descr->self, SB_VERT, &info );
1861 LISTBOX_SetTopItem( descr, info.nTrackPos, TRUE );
1864 LISTBOX_SetTopItem( descr, 0, TRUE );
1867 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1874 /***********************************************************************
1875 * LISTBOX_HandleHScroll
1877 static LRESULT LISTBOX_HandleHScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1882 if (descr->style & LBS_MULTICOLUMN)
1887 LISTBOX_SetTopItem( descr, descr->top_item-descr->page_size,
1891 LISTBOX_SetTopItem( descr, descr->top_item+descr->page_size,
1895 page = descr->width / descr->column_width;
1896 if (page < 1) page = 1;
1897 LISTBOX_SetTopItem( descr,
1898 descr->top_item - page * descr->page_size, TRUE );
1901 page = descr->width / descr->column_width;
1902 if (page < 1) page = 1;
1903 LISTBOX_SetTopItem( descr,
1904 descr->top_item + page * descr->page_size, TRUE );
1906 case SB_THUMBPOSITION:
1907 LISTBOX_SetTopItem( descr, pos*descr->page_size, TRUE );
1910 info.cbSize = sizeof(info);
1911 info.fMask = SIF_TRACKPOS;
1912 GetScrollInfo( descr->self, SB_VERT, &info );
1913 LISTBOX_SetTopItem( descr, info.nTrackPos*descr->page_size,
1917 LISTBOX_SetTopItem( descr, 0, TRUE );
1920 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1924 else if (descr->horz_extent)
1929 LISTBOX_SetHorizontalPos( descr, descr->horz_pos - 1 );
1932 LISTBOX_SetHorizontalPos( descr, descr->horz_pos + 1 );
1935 LISTBOX_SetHorizontalPos( descr,
1936 descr->horz_pos - descr->width );
1939 LISTBOX_SetHorizontalPos( descr,
1940 descr->horz_pos + descr->width );
1942 case SB_THUMBPOSITION:
1943 LISTBOX_SetHorizontalPos( descr, pos );
1946 info.cbSize = sizeof(info);
1947 info.fMask = SIF_TRACKPOS;
1948 GetScrollInfo( descr->self, SB_HORZ, &info );
1949 LISTBOX_SetHorizontalPos( descr, info.nTrackPos );
1952 LISTBOX_SetHorizontalPos( descr, 0 );
1955 LISTBOX_SetHorizontalPos( descr,
1956 descr->horz_extent - descr->width );
1963 static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta )
1965 short gcWheelDelta = 0;
1966 UINT pulScrollLines = 3;
1968 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1970 gcWheelDelta -= delta;
1972 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1974 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1975 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1976 LISTBOX_SetTopItem( descr, descr->top_item + cLineScroll, TRUE );
1981 /***********************************************************************
1982 * LISTBOX_HandleLButtonDown
1984 static LRESULT LISTBOX_HandleLButtonDown( LB_DESCR *descr, DWORD keys, INT x, INT y )
1986 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
1987 TRACE("[%p]: lbuttondown %d,%d item %d\n", descr->self, x, y, index );
1988 if (!descr->caret_on && (descr->in_focus)) return 0;
1990 if (!descr->in_focus)
1992 if( !descr->lphc ) SetFocus( descr->self );
1993 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
1996 if (index == -1) return 0;
1998 if (descr->style & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL))
2000 /* we should perhaps make sure that all items are deselected
2001 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2002 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2003 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2006 if (!(keys & MK_SHIFT)) descr->anchor_item = index;
2007 if (keys & MK_CONTROL)
2009 LISTBOX_SetCaretIndex( descr, index, FALSE );
2010 LISTBOX_SetSelection( descr, index,
2011 !descr->items[index].selected,
2012 (descr->style & LBS_NOTIFY) != 0);
2016 LISTBOX_MoveCaret( descr, index, FALSE );
2018 if (descr->style & LBS_MULTIPLESEL)
2020 LISTBOX_SetSelection( descr, index,
2021 (!(descr->style & LBS_MULTIPLESEL) ||
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 );