4 * Copyright 1996 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 * This code was audited for completeness against the documented features
23 * of Comctl32.dll version 6.0 on Oct. 9, 2004, by Dimitrie O. Paun.
25 * Unless otherwise noted, we believe this code to be complete, as per
26 * the specification mentioned above.
27 * If you discover missing features, or bugs, please note them below.
42 #include "wine/winuser16.h"
43 #include "wine/unicode.h"
44 #include "user_private.h"
46 #include "wine/exception.h"
47 #include "wine/debug.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(listbox);
51 /* Items array granularity */
52 #define LB_ARRAY_GRANULARITY 16
54 /* Scrolling timeout in ms */
55 #define LB_SCROLL_TIMEOUT 50
57 /* Listbox system timer id */
60 /* flag listbox changed while setredraw false - internal style */
61 #define LBS_DISPLAYCHANGED 0x80000000
66 LPWSTR str; /* Item text */
67 BOOL selected; /* Is item selected? */
68 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
69 ULONG_PTR data; /* User data */
72 /* Listbox structure */
75 HWND self; /* Our own window handle */
76 HWND owner; /* Owner window to send notifications to */
77 UINT style; /* Window style */
78 INT width; /* Window width */
79 INT height; /* Window height */
80 LB_ITEMDATA *items; /* Array of items */
81 INT nb_items; /* Number of items */
82 INT top_item; /* Top visible item */
83 INT selected_item; /* Selected item */
84 INT focus_item; /* Item that has the focus */
85 INT anchor_item; /* Anchor item for extended selection */
86 INT item_height; /* Default item height */
87 INT page_size; /* Items per listbox page */
88 INT column_width; /* Column width for multi-column listboxes */
89 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
90 INT horz_pos; /* Horizontal position */
91 INT nb_tabs; /* Number of tabs in array */
92 INT *tabs; /* Array of tabs */
93 INT avg_char_width; /* Average width of characters */
94 BOOL caret_on; /* Is caret on? */
95 BOOL captured; /* Is mouse captured? */
97 HFONT font; /* Current font */
98 LCID locale; /* Current locale for string comparisons */
99 LPHEADCOMBO lphc; /* ComboLBox */
103 #define IS_OWNERDRAW(descr) \
104 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
106 #define HAS_STRINGS(descr) \
107 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
110 #define IS_MULTISELECT(descr) \
111 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
112 !((descr)->style & LBS_NOSEL))
114 #define SEND_NOTIFICATION(descr,code) \
115 (SendMessageW( (descr)->owner, WM_COMMAND, \
116 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
118 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
120 /* Current timer status */
130 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
132 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
133 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
135 static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect );
137 /*********************************************************************
138 * listbox class descriptor
140 static const WCHAR listboxW[] = {'L','i','s','t','B','o','x',0};
141 const struct builtin_class_descr LISTBOX_builtin_class =
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 static const WCHAR combolboxW[] = {'C','o','m','b','o','L','B','o','x',0};
157 const struct builtin_class_descr COMBOLBOX_builtin_class =
159 combolboxW, /* name */
160 CS_DBLCLKS | CS_SAVEBITS, /* style */
161 ListBoxWndProcA, /* procA */
162 ListBoxWndProcW, /* procW */
163 sizeof(LB_DESCR *), /* extra */
164 IDC_ARROW, /* cursor */
169 /* check whether app is a Win 3.1 app */
170 static inline BOOL is_old_app( LB_DESCR *descr )
172 return (GetExpWinVer16( GetWindowLongPtrW(descr->self, GWLP_HINSTANCE) ) & 0xFF00 ) == 0x0300;
176 /***********************************************************************
177 * LISTBOX_GetCurrentPageSize
179 * Return the current page size
181 static INT LISTBOX_GetCurrentPageSize( const LB_DESCR *descr )
184 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
185 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
187 if ((height += descr->items[i].height) > descr->height) break;
189 if (i == descr->top_item) return 1;
190 else return i - descr->top_item;
194 /***********************************************************************
195 * LISTBOX_GetMaxTopIndex
197 * Return the maximum possible index for the top of the listbox.
199 static INT LISTBOX_GetMaxTopIndex( const LB_DESCR *descr )
203 if (descr->style & LBS_OWNERDRAWVARIABLE)
205 page = descr->height;
206 for (max = descr->nb_items - 1; max >= 0; max--)
207 if ((page -= descr->items[max].height) < 0) break;
208 if (max < descr->nb_items - 1) max++;
210 else if (descr->style & LBS_MULTICOLUMN)
212 if ((page = descr->width / descr->column_width) < 1) page = 1;
213 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
214 max = (max - page) * descr->page_size;
218 max = descr->nb_items - descr->page_size;
220 if (max < 0) max = 0;
225 /***********************************************************************
226 * LISTBOX_UpdateScroll
228 * Update the scrollbars. Should be called whenever the content
229 * of the listbox changes.
231 static void LISTBOX_UpdateScroll( LB_DESCR *descr )
235 /* Check the listbox scroll bar flags individually before we call
236 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
237 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
238 scroll bar when we do not need one.
239 if (!(descr->style & WS_VSCROLL)) return;
242 /* It is important that we check descr->style, and not wnd->dwStyle,
243 for WS_VSCROLL, as the former is exactly the one passed in
244 argument to CreateWindow.
245 In Windows (and from now on in Wine :) a listbox created
246 with such a style (no WS_SCROLL) does not update
247 the scrollbar with listbox-related data, thus letting
248 the programmer use it for his/her own purposes. */
250 if (descr->style & LBS_NOREDRAW) return;
251 info.cbSize = sizeof(info);
253 if (descr->style & LBS_MULTICOLUMN)
256 info.nMax = (descr->nb_items - 1) / descr->page_size;
257 info.nPos = descr->top_item / descr->page_size;
258 info.nPage = descr->width / descr->column_width;
259 if (info.nPage < 1) info.nPage = 1;
260 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
261 if (descr->style & LBS_DISABLENOSCROLL)
262 info.fMask |= SIF_DISABLENOSCROLL;
263 if (descr->style & WS_HSCROLL)
264 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
266 info.fMask = SIF_RANGE;
267 if (descr->style & WS_VSCROLL)
268 SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
273 info.nMax = descr->nb_items - 1;
274 info.nPos = descr->top_item;
275 info.nPage = LISTBOX_GetCurrentPageSize( descr );
276 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
277 if (descr->style & LBS_DISABLENOSCROLL)
278 info.fMask |= SIF_DISABLENOSCROLL;
279 if (descr->style & WS_VSCROLL)
280 SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
282 if (descr->horz_extent)
285 info.nMax = descr->horz_extent - 1;
286 info.nPos = descr->horz_pos;
287 info.nPage = descr->width;
288 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
289 if (descr->style & LBS_DISABLENOSCROLL)
290 info.fMask |= SIF_DISABLENOSCROLL;
291 if (descr->style & WS_HSCROLL)
292 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
298 /***********************************************************************
301 * Set the top item of the listbox, scrolling up or down if necessary.
303 static LRESULT LISTBOX_SetTopItem( LB_DESCR *descr, INT index, BOOL scroll )
305 INT max = LISTBOX_GetMaxTopIndex( descr );
307 TRACE("setting top item %d, scroll %d\n", index, scroll);
309 if (index > max) index = max;
310 if (index < 0) index = 0;
311 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
312 if (descr->top_item == index) return LB_OKAY;
313 if (descr->style & LBS_MULTICOLUMN)
315 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
316 if (scroll && (abs(diff) < descr->width))
317 ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
318 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
326 if (descr->style & LBS_OWNERDRAWVARIABLE)
330 if (index > descr->top_item)
332 for (i = index - 1; i >= descr->top_item; i--)
333 diff -= descr->items[i].height;
337 for (i = index; i < descr->top_item; i++)
338 diff += descr->items[i].height;
342 diff = (descr->top_item - index) * descr->item_height;
344 if (abs(diff) < descr->height)
345 ScrollWindowEx( descr->self, 0, diff, NULL, NULL, 0, NULL,
346 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
350 if (!scroll) InvalidateRect( descr->self, NULL, TRUE );
351 descr->top_item = index;
352 LISTBOX_UpdateScroll( descr );
357 /***********************************************************************
360 * Update the page size. Should be called when the size of
361 * the client area or the item height changes.
363 static void LISTBOX_UpdatePage( LB_DESCR *descr )
367 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
369 if (page_size == descr->page_size) return;
370 descr->page_size = page_size;
371 if (descr->style & LBS_MULTICOLUMN)
372 InvalidateRect( descr->self, NULL, TRUE );
373 LISTBOX_SetTopItem( descr, descr->top_item, FALSE );
377 /***********************************************************************
380 * Update the size of the listbox. Should be called when the size of
381 * the client area changes.
383 static void LISTBOX_UpdateSize( LB_DESCR *descr )
387 GetClientRect( descr->self, &rect );
388 descr->width = rect.right - rect.left;
389 descr->height = rect.bottom - rect.top;
390 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
395 GetWindowRect( descr->self, &rect );
396 if(descr->item_height != 0)
397 remaining = descr->height % descr->item_height;
400 if ((descr->height > descr->item_height) && remaining)
402 if (is_old_app(descr))
403 { /* give a margin for error to 16 bits programs - if we need
404 less than the height of the nonclient area, round to the
405 *next* number of items */
406 int ncheight = rect.bottom - rect.top - descr->height;
407 if ((descr->item_height - remaining) <= ncheight)
408 remaining = remaining - descr->item_height;
410 TRACE("[%p]: changing height %d -> %d\n",
411 descr->self, descr->height, descr->height - remaining );
412 SetWindowPos( descr->self, 0, 0, 0, rect.right - rect.left,
413 rect.bottom - rect.top - remaining,
414 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
418 TRACE("[%p]: new size = %d,%d\n", descr->self, descr->width, descr->height );
419 LISTBOX_UpdatePage( descr );
420 LISTBOX_UpdateScroll( descr );
422 /* Invalidate the focused item so it will be repainted correctly */
423 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
425 InvalidateRect( descr->self, &rect, FALSE );
430 /***********************************************************************
431 * LISTBOX_GetItemRect
433 * Get the rectangle enclosing an item, in listbox client coordinates.
434 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
436 static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect )
438 /* Index <= 0 is legal even on empty listboxes */
439 if (index && (index >= descr->nb_items))
441 memset(rect, 0, sizeof(*rect));
442 SetLastError(ERROR_INVALID_INDEX);
445 SetRect( rect, 0, 0, descr->width, descr->height );
446 if (descr->style & LBS_MULTICOLUMN)
448 INT col = (index / descr->page_size) -
449 (descr->top_item / descr->page_size);
450 rect->left += col * descr->column_width;
451 rect->right = rect->left + descr->column_width;
452 rect->top += (index % descr->page_size) * descr->item_height;
453 rect->bottom = rect->top + descr->item_height;
455 else if (descr->style & LBS_OWNERDRAWVARIABLE)
458 rect->right += descr->horz_pos;
459 if ((index >= 0) && (index < descr->nb_items))
461 if (index < descr->top_item)
463 for (i = descr->top_item-1; i >= index; i--)
464 rect->top -= descr->items[i].height;
468 for (i = descr->top_item; i < index; i++)
469 rect->top += descr->items[i].height;
471 rect->bottom = rect->top + descr->items[index].height;
477 rect->top += (index - descr->top_item) * descr->item_height;
478 rect->bottom = rect->top + descr->item_height;
479 rect->right += descr->horz_pos;
482 TRACE("item %d, rect %s\n", index, wine_dbgstr_rect(rect));
484 return ((rect->left < descr->width) && (rect->right > 0) &&
485 (rect->top < descr->height) && (rect->bottom > 0));
489 /***********************************************************************
490 * LISTBOX_GetItemFromPoint
492 * Return the item nearest from point (x,y) (in client coordinates).
494 static INT LISTBOX_GetItemFromPoint( const LB_DESCR *descr, INT x, INT y )
496 INT index = descr->top_item;
498 if (!descr->nb_items) return -1; /* No items */
499 if (descr->style & LBS_OWNERDRAWVARIABLE)
504 while (index < descr->nb_items)
506 if ((pos += descr->items[index].height) > y) break;
515 if ((pos -= descr->items[index].height) <= y) break;
519 else if (descr->style & LBS_MULTICOLUMN)
521 if (y >= descr->item_height * descr->page_size) return -1;
522 if (y >= 0) index += y / descr->item_height;
523 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
524 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
528 index += (y / descr->item_height);
530 if (index < 0) return 0;
531 if (index >= descr->nb_items) return -1;
536 /***********************************************************************
541 static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect,
542 INT index, UINT action, BOOL ignoreFocus )
544 LB_ITEMDATA *item = NULL;
545 if (index < descr->nb_items) item = &descr->items[index];
547 if (IS_OWNERDRAW(descr))
555 if (action == ODA_FOCUS)
556 DrawFocusRect( hdc, rect );
558 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
562 /* some programs mess with the clipping region when
563 drawing the item, *and* restore the previous region
564 after they are done, so a region has better to exist
565 else everything ends clipped */
566 GetClientRect(descr->self, &r);
567 hrgn = CreateRectRgnIndirect(&r);
568 SelectClipRgn( hdc, hrgn);
569 DeleteObject( hrgn );
571 dis.CtlType = ODT_LISTBOX;
572 dis.CtlID = GetWindowLongPtrW( descr->self, GWLP_ID );
573 dis.hwndItem = descr->self;
574 dis.itemAction = action;
578 if (item->selected) dis.itemState |= ODS_SELECTED;
579 if (!ignoreFocus && (descr->focus_item == index) &&
581 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
582 if (!IsWindowEnabled(descr->self)) dis.itemState |= ODS_DISABLED;
583 dis.itemData = item->data;
585 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
586 descr->self, index, item ? debugstr_w(item->str) : "", action,
587 dis.itemState, wine_dbgstr_rect(rect) );
588 SendMessageW(descr->owner, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
592 COLORREF oldText = 0, oldBk = 0;
594 if (action == ODA_FOCUS)
596 DrawFocusRect( hdc, rect );
599 if (item && item->selected)
601 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
602 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
605 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
606 descr->self, index, item ? debugstr_w(item->str) : "", action,
607 wine_dbgstr_rect(rect) );
609 ExtTextOutW( hdc, rect->left + 1, rect->top,
610 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
611 else if (!(descr->style & LBS_USETABSTOPS))
612 ExtTextOutW( hdc, rect->left + 1, rect->top,
613 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
614 strlenW(item->str), NULL );
617 /* Output empty string to paint background in the full width. */
618 ExtTextOutW( hdc, rect->left + 1, rect->top,
619 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
620 TabbedTextOutW( hdc, rect->left + 1 , rect->top,
621 item->str, strlenW(item->str),
622 descr->nb_tabs, descr->tabs, 0);
624 if (item && item->selected)
626 SetBkColor( hdc, oldBk );
627 SetTextColor( hdc, oldText );
629 if (!ignoreFocus && (descr->focus_item == index) &&
631 (descr->in_focus)) DrawFocusRect( hdc, rect );
636 /***********************************************************************
639 * Change the redraw flag.
641 static void LISTBOX_SetRedraw( LB_DESCR *descr, BOOL on )
645 if (!(descr->style & LBS_NOREDRAW)) return;
646 descr->style &= ~LBS_NOREDRAW;
647 if (descr->style & LBS_DISPLAYCHANGED)
648 { /* page was changed while setredraw false, refresh automatically */
649 InvalidateRect(descr->self, NULL, TRUE);
650 if ((descr->top_item + descr->page_size) > descr->nb_items)
651 { /* reset top of page if less than number of items/page */
652 descr->top_item = descr->nb_items - descr->page_size;
653 if (descr->top_item < 0) descr->top_item = 0;
655 descr->style &= ~LBS_DISPLAYCHANGED;
657 LISTBOX_UpdateScroll( descr );
659 else descr->style |= LBS_NOREDRAW;
663 /***********************************************************************
664 * LISTBOX_RepaintItem
666 * Repaint a single item synchronously.
668 static void LISTBOX_RepaintItem( LB_DESCR *descr, INT index, UINT action )
673 HBRUSH hbrush, oldBrush = 0;
675 /* Do not repaint the item if the item is not visible */
676 if (!IsWindowVisible(descr->self)) return;
677 if (descr->style & LBS_NOREDRAW)
679 descr->style |= LBS_DISPLAYCHANGED;
682 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
683 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
684 if (descr->font) oldFont = SelectObject( hdc, descr->font );
685 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
686 (WPARAM)hdc, (LPARAM)descr->self );
687 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
688 if (!IsWindowEnabled(descr->self))
689 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
690 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
691 LISTBOX_PaintItem( descr, hdc, &rect, index, action, TRUE );
692 if (oldFont) SelectObject( hdc, oldFont );
693 if (oldBrush) SelectObject( hdc, oldBrush );
694 ReleaseDC( descr->self, hdc );
698 /***********************************************************************
699 * LISTBOX_DrawFocusRect
701 static void LISTBOX_DrawFocusRect( LB_DESCR *descr, BOOL on )
707 /* Do not repaint the item if the item is not visible */
708 if (!IsWindowVisible(descr->self)) return;
710 if (descr->focus_item == -1) return;
711 if (!descr->caret_on || !descr->in_focus) return;
713 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) != 1) return;
714 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
715 if (descr->font) oldFont = SelectObject( hdc, descr->font );
716 if (!IsWindowEnabled(descr->self))
717 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
718 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
719 LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, on ? FALSE : TRUE );
720 if (oldFont) SelectObject( hdc, oldFont );
721 ReleaseDC( descr->self, hdc );
725 /***********************************************************************
726 * LISTBOX_InitStorage
728 static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items )
732 nb_items += LB_ARRAY_GRANULARITY - 1;
733 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
735 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
736 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
737 nb_items * sizeof(LB_ITEMDATA));
740 item = HeapAlloc( GetProcessHeap(), 0,
741 nb_items * sizeof(LB_ITEMDATA));
746 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
754 /***********************************************************************
755 * LISTBOX_SetTabStops
757 static BOOL LISTBOX_SetTabStops( LB_DESCR *descr, INT count, LPINT tabs )
761 if (!(descr->style & LBS_USETABSTOPS))
763 SetLastError(ERROR_LB_WITHOUT_TABSTOPS);
767 HeapFree( GetProcessHeap(), 0, descr->tabs );
768 if (!(descr->nb_tabs = count))
773 if (!(descr->tabs = HeapAlloc( GetProcessHeap(), 0,
774 descr->nb_tabs * sizeof(INT) )))
776 memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
778 /* convert into "dialog units"*/
779 for (i = 0; i < descr->nb_tabs; i++)
780 descr->tabs[i] = MulDiv(descr->tabs[i], descr->avg_char_width, 4);
786 /***********************************************************************
789 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPWSTR buffer, BOOL unicode )
793 if ((index < 0) || (index >= descr->nb_items))
795 SetLastError(ERROR_INVALID_INDEX);
798 if (HAS_STRINGS(descr))
802 len = strlenW(descr->items[index].str);
805 return WideCharToMultiByte( CP_ACP, 0, descr->items[index].str, len,
806 NULL, 0, NULL, NULL );
809 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
811 __TRY /* hide a Delphi bug that passes a read-only buffer */
815 strcpyW( buffer, descr->items[index].str );
816 len = strlenW(buffer);
820 len = WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1,
821 (LPSTR)buffer, 0x7FFFFFFF, NULL, NULL) - 1;
826 WARN( "got an invalid buffer (Delphi bug?)\n" );
827 SetLastError( ERROR_INVALID_PARAMETER );
833 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
839 static inline INT LISTBOX_lstrcmpiW( LCID lcid, LPCWSTR str1, LPCWSTR str2 )
841 INT ret = CompareStringW( lcid, NORM_IGNORECASE, str1, -1, str2, -1 );
842 if (ret == CSTR_LESS_THAN)
844 if (ret == CSTR_EQUAL)
846 if (ret == CSTR_GREATER_THAN)
851 /***********************************************************************
852 * LISTBOX_FindStringPos
854 * Find the nearest string located before a given string in sort order.
855 * If 'exact' is TRUE, return an error if we don't get an exact match.
857 static INT LISTBOX_FindStringPos( LB_DESCR *descr, LPCWSTR str, BOOL exact )
859 INT index, min, max, res = -1;
861 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
863 max = descr->nb_items;
866 index = (min + max) / 2;
867 if (HAS_STRINGS(descr))
868 res = LISTBOX_lstrcmpiW( descr->locale, str, descr->items[index].str);
871 COMPAREITEMSTRUCT cis;
872 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
874 cis.CtlType = ODT_LISTBOX;
876 cis.hwndItem = descr->self;
877 /* note that some application (MetaStock) expects the second item
878 * to be in the listbox */
880 cis.itemData1 = (ULONG_PTR)str;
882 cis.itemData2 = descr->items[index].data;
883 cis.dwLocaleId = descr->locale;
884 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
886 if (!res) return index;
887 if (res < 0) max = index;
888 else min = index + 1;
890 return exact ? -1 : max;
894 /***********************************************************************
895 * LISTBOX_FindFileStrPos
897 * Find the nearest string located before a given string in directory
898 * sort order (i.e. first files, then directories, then drives).
900 static INT LISTBOX_FindFileStrPos( LB_DESCR *descr, LPCWSTR str )
902 INT min, max, res = -1;
904 if (!HAS_STRINGS(descr))
905 return LISTBOX_FindStringPos( descr, str, FALSE );
907 max = descr->nb_items;
910 INT index = (min + max) / 2;
911 LPCWSTR p = descr->items[index].str;
912 if (*p == '[') /* drive or directory */
914 if (*str != '[') res = -1;
915 else if (p[1] == '-') /* drive */
917 if (str[1] == '-') res = str[2] - p[2];
922 if (str[1] == '-') res = 1;
923 else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
928 if (*str == '[') res = 1;
929 else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
931 if (!res) return index;
932 if (res < 0) max = index;
933 else min = index + 1;
939 /***********************************************************************
942 * Find the item beginning with a given string.
944 static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exact )
949 if (start >= descr->nb_items) start = -1;
950 item = descr->items + start + 1;
951 if (HAS_STRINGS(descr))
953 if (!str || ! str[0] ) return LB_ERR;
956 for (i = start + 1; i < descr->nb_items; i++, item++)
957 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
958 for (i = 0, item = descr->items; i <= start; i++, item++)
959 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
963 /* Special case for drives and directories: ignore prefix */
964 #define CHECK_DRIVE(item) \
965 if ((item)->str[0] == '[') \
967 if (!strncmpiW( str, (item)->str+1, len )) return i; \
968 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
972 INT len = strlenW(str);
973 for (i = start + 1; i < descr->nb_items; i++, item++)
975 if (!strncmpiW( str, item->str, len )) return i;
978 for (i = 0, item = descr->items; i <= start; i++, item++)
980 if (!strncmpiW( str, item->str, len )) return i;
988 if (exact && (descr->style & LBS_SORT))
989 /* If sorted, use a WM_COMPAREITEM binary search */
990 return LISTBOX_FindStringPos( descr, str, TRUE );
992 /* Otherwise use a linear search */
993 for (i = start + 1; i < descr->nb_items; i++, item++)
994 if (item->data == (ULONG_PTR)str) return i;
995 for (i = 0, item = descr->items; i <= start; i++, item++)
996 if (item->data == (ULONG_PTR)str) return i;
1002 /***********************************************************************
1003 * LISTBOX_GetSelCount
1005 static LRESULT LISTBOX_GetSelCount( const LB_DESCR *descr )
1008 const LB_ITEMDATA *item = descr->items;
1010 if (!(descr->style & LBS_MULTIPLESEL) ||
1011 (descr->style & LBS_NOSEL))
1013 for (i = count = 0; i < descr->nb_items; i++, item++)
1014 if (item->selected) count++;
1019 /***********************************************************************
1020 * LISTBOX_GetSelItems
1022 static LRESULT LISTBOX_GetSelItems( const LB_DESCR *descr, INT max, LPINT array )
1025 const LB_ITEMDATA *item = descr->items;
1027 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1028 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
1029 if (item->selected) array[count++] = i;
1034 /***********************************************************************
1037 static LRESULT LISTBOX_Paint( LB_DESCR *descr, HDC hdc )
1039 INT i, col_pos = descr->page_size - 1;
1041 RECT focusRect = {-1, -1, -1, -1};
1043 HBRUSH hbrush, oldBrush = 0;
1045 if (descr->style & LBS_NOREDRAW) return 0;
1047 SetRect( &rect, 0, 0, descr->width, descr->height );
1048 if (descr->style & LBS_MULTICOLUMN)
1049 rect.right = rect.left + descr->column_width;
1050 else if (descr->horz_pos)
1052 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
1053 rect.right += descr->horz_pos;
1056 if (descr->font) oldFont = SelectObject( hdc, descr->font );
1057 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1058 (WPARAM)hdc, (LPARAM)descr->self );
1059 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1060 if (!IsWindowEnabled(descr->self)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1062 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1065 /* Special case for empty listbox: paint focus rect */
1066 rect.bottom = rect.top + descr->item_height;
1067 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1068 &rect, NULL, 0, NULL );
1069 LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, FALSE );
1070 rect.top = rect.bottom;
1073 /* Paint all the item, regarding the selection
1074 Focus state will be painted after */
1076 for (i = descr->top_item; i < descr->nb_items; i++)
1078 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1079 rect.bottom = rect.top + descr->item_height;
1081 rect.bottom = rect.top + descr->items[i].height;
1083 if (i == descr->focus_item)
1085 /* keep the focus rect, to paint the focus item after */
1086 focusRect.left = rect.left;
1087 focusRect.right = rect.right;
1088 focusRect.top = rect.top;
1089 focusRect.bottom = rect.bottom;
1091 LISTBOX_PaintItem( descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1092 rect.top = rect.bottom;
1094 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1096 if (!IS_OWNERDRAW(descr))
1098 /* Clear the bottom of the column */
1099 if (rect.top < descr->height)
1101 rect.bottom = descr->height;
1102 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1103 &rect, NULL, 0, NULL );
1107 /* Go to the next column */
1108 rect.left += descr->column_width;
1109 rect.right += descr->column_width;
1111 col_pos = descr->page_size - 1;
1116 if (rect.top >= descr->height) break;
1120 /* Paint the focus item now */
1121 if (focusRect.top != focusRect.bottom &&
1122 descr->caret_on && descr->in_focus)
1123 LISTBOX_PaintItem( descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1125 if (!IS_OWNERDRAW(descr))
1127 /* Clear the remainder of the client area */
1128 if (rect.top < descr->height)
1130 rect.bottom = descr->height;
1131 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1132 &rect, NULL, 0, NULL );
1134 if (rect.right < descr->width)
1136 rect.left = rect.right;
1137 rect.right = descr->width;
1139 rect.bottom = descr->height;
1140 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1141 &rect, NULL, 0, NULL );
1144 if (oldFont) SelectObject( hdc, oldFont );
1145 if (oldBrush) SelectObject( hdc, oldBrush );
1150 /***********************************************************************
1151 * LISTBOX_InvalidateItems
1153 * Invalidate all items from a given item. If the specified item is not
1154 * visible, nothing happens.
1156 static void LISTBOX_InvalidateItems( LB_DESCR *descr, INT index )
1160 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1162 if (descr->style & LBS_NOREDRAW)
1164 descr->style |= LBS_DISPLAYCHANGED;
1167 rect.bottom = descr->height;
1168 InvalidateRect( descr->self, &rect, TRUE );
1169 if (descr->style & LBS_MULTICOLUMN)
1171 /* Repaint the other columns */
1172 rect.left = rect.right;
1173 rect.right = descr->width;
1175 InvalidateRect( descr->self, &rect, TRUE );
1180 static void LISTBOX_InvalidateItemRect( LB_DESCR *descr, INT index )
1184 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1185 InvalidateRect( descr->self, &rect, TRUE );
1188 /***********************************************************************
1189 * LISTBOX_GetItemHeight
1191 static LRESULT LISTBOX_GetItemHeight( const LB_DESCR *descr, INT index )
1193 if (descr->style & LBS_OWNERDRAWVARIABLE && descr->nb_items > 0)
1195 if ((index < 0) || (index >= descr->nb_items))
1197 SetLastError(ERROR_INVALID_INDEX);
1200 return descr->items[index].height;
1202 else return descr->item_height;
1206 /***********************************************************************
1207 * LISTBOX_SetItemHeight
1209 static LRESULT LISTBOX_SetItemHeight( LB_DESCR *descr, INT index, INT height, BOOL repaint )
1211 if (height > MAXBYTE)
1214 if (!height) height = 1;
1216 if (descr->style & LBS_OWNERDRAWVARIABLE)
1218 if ((index < 0) || (index >= descr->nb_items))
1220 SetLastError(ERROR_INVALID_INDEX);
1223 TRACE("[%p]: item %d height = %d\n", descr->self, index, height );
1224 descr->items[index].height = height;
1225 LISTBOX_UpdateScroll( descr );
1227 LISTBOX_InvalidateItems( descr, index );
1229 else if (height != descr->item_height)
1231 TRACE("[%p]: new height = %d\n", descr->self, height );
1232 descr->item_height = height;
1233 LISTBOX_UpdatePage( descr );
1234 LISTBOX_UpdateScroll( descr );
1236 InvalidateRect( descr->self, 0, TRUE );
1242 /***********************************************************************
1243 * LISTBOX_SetHorizontalPos
1245 static void LISTBOX_SetHorizontalPos( LB_DESCR *descr, INT pos )
1249 if (pos > descr->horz_extent - descr->width)
1250 pos = descr->horz_extent - descr->width;
1251 if (pos < 0) pos = 0;
1252 if (!(diff = descr->horz_pos - pos)) return;
1253 TRACE("[%p]: new horz pos = %d\n", descr->self, pos );
1254 descr->horz_pos = pos;
1255 LISTBOX_UpdateScroll( descr );
1256 if (abs(diff) < descr->width)
1259 /* Invalidate the focused item so it will be repainted correctly */
1260 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
1261 InvalidateRect( descr->self, &rect, TRUE );
1262 ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
1263 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1266 InvalidateRect( descr->self, NULL, TRUE );
1270 /***********************************************************************
1271 * LISTBOX_SetHorizontalExtent
1273 static LRESULT LISTBOX_SetHorizontalExtent( LB_DESCR *descr, INT extent )
1275 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1277 if (extent <= 0) extent = 1;
1278 if (extent == descr->horz_extent) return LB_OKAY;
1279 TRACE("[%p]: new horz extent = %d\n", descr->self, extent );
1280 descr->horz_extent = extent;
1281 if (descr->horz_pos > extent - descr->width)
1282 LISTBOX_SetHorizontalPos( descr, extent - descr->width );
1284 LISTBOX_UpdateScroll( descr );
1289 /***********************************************************************
1290 * LISTBOX_SetColumnWidth
1292 static LRESULT LISTBOX_SetColumnWidth( LB_DESCR *descr, INT width)
1294 if (width == descr->column_width) return LB_OKAY;
1295 TRACE("[%p]: new column width = %d\n", descr->self, width );
1296 descr->column_width = width;
1297 LISTBOX_UpdatePage( descr );
1302 /***********************************************************************
1305 * Returns the item height.
1307 static INT LISTBOX_SetFont( LB_DESCR *descr, HFONT font )
1311 const char *alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1316 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE )))
1318 ERR("unable to get DC.\n" );
1321 if (font) oldFont = SelectObject( hdc, font );
1322 GetTextExtentPointA( hdc, alphabet, 52, &sz);
1323 if (oldFont) SelectObject( hdc, oldFont );
1324 ReleaseDC( descr->self, hdc );
1326 descr->avg_char_width = (sz.cx / 26 + 1) / 2;
1327 if (!IS_OWNERDRAW(descr))
1328 LISTBOX_SetItemHeight( descr, 0, sz.cy, FALSE );
1333 /***********************************************************************
1334 * LISTBOX_MakeItemVisible
1336 * Make sure that a given item is partially or fully visible.
1338 static void LISTBOX_MakeItemVisible( LB_DESCR *descr, INT index, BOOL fully )
1342 TRACE("current top item %d, index %d, fully %d\n", descr->top_item, index, fully);
1344 if (index <= descr->top_item) top = index;
1345 else if (descr->style & LBS_MULTICOLUMN)
1347 INT cols = descr->width;
1348 if (!fully) cols += descr->column_width - 1;
1349 if (cols >= descr->column_width) cols /= descr->column_width;
1351 if (index < descr->top_item + (descr->page_size * cols)) return;
1352 top = index - descr->page_size * (cols - 1);
1354 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1356 INT height = fully ? descr->items[index].height : 1;
1357 for (top = index; top > descr->top_item; top--)
1358 if ((height += descr->items[top-1].height) > descr->height) break;
1362 if (index < descr->top_item + descr->page_size) return;
1363 if (!fully && (index == descr->top_item + descr->page_size) &&
1364 (descr->height > (descr->page_size * descr->item_height))) return;
1365 top = index - descr->page_size + 1;
1367 LISTBOX_SetTopItem( descr, top, TRUE );
1370 /***********************************************************************
1371 * LISTBOX_SetCaretIndex
1374 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1377 static LRESULT LISTBOX_SetCaretIndex( LB_DESCR *descr, INT index, BOOL fully_visible )
1379 INT oldfocus = descr->focus_item;
1381 TRACE("old focus %d, index %d\n", oldfocus, index);
1383 if (descr->style & LBS_NOSEL) return LB_ERR;
1384 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1385 if (index == oldfocus) return LB_OKAY;
1387 LISTBOX_DrawFocusRect( descr, FALSE );
1388 descr->focus_item = index;
1390 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1391 LISTBOX_DrawFocusRect( descr, TRUE );
1397 /***********************************************************************
1398 * LISTBOX_SelectItemRange
1400 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1402 static LRESULT LISTBOX_SelectItemRange( LB_DESCR *descr, INT first,
1407 /* A few sanity checks */
1409 if (descr->style & LBS_NOSEL) return LB_ERR;
1410 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1412 if (!descr->nb_items) return LB_OKAY;
1414 if (last == -1 || last >= descr->nb_items) last = descr->nb_items - 1;
1415 if (first < 0) first = 0;
1416 if (last < first) return LB_OKAY;
1418 if (on) /* Turn selection on */
1420 for (i = first; i <= last; i++)
1422 if (descr->items[i].selected) continue;
1423 descr->items[i].selected = TRUE;
1424 LISTBOX_InvalidateItemRect(descr, i);
1427 else /* Turn selection off */
1429 for (i = first; i <= last; i++)
1431 if (!descr->items[i].selected) continue;
1432 descr->items[i].selected = FALSE;
1433 LISTBOX_InvalidateItemRect(descr, i);
1439 /***********************************************************************
1440 * LISTBOX_SetSelection
1442 static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index,
1443 BOOL on, BOOL send_notify )
1445 TRACE( "cur_sel=%d index=%d notify=%s\n",
1446 descr->selected_item, index, send_notify ? "YES" : "NO" );
1448 if (descr->style & LBS_NOSEL)
1450 descr->selected_item = index;
1453 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1454 if (descr->style & LBS_MULTIPLESEL)
1456 if (index == -1) /* Select all items */
1457 return LISTBOX_SelectItemRange( descr, 0, descr->nb_items, on );
1458 else /* Only one item */
1459 return LISTBOX_SelectItemRange( descr, index, index, on );
1463 INT oldsel = descr->selected_item;
1464 if (index == oldsel) return LB_OKAY;
1465 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1466 if (index != -1) descr->items[index].selected = TRUE;
1467 if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT );
1468 descr->selected_item = index;
1469 if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT );
1470 if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr,
1471 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1473 if( descr->lphc ) /* set selection change flag for parent combo */
1474 descr->lphc->wState |= CBF_SELCHANGE;
1480 /***********************************************************************
1483 * Change the caret position and extend the selection to the new caret.
1485 static void LISTBOX_MoveCaret( LB_DESCR *descr, INT index, BOOL fully_visible )
1487 TRACE("old focus %d, index %d\n", descr->focus_item, index);
1489 if ((index < 0) || (index >= descr->nb_items))
1492 /* Important, repaint needs to be done in this order if
1493 you want to mimic Windows behavior:
1494 1. Remove the focus and paint the item
1495 2. Remove the selection and paint the item(s)
1496 3. Set the selection and repaint the item(s)
1497 4. Set the focus to 'index' and repaint the item */
1499 /* 1. remove the focus and repaint the item */
1500 LISTBOX_DrawFocusRect( descr, FALSE );
1502 /* 2. then turn off the previous selection */
1503 /* 3. repaint the new selected item */
1504 if (descr->style & LBS_EXTENDEDSEL)
1506 if (descr->anchor_item != -1)
1508 INT first = min( index, descr->anchor_item );
1509 INT last = max( index, descr->anchor_item );
1511 LISTBOX_SelectItemRange( descr, 0, first - 1, FALSE );
1512 LISTBOX_SelectItemRange( descr, last + 1, -1, FALSE );
1513 LISTBOX_SelectItemRange( descr, first, last, TRUE );
1516 else if (!(descr->style & LBS_MULTIPLESEL))
1518 /* Set selection to new caret item */
1519 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
1522 /* 4. repaint the new item with the focus */
1523 descr->focus_item = index;
1524 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1525 LISTBOX_DrawFocusRect( descr, TRUE );
1529 /***********************************************************************
1530 * LISTBOX_InsertItem
1532 static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index,
1533 LPWSTR str, ULONG_PTR data )
1537 INT oldfocus = descr->focus_item;
1539 if (index == -1) index = descr->nb_items;
1540 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1541 if (!descr->items) max_items = 0;
1542 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1543 if (descr->nb_items == max_items)
1545 /* We need to grow the array */
1546 max_items += LB_ARRAY_GRANULARITY;
1548 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1549 max_items * sizeof(LB_ITEMDATA) );
1551 item = HeapAlloc( GetProcessHeap(), 0,
1552 max_items * sizeof(LB_ITEMDATA) );
1555 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1558 descr->items = item;
1561 /* Insert the item structure */
1563 item = &descr->items[index];
1564 if (index < descr->nb_items)
1565 RtlMoveMemory( item + 1, item,
1566 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1570 item->selected = FALSE;
1573 /* Get item height */
1575 if (descr->style & LBS_OWNERDRAWVARIABLE)
1577 MEASUREITEMSTRUCT mis;
1578 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1580 mis.CtlType = ODT_LISTBOX;
1583 mis.itemData = descr->items[index].data;
1584 mis.itemHeight = descr->item_height;
1585 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1586 item->height = mis.itemHeight ? mis.itemHeight : 1;
1587 TRACE("[%p]: measure item %d (%s) = %d\n",
1588 descr->self, index, str ? debugstr_w(str) : "", item->height );
1591 /* Repaint the items */
1593 LISTBOX_UpdateScroll( descr );
1594 LISTBOX_InvalidateItems( descr, index );
1596 /* Move selection and focused item */
1597 /* If listbox was empty, set focus to the first item */
1598 if (descr->nb_items == 1)
1599 LISTBOX_SetCaretIndex( descr, 0, FALSE );
1600 /* single select don't change selection index in win31 */
1601 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1603 descr->selected_item++;
1604 LISTBOX_SetSelection( descr, descr->selected_item-1, TRUE, FALSE );
1608 if (index <= descr->selected_item)
1610 descr->selected_item++;
1611 descr->focus_item = oldfocus; /* focus not changed */
1618 /***********************************************************************
1619 * LISTBOX_InsertString
1621 static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index, LPCWSTR str )
1623 LPWSTR new_str = NULL;
1627 if (HAS_STRINGS(descr))
1629 static const WCHAR empty_stringW[] = { 0 };
1630 if (!str) str = empty_stringW;
1631 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1633 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1636 strcpyW(new_str, str);
1638 else data = (ULONG_PTR)str;
1640 if (index == -1) index = descr->nb_items;
1641 if ((ret = LISTBOX_InsertItem( descr, index, new_str, data )) != 0)
1643 HeapFree( GetProcessHeap(), 0, new_str );
1647 TRACE("[%p]: added item %d %s\n",
1648 descr->self, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1653 /***********************************************************************
1654 * LISTBOX_DeleteItem
1656 * Delete the content of an item. 'index' must be a valid index.
1658 static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index )
1660 /* save the item data before it gets freed by LB_RESETCONTENT */
1661 ULONG_PTR item_data = descr->items[index].data;
1662 LPWSTR item_str = descr->items[index].str;
1664 if (!descr->nb_items)
1665 SendMessageW( descr->self, LB_RESETCONTENT, 0, 0 );
1667 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1668 * while Win95 sends it for all items with user data.
1669 * It's probably better to send it too often than not
1670 * often enough, so this is what we do here.
1672 if (IS_OWNERDRAW(descr) || item_data)
1674 DELETEITEMSTRUCT dis;
1675 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1677 dis.CtlType = ODT_LISTBOX;
1680 dis.hwndItem = descr->self;
1681 dis.itemData = item_data;
1682 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1684 if (HAS_STRINGS(descr))
1685 HeapFree( GetProcessHeap(), 0, item_str );
1689 /***********************************************************************
1690 * LISTBOX_RemoveItem
1692 * Remove an item from the listbox and delete its content.
1694 static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index )
1699 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1701 /* We need to invalidate the original rect instead of the updated one. */
1702 LISTBOX_InvalidateItems( descr, index );
1705 LISTBOX_DeleteItem( descr, index );
1707 if (!descr->nb_items) return LB_OKAY;
1709 /* Remove the item */
1711 item = &descr->items[index];
1712 if (index < descr->nb_items)
1713 RtlMoveMemory( item, item + 1,
1714 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1715 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1717 /* Shrink the item array if possible */
1719 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1720 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1722 max_items -= LB_ARRAY_GRANULARITY;
1723 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1724 max_items * sizeof(LB_ITEMDATA) );
1725 if (item) descr->items = item;
1727 /* Repaint the items */
1729 LISTBOX_UpdateScroll( descr );
1730 /* if we removed the scrollbar, reset the top of the list
1731 (correct for owner-drawn ???) */
1732 if (descr->nb_items == descr->page_size)
1733 LISTBOX_SetTopItem( descr, 0, TRUE );
1735 /* Move selection and focused item */
1736 if (!IS_MULTISELECT(descr))
1738 if (index == descr->selected_item)
1739 descr->selected_item = -1;
1740 else if (index < descr->selected_item)
1742 descr->selected_item--;
1743 if (ISWIN31) /* win 31 do not change the selected item number */
1744 LISTBOX_SetSelection( descr, descr->selected_item + 1, TRUE, FALSE);
1748 if (descr->focus_item >= descr->nb_items)
1750 descr->focus_item = descr->nb_items - 1;
1751 if (descr->focus_item < 0) descr->focus_item = 0;
1757 /***********************************************************************
1758 * LISTBOX_ResetContent
1760 static void LISTBOX_ResetContent( LB_DESCR *descr )
1764 for(i = descr->nb_items - 1; i>=0; i--) LISTBOX_DeleteItem( descr, i);
1765 HeapFree( GetProcessHeap(), 0, descr->items );
1766 descr->nb_items = 0;
1767 descr->top_item = 0;
1768 descr->selected_item = -1;
1769 descr->focus_item = 0;
1770 descr->anchor_item = -1;
1771 descr->items = NULL;
1775 /***********************************************************************
1778 static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count )
1782 if (HAS_STRINGS(descr))
1784 SetLastError(ERROR_SETCOUNT_ON_BAD_LB);
1788 /* FIXME: this is far from optimal... */
1789 if (count > descr->nb_items)
1791 while (count > descr->nb_items)
1792 if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0)
1795 else if (count < descr->nb_items)
1797 while (count < descr->nb_items)
1798 if ((ret = LISTBOX_RemoveItem( descr, (descr->nb_items - 1) )) < 0)
1805 /***********************************************************************
1808 static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib,
1809 LPCWSTR filespec, BOOL long_names )
1812 LRESULT ret = LB_OKAY;
1813 WIN32_FIND_DATAW entry;
1815 LRESULT maxinsert = LB_ERR;
1817 /* don't scan directory if we just want drives exclusively */
1818 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1819 /* scan directory */
1820 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1822 int le = GetLastError();
1823 if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR;
1830 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1832 static const WCHAR bracketW[] = { ']',0 };
1833 static const WCHAR dotW[] = { '.',0 };
1834 if (!(attrib & DDL_DIRECTORY) ||
1835 !strcmpW( entry.cFileName, dotW )) continue;
1837 if (!long_names && entry.cAlternateFileName[0])
1838 strcpyW( buffer + 1, entry.cAlternateFileName );
1840 strcpyW( buffer + 1, entry.cFileName );
1841 strcatW(buffer, bracketW);
1843 else /* not a directory */
1845 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1846 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1848 if ((attrib & DDL_EXCLUSIVE) &&
1849 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1852 if (!long_names && entry.cAlternateFileName[0])
1853 strcpyW( buffer, entry.cAlternateFileName );
1855 strcpyW( buffer, entry.cFileName );
1857 if (!long_names) CharLowerW( buffer );
1858 pos = LISTBOX_FindFileStrPos( descr, buffer );
1859 if ((ret = LISTBOX_InsertString( descr, pos, buffer )) < 0)
1861 if (ret <= maxinsert) maxinsert++; else maxinsert = ret;
1862 } while (FindNextFileW( handle, &entry ));
1863 FindClose( handle );
1871 if (attrib & DDL_DRIVES)
1873 WCHAR buffer[] = {'[','-','a','-',']',0};
1874 WCHAR root[] = {'A',':','\\',0};
1876 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1878 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1879 if ((ret = LISTBOX_InsertString( descr, -1, buffer )) < 0)
1888 /***********************************************************************
1889 * LISTBOX_HandleVScroll
1891 static LRESULT LISTBOX_HandleVScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1895 if (descr->style & LBS_MULTICOLUMN) return 0;
1899 LISTBOX_SetTopItem( descr, descr->top_item - 1, TRUE );
1902 LISTBOX_SetTopItem( descr, descr->top_item + 1, TRUE );
1905 LISTBOX_SetTopItem( descr, descr->top_item -
1906 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1909 LISTBOX_SetTopItem( descr, descr->top_item +
1910 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1912 case SB_THUMBPOSITION:
1913 LISTBOX_SetTopItem( descr, pos, TRUE );
1916 info.cbSize = sizeof(info);
1917 info.fMask = SIF_TRACKPOS;
1918 GetScrollInfo( descr->self, SB_VERT, &info );
1919 LISTBOX_SetTopItem( descr, info.nTrackPos, TRUE );
1922 LISTBOX_SetTopItem( descr, 0, TRUE );
1925 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1932 /***********************************************************************
1933 * LISTBOX_HandleHScroll
1935 static LRESULT LISTBOX_HandleHScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1940 if (descr->style & LBS_MULTICOLUMN)
1945 LISTBOX_SetTopItem( descr, descr->top_item-descr->page_size,
1949 LISTBOX_SetTopItem( descr, descr->top_item+descr->page_size,
1953 page = descr->width / descr->column_width;
1954 if (page < 1) page = 1;
1955 LISTBOX_SetTopItem( descr,
1956 descr->top_item - page * descr->page_size, TRUE );
1959 page = descr->width / descr->column_width;
1960 if (page < 1) page = 1;
1961 LISTBOX_SetTopItem( descr,
1962 descr->top_item + page * descr->page_size, TRUE );
1964 case SB_THUMBPOSITION:
1965 LISTBOX_SetTopItem( descr, pos*descr->page_size, TRUE );
1968 info.cbSize = sizeof(info);
1969 info.fMask = SIF_TRACKPOS;
1970 GetScrollInfo( descr->self, SB_VERT, &info );
1971 LISTBOX_SetTopItem( descr, info.nTrackPos*descr->page_size,
1975 LISTBOX_SetTopItem( descr, 0, TRUE );
1978 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1982 else if (descr->horz_extent)
1987 LISTBOX_SetHorizontalPos( descr, descr->horz_pos - 1 );
1990 LISTBOX_SetHorizontalPos( descr, descr->horz_pos + 1 );
1993 LISTBOX_SetHorizontalPos( descr,
1994 descr->horz_pos - descr->width );
1997 LISTBOX_SetHorizontalPos( descr,
1998 descr->horz_pos + descr->width );
2000 case SB_THUMBPOSITION:
2001 LISTBOX_SetHorizontalPos( descr, pos );
2004 info.cbSize = sizeof(info);
2005 info.fMask = SIF_TRACKPOS;
2006 GetScrollInfo( descr->self, SB_HORZ, &info );
2007 LISTBOX_SetHorizontalPos( descr, info.nTrackPos );
2010 LISTBOX_SetHorizontalPos( descr, 0 );
2013 LISTBOX_SetHorizontalPos( descr,
2014 descr->horz_extent - descr->width );
2021 static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta )
2023 short gcWheelDelta = 0;
2024 UINT pulScrollLines = 3;
2026 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
2028 gcWheelDelta -= delta;
2030 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
2032 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
2033 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
2034 LISTBOX_SetTopItem( descr, descr->top_item + cLineScroll, TRUE );
2039 /***********************************************************************
2040 * LISTBOX_HandleLButtonDown
2042 static LRESULT LISTBOX_HandleLButtonDown( LB_DESCR *descr, DWORD keys, INT x, INT y )
2044 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
2046 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2047 descr->self, x, y, index, descr->focus_item);
2049 if (!descr->caret_on && (descr->in_focus)) return 0;
2051 if (!descr->in_focus)
2053 if( !descr->lphc ) SetFocus( descr->self );
2054 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
2057 if (index == -1) return 0;
2061 if (descr->style & LBS_NOTIFY )
2062 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
2063 MAKELPARAM( x, y ) );
2066 descr->captured = TRUE;
2067 SetCapture( descr->self );
2069 if (descr->style & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL))
2071 /* we should perhaps make sure that all items are deselected
2072 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2073 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2074 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2077 if (!(keys & MK_SHIFT)) descr->anchor_item = index;
2078 if (keys & MK_CONTROL)
2080 LISTBOX_SetCaretIndex( descr, index, FALSE );
2081 LISTBOX_SetSelection( descr, index,
2082 !descr->items[index].selected,
2083 (descr->style & LBS_NOTIFY) != 0);
2087 LISTBOX_MoveCaret( descr, index, FALSE );
2089 if (descr->style & LBS_EXTENDEDSEL)
2091 LISTBOX_SetSelection( descr, index,
2092 descr->items[index].selected,
2093 (descr->style & LBS_NOTIFY) != 0 );
2097 LISTBOX_SetSelection( descr, index,
2098 !descr->items[index].selected,
2099 (descr->style & LBS_NOTIFY) != 0 );
2105 descr->anchor_item = index;
2106 LISTBOX_MoveCaret( descr, index, FALSE );
2107 LISTBOX_SetSelection( descr, index,
2108 TRUE, (descr->style & LBS_NOTIFY) != 0 );
2113 if (GetWindowLongW( descr->self, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
2120 if (DragDetect( descr->self, pt ))
2121 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
2128 /*************************************************************************
2129 * LISTBOX_HandleLButtonDownCombo [Internal]
2131 * Process LButtonDown message for the ComboListBox
2134 * pWnd [I] The windows internal structure
2135 * pDescr [I] The ListBox internal structure
2136 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2137 * x [I] X Mouse Coordinate
2138 * y [I] Y Mouse Coordinate
2141 * 0 since we are processing the WM_LBUTTONDOWN Message
2144 * This function is only to be used when a ListBox is a ComboListBox
2147 static LRESULT LISTBOX_HandleLButtonDownCombo( LB_DESCR *descr, UINT msg, DWORD keys, INT x, INT y)
2149 RECT clientRect, screenRect;
2155 GetClientRect(descr->self, &clientRect);
2157 if(PtInRect(&clientRect, mousePos))
2159 /* MousePos is in client, resume normal processing */
2160 if (msg == WM_LBUTTONDOWN)
2162 descr->lphc->droppedIndex = descr->nb_items ? descr->selected_item : -1;
2163 return LISTBOX_HandleLButtonDown( descr, keys, x, y);
2165 else if (descr->style & LBS_NOTIFY)
2166 SEND_NOTIFICATION( descr, LBN_DBLCLK );
2170 POINT screenMousePos;
2171 HWND hWndOldCapture;
2173 /* Check the Non-Client Area */
2174 screenMousePos = mousePos;
2175 hWndOldCapture = GetCapture();
2177 GetWindowRect(descr->self, &screenRect);
2178 ClientToScreen(descr->self, &screenMousePos);
2180 if(!PtInRect(&screenRect, screenMousePos))
2182 LISTBOX_SetCaretIndex( descr, descr->lphc->droppedIndex, FALSE );
2183 LISTBOX_SetSelection( descr, descr->lphc->droppedIndex, FALSE, FALSE );
2184 COMBO_FlipListbox( descr->lphc, FALSE, FALSE );
2188 /* Check to see the NC is a scrollbar */
2190 LONG style = GetWindowLongW( descr->self, GWL_STYLE );
2191 /* Check Vertical scroll bar */
2192 if (style & WS_VSCROLL)
2194 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2195 if (PtInRect( &clientRect, mousePos ))
2196 nHitTestType = HTVSCROLL;
2198 /* Check horizontal scroll bar */
2199 if (style & WS_HSCROLL)
2201 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2202 if (PtInRect( &clientRect, mousePos ))
2203 nHitTestType = HTHSCROLL;
2205 /* Windows sends this message when a scrollbar is clicked
2208 if(nHitTestType != 0)
2210 SendMessageW(descr->self, WM_NCLBUTTONDOWN, nHitTestType,
2211 MAKELONG(screenMousePos.x, screenMousePos.y));
2213 /* Resume the Capture after scrolling is complete
2215 if(hWndOldCapture != 0)
2216 SetCapture(hWndOldCapture);
2222 /***********************************************************************
2223 * LISTBOX_HandleLButtonUp
2225 static LRESULT LISTBOX_HandleLButtonUp( LB_DESCR *descr )
2227 if (LISTBOX_Timer != LB_TIMER_NONE)
2228 KillSystemTimer( descr->self, LB_TIMER_ID );
2229 LISTBOX_Timer = LB_TIMER_NONE;
2230 if (descr->captured)
2232 descr->captured = FALSE;
2233 if (GetCapture() == descr->self) ReleaseCapture();
2234 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2235 SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2241 /***********************************************************************
2242 * LISTBOX_HandleTimer
2244 * Handle scrolling upon a timer event.
2245 * Return TRUE if scrolling should continue.
2247 static LRESULT LISTBOX_HandleTimer( LB_DESCR *descr, INT index, TIMER_DIRECTION dir )
2252 if (descr->top_item) index = descr->top_item - 1;
2256 if (descr->top_item) index -= descr->page_size;
2259 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2260 if (index == descr->focus_item) index++;
2261 if (index >= descr->nb_items) index = descr->nb_items - 1;
2263 case LB_TIMER_RIGHT:
2264 if (index + descr->page_size < descr->nb_items)
2265 index += descr->page_size;
2270 if (index == descr->focus_item) return FALSE;
2271 LISTBOX_MoveCaret( descr, index, FALSE );
2276 /***********************************************************************
2277 * LISTBOX_HandleSystemTimer
2279 * WM_SYSTIMER handler.
2281 static LRESULT LISTBOX_HandleSystemTimer( LB_DESCR *descr )
2283 if (!LISTBOX_HandleTimer( descr, descr->focus_item, LISTBOX_Timer ))
2285 KillSystemTimer( descr->self, LB_TIMER_ID );
2286 LISTBOX_Timer = LB_TIMER_NONE;
2292 /***********************************************************************
2293 * LISTBOX_HandleMouseMove
2295 * WM_MOUSEMOVE handler.
2297 static void LISTBOX_HandleMouseMove( LB_DESCR *descr,
2301 TIMER_DIRECTION dir = LB_TIMER_NONE;
2303 if (!descr->captured) return;
2305 if (descr->style & LBS_MULTICOLUMN)
2308 else if (y >= descr->item_height * descr->page_size)
2309 y = descr->item_height * descr->page_size - 1;
2313 dir = LB_TIMER_LEFT;
2316 else if (x >= descr->width)
2318 dir = LB_TIMER_RIGHT;
2319 x = descr->width - 1;
2324 if (y < 0) dir = LB_TIMER_UP; /* above */
2325 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2328 index = LISTBOX_GetItemFromPoint( descr, x, y );
2329 if (index == -1) index = descr->focus_item;
2330 if (!LISTBOX_HandleTimer( descr, index, dir )) dir = LB_TIMER_NONE;
2332 /* Start/stop the system timer */
2334 if (dir != LB_TIMER_NONE)
2335 SetSystemTimer( descr->self, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2336 else if (LISTBOX_Timer != LB_TIMER_NONE)
2337 KillSystemTimer( descr->self, LB_TIMER_ID );
2338 LISTBOX_Timer = dir;
2342 /***********************************************************************
2343 * LISTBOX_HandleKeyDown
2345 static LRESULT LISTBOX_HandleKeyDown( LB_DESCR *descr, DWORD key )
2348 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2349 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2350 bForceSelection = FALSE; /* only for single select list */
2352 if (descr->style & LBS_WANTKEYBOARDINPUT)
2354 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2355 MAKEWPARAM(LOWORD(key), descr->focus_item),
2356 (LPARAM)descr->self );
2357 if (caret == -2) return 0;
2359 if (caret == -1) switch(key)
2362 if (descr->style & LBS_MULTICOLUMN)
2364 bForceSelection = FALSE;
2365 if (descr->focus_item >= descr->page_size)
2366 caret = descr->focus_item - descr->page_size;
2371 caret = descr->focus_item - 1;
2372 if (caret < 0) caret = 0;
2375 if (descr->style & LBS_MULTICOLUMN)
2377 bForceSelection = FALSE;
2378 if (descr->focus_item + descr->page_size < descr->nb_items)
2379 caret = descr->focus_item + descr->page_size;
2384 caret = descr->focus_item + 1;
2385 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2389 if (descr->style & LBS_MULTICOLUMN)
2391 INT page = descr->width / descr->column_width;
2392 if (page < 1) page = 1;
2393 caret = descr->focus_item - (page * descr->page_size) + 1;
2395 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2396 if (caret < 0) caret = 0;
2399 if (descr->style & LBS_MULTICOLUMN)
2401 INT page = descr->width / descr->column_width;
2402 if (page < 1) page = 1;
2403 caret = descr->focus_item + (page * descr->page_size) - 1;
2405 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2406 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2412 caret = descr->nb_items - 1;
2415 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2416 else if (descr->style & LBS_MULTIPLESEL)
2418 LISTBOX_SetSelection( descr, descr->focus_item,
2419 !descr->items[descr->focus_item].selected,
2420 (descr->style & LBS_NOTIFY) != 0 );
2424 bForceSelection = FALSE;
2426 if (bForceSelection) /* focused item is used instead of key */
2427 caret = descr->focus_item;
2430 if (((descr->style & LBS_EXTENDEDSEL) &&
2431 !(GetKeyState( VK_SHIFT ) & 0x8000)) ||
2432 !IS_MULTISELECT(descr))
2433 descr->anchor_item = caret;
2434 LISTBOX_MoveCaret( descr, caret, TRUE );
2436 if (descr->style & LBS_MULTIPLESEL)
2437 descr->selected_item = caret;
2439 LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2440 if (descr->style & LBS_NOTIFY)
2442 if (descr->lphc && IsWindowVisible( descr->self ))
2444 /* make sure that combo parent doesn't hide us */
2445 descr->lphc->wState |= CBF_NOROLLUP;
2447 if (descr->nb_items) SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2454 /***********************************************************************
2455 * LISTBOX_HandleChar
2457 static LRESULT LISTBOX_HandleChar( LB_DESCR *descr, WCHAR charW )
2465 if (descr->style & LBS_WANTKEYBOARDINPUT)
2467 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2468 MAKEWPARAM(charW, descr->focus_item),
2469 (LPARAM)descr->self );
2470 if (caret == -2) return 0;
2473 caret = LISTBOX_FindString( descr, descr->focus_item, str, FALSE);
2476 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2477 LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2478 LISTBOX_MoveCaret( descr, caret, TRUE );
2479 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2480 SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2486 /***********************************************************************
2489 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2492 MEASUREITEMSTRUCT mis;
2495 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2498 GetClientRect( hwnd, &rect );
2500 descr->owner = GetParent( descr->self );
2501 descr->style = GetWindowLongW( descr->self, GWL_STYLE );
2502 descr->width = rect.right - rect.left;
2503 descr->height = rect.bottom - rect.top;
2504 descr->items = NULL;
2505 descr->nb_items = 0;
2506 descr->top_item = 0;
2507 descr->selected_item = -1;
2508 descr->focus_item = 0;
2509 descr->anchor_item = -1;
2510 descr->item_height = 1;
2511 descr->page_size = 1;
2512 descr->column_width = 150;
2513 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0;
2514 descr->horz_pos = 0;
2517 descr->caret_on = lphc ? FALSE : TRUE;
2518 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2519 descr->in_focus = FALSE;
2520 descr->captured = FALSE;
2522 descr->locale = GetUserDefaultLCID();
2525 if (is_old_app(descr) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2527 /* Win95 document "List Box Differences" from MSDN:
2528 If a list box in a version 3.x application has either the
2529 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2530 horizontal and vertical scroll bars.
2532 descr->style |= WS_VSCROLL | WS_HSCROLL;
2537 TRACE("[%p]: resetting owner %p -> %p\n", descr->self, descr->owner, lphc->self );
2538 descr->owner = lphc->self;
2541 SetWindowLongPtrW( descr->self, 0, (LONG_PTR)descr );
2543 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2545 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2546 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2547 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2548 descr->item_height = LISTBOX_SetFont( descr, 0 );
2550 if (descr->style & LBS_OWNERDRAWFIXED)
2552 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2554 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2555 descr->item_height = lphc->fixedOwnerDrawHeight;
2559 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
2560 mis.CtlType = ODT_LISTBOX;
2565 mis.itemHeight = descr->item_height;
2566 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2567 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2571 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2576 /***********************************************************************
2579 static BOOL LISTBOX_Destroy( LB_DESCR *descr )
2581 LISTBOX_ResetContent( descr );
2582 SetWindowLongPtrW( descr->self, 0, 0 );
2583 HeapFree( GetProcessHeap(), 0, descr );
2588 /***********************************************************************
2589 * ListBoxWndProc_common
2591 static LRESULT ListBoxWndProc_common( HWND hwnd, UINT msg,
2592 WPARAM wParam, LPARAM lParam, BOOL unicode )
2594 LB_DESCR *descr = (LB_DESCR *)GetWindowLongPtrW( hwnd, 0 );
2595 LPHEADCOMBO lphc = 0;
2600 if (!IsWindow(hwnd)) return 0;
2602 if (msg == WM_CREATE)
2604 CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam;
2605 if (lpcs->style & LBS_COMBOBOX) lphc = lpcs->lpCreateParams;
2606 if (!LISTBOX_Create( hwnd, lphc )) return -1;
2607 TRACE("creating hwnd %p descr %p\n", hwnd, (void *)GetWindowLongPtrW( hwnd, 0 ) );
2610 /* Ignore all other messages before we get a WM_CREATE */
2611 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2612 DefWindowProcA( hwnd, msg, wParam, lParam );
2614 if (descr->style & LBS_COMBOBOX) lphc = descr->lphc;
2616 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2617 descr->self, SPY_GetMsgName(msg, descr->self), wParam, lParam );
2621 case LB_RESETCONTENT:
2622 LISTBOX_ResetContent( descr );
2623 LISTBOX_UpdateScroll( descr );
2624 InvalidateRect( descr->self, NULL, TRUE );
2631 if(unicode || !HAS_STRINGS(descr))
2632 textW = (LPWSTR)lParam;
2635 LPSTR textA = (LPSTR)lParam;
2636 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2637 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2638 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2642 wParam = LISTBOX_FindStringPos( descr, textW, FALSE );
2643 ret = LISTBOX_InsertString( descr, wParam, textW );
2644 if (!unicode && HAS_STRINGS(descr))
2645 HeapFree(GetProcessHeap(), 0, textW);
2649 case LB_INSERTSTRING:
2653 if(unicode || !HAS_STRINGS(descr))
2654 textW = (LPWSTR)lParam;
2657 LPSTR textA = (LPSTR)lParam;
2658 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2659 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2660 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2664 ret = LISTBOX_InsertString( descr, wParam, textW );
2665 if(!unicode && HAS_STRINGS(descr))
2666 HeapFree(GetProcessHeap(), 0, textW);
2674 if(unicode || !HAS_STRINGS(descr))
2675 textW = (LPWSTR)lParam;
2678 LPSTR textA = (LPSTR)lParam;
2679 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2680 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2681 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2685 wParam = LISTBOX_FindFileStrPos( descr, textW );
2686 ret = LISTBOX_InsertString( descr, wParam, textW );
2687 if(!unicode && HAS_STRINGS(descr))
2688 HeapFree(GetProcessHeap(), 0, textW);
2692 case LB_DELETESTRING:
2693 if (LISTBOX_RemoveItem( descr, wParam) != LB_ERR)
2694 return descr->nb_items;
2697 SetLastError(ERROR_INVALID_INDEX);
2701 case LB_GETITEMDATA:
2702 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2704 SetLastError(ERROR_INVALID_INDEX);
2707 return descr->items[wParam].data;
2709 case LB_SETITEMDATA:
2710 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2712 SetLastError(ERROR_INVALID_INDEX);
2715 descr->items[wParam].data = lParam;
2716 /* undocumented: returns TRUE, not LB_OKAY (0) */
2720 return descr->nb_items;
2723 return LISTBOX_GetText( descr, wParam, (LPWSTR)lParam, unicode );
2726 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2728 SetLastError(ERROR_INVALID_INDEX);
2731 if (!HAS_STRINGS(descr)) return sizeof(DWORD);
2732 if (unicode) return strlenW( descr->items[wParam].str );
2733 return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
2734 strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
2737 if (descr->nb_items == 0)
2739 if (!IS_MULTISELECT(descr))
2740 return descr->selected_item;
2741 if (descr->selected_item != -1)
2742 return descr->selected_item;
2743 return descr->focus_item;
2744 /* otherwise, if the user tries to move the selection with the */
2745 /* arrow keys, we will give the application something to choke on */
2746 case LB_GETTOPINDEX:
2747 return descr->top_item;
2749 case LB_GETITEMHEIGHT:
2750 return LISTBOX_GetItemHeight( descr, wParam );
2752 case LB_SETITEMHEIGHT:
2753 return LISTBOX_SetItemHeight( descr, wParam, lParam, TRUE );
2755 case LB_ITEMFROMPOINT:
2762 /* The hiword of the return value is not a client area
2763 hittest as suggested by MSDN, but rather a hittest on
2764 the returned listbox item. */
2766 if(descr->nb_items == 0)
2767 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2769 pt.x = (short)LOWORD(lParam);
2770 pt.y = (short)HIWORD(lParam);
2772 SetRect(&rect, 0, 0, descr->width, descr->height);
2774 if(!PtInRect(&rect, pt))
2776 pt.x = min(pt.x, rect.right - 1);
2777 pt.x = max(pt.x, 0);
2778 pt.y = min(pt.y, rect.bottom - 1);
2779 pt.y = max(pt.y, 0);
2783 index = LISTBOX_GetItemFromPoint(descr, pt.x, pt.y);
2787 index = descr->nb_items - 1;
2790 return MAKELONG(index, hit ? 0 : 1);
2793 case LB_SETCARETINDEX:
2794 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2795 if (LISTBOX_SetCaretIndex( descr, wParam, !lParam ) == LB_ERR)
2802 case LB_GETCARETINDEX:
2803 return descr->focus_item;
2805 case LB_SETTOPINDEX:
2806 return LISTBOX_SetTopItem( descr, wParam, TRUE );
2808 case LB_SETCOLUMNWIDTH:
2809 return LISTBOX_SetColumnWidth( descr, wParam );
2811 case LB_GETITEMRECT:
2812 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2818 if(unicode || !HAS_STRINGS(descr))
2819 textW = (LPWSTR)lParam;
2822 LPSTR textA = (LPSTR)lParam;
2823 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2824 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2825 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2827 ret = LISTBOX_FindString( descr, wParam, textW, FALSE );
2828 if(!unicode && HAS_STRINGS(descr))
2829 HeapFree(GetProcessHeap(), 0, textW);
2833 case LB_FINDSTRINGEXACT:
2837 if(unicode || !HAS_STRINGS(descr))
2838 textW = (LPWSTR)lParam;
2841 LPSTR textA = (LPSTR)lParam;
2842 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2843 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2844 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2846 ret = LISTBOX_FindString( descr, wParam, textW, TRUE );
2847 if(!unicode && HAS_STRINGS(descr))
2848 HeapFree(GetProcessHeap(), 0, textW);
2852 case LB_SELECTSTRING:
2857 if(HAS_STRINGS(descr))
2858 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2859 debugstr_a((LPSTR)lParam));
2860 if(unicode || !HAS_STRINGS(descr))
2861 textW = (LPWSTR)lParam;
2864 LPSTR textA = (LPSTR)lParam;
2865 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2866 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2867 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2869 index = LISTBOX_FindString( descr, wParam, textW, FALSE );
2870 if(!unicode && HAS_STRINGS(descr))
2871 HeapFree(GetProcessHeap(), 0, textW);
2872 if (index != LB_ERR)
2874 LISTBOX_MoveCaret( descr, index, TRUE );
2875 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
2881 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2883 return descr->items[wParam].selected;
2886 return LISTBOX_SetSelection( descr, lParam, wParam, FALSE );
2889 if (IS_MULTISELECT(descr)) return LB_ERR;
2890 LISTBOX_SetCaretIndex( descr, wParam, FALSE );
2891 ret = LISTBOX_SetSelection( descr, wParam, TRUE, FALSE );
2892 if (ret != LB_ERR) ret = descr->selected_item;
2895 case LB_GETSELCOUNT:
2896 return LISTBOX_GetSelCount( descr );
2898 case LB_GETSELITEMS:
2899 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2901 case LB_SELITEMRANGE:
2902 if (LOWORD(lParam) <= HIWORD(lParam))
2903 return LISTBOX_SelectItemRange( descr, LOWORD(lParam),
2904 HIWORD(lParam), wParam );
2906 return LISTBOX_SelectItemRange( descr, HIWORD(lParam),
2907 LOWORD(lParam), wParam );
2909 case LB_SELITEMRANGEEX:
2910 if ((INT)lParam >= (INT)wParam)
2911 return LISTBOX_SelectItemRange( descr, wParam, lParam, TRUE );
2913 return LISTBOX_SelectItemRange( descr, lParam, wParam, FALSE);
2915 case LB_GETHORIZONTALEXTENT:
2916 return descr->horz_extent;
2918 case LB_SETHORIZONTALEXTENT:
2919 return LISTBOX_SetHorizontalExtent( descr, wParam );
2921 case LB_GETANCHORINDEX:
2922 return descr->anchor_item;
2924 case LB_SETANCHORINDEX:
2925 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2927 SetLastError(ERROR_INVALID_INDEX);
2930 descr->anchor_item = (INT)wParam;
2938 textW = (LPWSTR)lParam;
2941 LPSTR textA = (LPSTR)lParam;
2942 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2943 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2944 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2946 ret = LISTBOX_Directory( descr, wParam, textW, msg == LB_DIR );
2948 HeapFree(GetProcessHeap(), 0, textW);
2953 return descr->locale;
2958 if (!IsValidLocale((LCID)wParam, LCID_INSTALLED))
2960 ret = descr->locale;
2961 descr->locale = (LCID)wParam;
2965 case LB_INITSTORAGE:
2966 return LISTBOX_InitStorage( descr, wParam );
2969 return LISTBOX_SetCount( descr, (INT)wParam );
2971 case LB_SETTABSTOPS:
2972 return LISTBOX_SetTabStops( descr, wParam, (LPINT)lParam );
2975 if (descr->caret_on)
2977 descr->caret_on = TRUE;
2978 if ((descr->focus_item != -1) && (descr->in_focus))
2979 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2983 if (!descr->caret_on)
2985 descr->caret_on = FALSE;
2986 if ((descr->focus_item != -1) && (descr->in_focus))
2987 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2990 case LB_GETLISTBOXINFO:
2991 FIXME("LB_GETLISTBOXINFO: stub!\n");
2995 return LISTBOX_Destroy( descr );
2998 InvalidateRect( descr->self, NULL, TRUE );
3002 LISTBOX_SetRedraw( descr, wParam != 0 );
3006 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3008 case WM_PRINTCLIENT:
3012 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( descr->self, &ps );
3013 ret = LISTBOX_Paint( descr, hdc );
3014 if( !wParam ) EndPaint( descr->self, &ps );
3018 LISTBOX_UpdateSize( descr );
3021 return (LRESULT)descr->font;
3023 LISTBOX_SetFont( descr, (HFONT)wParam );
3024 if (lParam) InvalidateRect( descr->self, 0, TRUE );
3027 descr->in_focus = TRUE;
3028 descr->caret_on = TRUE;
3029 if (descr->focus_item != -1)
3030 LISTBOX_DrawFocusRect( descr, TRUE );
3031 SEND_NOTIFICATION( descr, LBN_SETFOCUS );
3034 descr->in_focus = FALSE;
3035 if ((descr->focus_item != -1) && descr->caret_on)
3036 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3037 SEND_NOTIFICATION( descr, LBN_KILLFOCUS );
3040 return LISTBOX_HandleHScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3042 return LISTBOX_HandleVScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3044 if (wParam & (MK_SHIFT | MK_CONTROL))
3045 return DefWindowProcW( descr->self, msg, wParam, lParam );
3046 return LISTBOX_HandleMouseWheel( descr, (SHORT)HIWORD(wParam) );
3047 case WM_LBUTTONDOWN:
3049 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3050 (INT16)LOWORD(lParam),
3051 (INT16)HIWORD(lParam) );
3052 return LISTBOX_HandleLButtonDown( descr, wParam,
3053 (INT16)LOWORD(lParam),
3054 (INT16)HIWORD(lParam) );
3055 case WM_LBUTTONDBLCLK:
3057 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3058 (INT16)LOWORD(lParam),
3059 (INT16)HIWORD(lParam) );
3060 if (descr->style & LBS_NOTIFY)
3061 SEND_NOTIFICATION( descr, LBN_DBLCLK );
3064 if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) )
3066 BOOL captured = descr->captured;
3070 mousePos.x = (INT16)LOWORD(lParam);
3071 mousePos.y = (INT16)HIWORD(lParam);
3074 * If we are in a dropdown combobox, we simulate that
3075 * the mouse is captured to show the tracking of the item.
3077 if (GetClientRect(descr->self, &clientRect) && PtInRect( &clientRect, mousePos ))
3078 descr->captured = TRUE;
3080 LISTBOX_HandleMouseMove( descr, mousePos.x, mousePos.y);
3082 descr->captured = captured;
3084 else if (GetCapture() == descr->self)
3086 LISTBOX_HandleMouseMove( descr, (INT16)LOWORD(lParam),
3087 (INT16)HIWORD(lParam) );
3097 * If the mouse button "up" is not in the listbox,
3098 * we make sure there is no selection by re-selecting the
3099 * item that was selected when the listbox was made visible.
3101 mousePos.x = (INT16)LOWORD(lParam);
3102 mousePos.y = (INT16)HIWORD(lParam);
3104 GetClientRect(descr->self, &clientRect);
3107 * When the user clicks outside the combobox and the focus
3108 * is lost, the owning combobox will send a fake buttonup with
3109 * 0xFFFFFFF as the mouse location, we must also revert the
3110 * selection to the original selection.
3112 if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) )
3113 LISTBOX_MoveCaret( descr, lphc->droppedIndex, FALSE );
3115 return LISTBOX_HandleLButtonUp( descr );
3117 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3119 /* for some reason Windows makes it possible to
3120 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3122 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3123 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3124 && (wParam == VK_DOWN || wParam == VK_UP)) )
3126 COMBO_FlipListbox( lphc, FALSE, FALSE );
3130 return LISTBOX_HandleKeyDown( descr, wParam );
3135 charW = (WCHAR)wParam;
3138 CHAR charA = (CHAR)wParam;
3139 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
3141 return LISTBOX_HandleChar( descr, charW );
3144 return LISTBOX_HandleSystemTimer( descr );
3146 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
3149 HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
3150 wParam, (LPARAM)descr->self );
3151 TRACE("hbrush = %p\n", hbrush);
3153 hbrush = GetSysColorBrush(COLOR_WINDOW);
3156 GetClientRect(descr->self, &rect);
3157 FillRect((HDC)wParam, &rect, hbrush);
3162 if( lphc ) return 0;
3163 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3164 SendMessageA( descr->owner, msg, wParam, lParam );
3167 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3176 if ((msg >= WM_USER) && (msg < 0xc000))
3177 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3178 hwnd, msg, wParam, lParam );
3181 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3182 DefWindowProcA( hwnd, msg, wParam, lParam );
3185 /***********************************************************************
3186 * ListBoxWndProc_wrapper16
3188 static LRESULT ListBoxWndProc_wrapper16( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, BOOL unicode )
3190 static const UINT msg16_offset = LB_ADDSTRING16 - LB_ADDSTRING;
3195 case LB_RESETCONTENT16:
3196 case LB_DELETESTRING16:
3197 case LB_GETITEMDATA16:
3198 case LB_SETITEMDATA16:
3200 case LB_GETTEXTLEN16:
3201 case LB_GETCURSEL16:
3202 case LB_GETTOPINDEX16:
3203 case LB_GETITEMHEIGHT16:
3204 case LB_SETCARETINDEX16:
3205 case LB_GETCARETINDEX16:
3206 case LB_SETTOPINDEX16:
3207 case LB_SETCOLUMNWIDTH16:
3208 case LB_GETSELCOUNT16:
3209 case LB_SELITEMRANGE16:
3210 case LB_SELITEMRANGEEX16:
3211 case LB_GETHORIZONTALEXTENT16:
3212 case LB_SETHORIZONTALEXTENT16:
3213 case LB_GETANCHORINDEX16:
3216 msg -= msg16_offset;
3220 case LB_SETCURSEL16:
3221 case LB_SETANCHORINDEX16:
3222 wParam = (INT)(INT16)wParam;
3223 msg -= msg16_offset;
3225 case LB_INSERTSTRING16:
3226 case LB_FINDSTRING16:
3227 case LB_FINDSTRINGEXACT16:
3228 case LB_SELECTSTRING16:
3229 wParam = (INT)(INT16)wParam;
3231 case LB_ADDSTRING16:
3234 DWORD style = GetWindowLongW( hwnd, GWL_STYLE );
3235 if ((style & LBS_HASSTRINGS) || !(style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE)))
3236 lParam = (LPARAM)MapSL(lParam);
3237 msg -= msg16_offset;
3241 lParam = (LPARAM)MapSL(lParam);
3242 msg -= msg16_offset;
3244 case LB_SETITEMHEIGHT16:
3245 lParam = LOWORD(lParam);
3246 msg -= msg16_offset;
3248 case LB_GETITEMRECT16:
3251 RECT16 *r16 = MapSL(lParam);
3252 ret = ListBoxWndProc_common( hwnd, LB_GETITEMRECT, (INT16)wParam, (LPARAM)&rect, FALSE );
3253 r16->left = rect.left;
3254 r16->top = rect.top;
3255 r16->right = rect.right;
3256 r16->bottom = rect.bottom;
3259 case LB_GETSELITEMS16:
3261 INT16 *array16 = MapSL( lParam );
3262 INT i, count = (INT16)wParam, *array;
3263 if (!(array = HeapAlloc( GetProcessHeap(), 0, wParam * sizeof(*array) ))) return LB_ERRSPACE;
3264 ret = ListBoxWndProc_common( hwnd, LB_GETSELITEMS, count, (LPARAM)array, FALSE );
3265 for (i = 0; i < ret; i++) array16[i] = array[i];
3266 HeapFree( GetProcessHeap(), 0, array );
3270 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
3271 * be set automatically (this is different in Win32) */
3272 if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
3273 lParam = (LPARAM)MapSL(lParam);
3274 msg -= msg16_offset;
3276 case LB_SETTABSTOPS16:
3278 INT i, count, *tabs = NULL;
3279 INT16 *tabs16 = MapSL( lParam );
3281 if ((count = (INT16)wParam) > 0)
3283 if (!(tabs = HeapAlloc( GetProcessHeap(), 0, wParam * sizeof(*tabs) ))) return LB_ERRSPACE;
3284 for (i = 0; i < count; i++) tabs[i] = tabs16[i] << 1; /* FIXME */
3286 ret = ListBoxWndProc_common( hwnd, LB_SETTABSTOPS, count, (LPARAM)tabs, FALSE );
3287 HeapFree( GetProcessHeap(), 0, tabs );
3291 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, unicode );
3293 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3296 /***********************************************************************
3299 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3301 return ListBoxWndProc_wrapper16( hwnd, msg, wParam, lParam, FALSE );
3304 /***********************************************************************
3307 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3309 return ListBoxWndProc_wrapper16( hwnd, msg, wParam, lParam, TRUE );