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, BOOL short_ints )
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) )))
779 LPINT16 p = (LPINT16)tabs;
781 TRACE("[%p]: settabstops ", descr->self );
782 for (i = 0; i < descr->nb_tabs; i++) {
783 descr->tabs[i] = *p++<<1; /* FIXME */
784 TRACE("%hd ", descr->tabs[i]);
788 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
790 /* convert into "dialog units"*/
791 for (i = 0; i < descr->nb_tabs; i++)
792 descr->tabs[i] = MulDiv(descr->tabs[i], descr->avg_char_width, 4);
798 /***********************************************************************
801 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPWSTR buffer, BOOL unicode )
805 if ((index < 0) || (index >= descr->nb_items))
807 SetLastError(ERROR_INVALID_INDEX);
810 if (HAS_STRINGS(descr))
814 len = strlenW(descr->items[index].str);
817 return WideCharToMultiByte( CP_ACP, 0, descr->items[index].str, len,
818 NULL, 0, NULL, NULL );
821 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
823 __TRY /* hide a Delphi bug that passes a read-only buffer */
827 strcpyW( buffer, descr->items[index].str );
828 len = strlenW(buffer);
832 len = WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1,
833 (LPSTR)buffer, 0x7FFFFFFF, NULL, NULL) - 1;
838 WARN( "got an invalid buffer (Delphi bug?)\n" );
839 SetLastError( ERROR_INVALID_PARAMETER );
845 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
851 static inline INT LISTBOX_lstrcmpiW( LCID lcid, LPCWSTR str1, LPCWSTR str2 )
853 INT ret = CompareStringW( lcid, NORM_IGNORECASE, str1, -1, str2, -1 );
854 if (ret == CSTR_LESS_THAN)
856 if (ret == CSTR_EQUAL)
858 if (ret == CSTR_GREATER_THAN)
863 /***********************************************************************
864 * LISTBOX_FindStringPos
866 * Find the nearest string located before a given string in sort order.
867 * If 'exact' is TRUE, return an error if we don't get an exact match.
869 static INT LISTBOX_FindStringPos( LB_DESCR *descr, LPCWSTR str, BOOL exact )
871 INT index, min, max, res = -1;
873 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
875 max = descr->nb_items;
878 index = (min + max) / 2;
879 if (HAS_STRINGS(descr))
880 res = LISTBOX_lstrcmpiW( descr->locale, str, descr->items[index].str);
883 COMPAREITEMSTRUCT cis;
884 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
886 cis.CtlType = ODT_LISTBOX;
888 cis.hwndItem = descr->self;
889 /* note that some application (MetaStock) expects the second item
890 * to be in the listbox */
892 cis.itemData1 = (ULONG_PTR)str;
894 cis.itemData2 = descr->items[index].data;
895 cis.dwLocaleId = descr->locale;
896 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
898 if (!res) return index;
899 if (res < 0) max = index;
900 else min = index + 1;
902 return exact ? -1 : max;
906 /***********************************************************************
907 * LISTBOX_FindFileStrPos
909 * Find the nearest string located before a given string in directory
910 * sort order (i.e. first files, then directories, then drives).
912 static INT LISTBOX_FindFileStrPos( LB_DESCR *descr, LPCWSTR str )
914 INT min, max, res = -1;
916 if (!HAS_STRINGS(descr))
917 return LISTBOX_FindStringPos( descr, str, FALSE );
919 max = descr->nb_items;
922 INT index = (min + max) / 2;
923 LPCWSTR p = descr->items[index].str;
924 if (*p == '[') /* drive or directory */
926 if (*str != '[') res = -1;
927 else if (p[1] == '-') /* drive */
929 if (str[1] == '-') res = str[2] - p[2];
934 if (str[1] == '-') res = 1;
935 else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
940 if (*str == '[') res = 1;
941 else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
943 if (!res) return index;
944 if (res < 0) max = index;
945 else min = index + 1;
951 /***********************************************************************
954 * Find the item beginning with a given string.
956 static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exact )
961 if (start >= descr->nb_items) start = -1;
962 item = descr->items + start + 1;
963 if (HAS_STRINGS(descr))
965 if (!str || ! str[0] ) return LB_ERR;
968 for (i = start + 1; i < descr->nb_items; i++, item++)
969 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
970 for (i = 0, item = descr->items; i <= start; i++, item++)
971 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
975 /* Special case for drives and directories: ignore prefix */
976 #define CHECK_DRIVE(item) \
977 if ((item)->str[0] == '[') \
979 if (!strncmpiW( str, (item)->str+1, len )) return i; \
980 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
984 INT len = strlenW(str);
985 for (i = start + 1; i < descr->nb_items; i++, item++)
987 if (!strncmpiW( str, item->str, len )) return i;
990 for (i = 0, item = descr->items; i <= start; i++, item++)
992 if (!strncmpiW( str, item->str, len )) return i;
1000 if (exact && (descr->style & LBS_SORT))
1001 /* If sorted, use a WM_COMPAREITEM binary search */
1002 return LISTBOX_FindStringPos( descr, str, TRUE );
1004 /* Otherwise use a linear search */
1005 for (i = start + 1; i < descr->nb_items; i++, item++)
1006 if (item->data == (ULONG_PTR)str) return i;
1007 for (i = 0, item = descr->items; i <= start; i++, item++)
1008 if (item->data == (ULONG_PTR)str) return i;
1014 /***********************************************************************
1015 * LISTBOX_GetSelCount
1017 static LRESULT LISTBOX_GetSelCount( const LB_DESCR *descr )
1020 const LB_ITEMDATA *item = descr->items;
1022 if (!(descr->style & LBS_MULTIPLESEL) ||
1023 (descr->style & LBS_NOSEL))
1025 for (i = count = 0; i < descr->nb_items; i++, item++)
1026 if (item->selected) count++;
1031 /***********************************************************************
1032 * LISTBOX_GetSelItems16
1034 static LRESULT LISTBOX_GetSelItems16( const LB_DESCR *descr, INT16 max, LPINT16 array )
1037 const LB_ITEMDATA *item = descr->items;
1039 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1040 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
1041 if (item->selected) array[count++] = (INT16)i;
1046 /***********************************************************************
1047 * LISTBOX_GetSelItems
1049 static LRESULT LISTBOX_GetSelItems( const LB_DESCR *descr, INT max, LPINT array )
1052 const LB_ITEMDATA *item = descr->items;
1054 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1055 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
1056 if (item->selected) array[count++] = i;
1061 /***********************************************************************
1064 static LRESULT LISTBOX_Paint( LB_DESCR *descr, HDC hdc )
1066 INT i, col_pos = descr->page_size - 1;
1068 RECT focusRect = {-1, -1, -1, -1};
1070 HBRUSH hbrush, oldBrush = 0;
1072 if (descr->style & LBS_NOREDRAW) return 0;
1074 SetRect( &rect, 0, 0, descr->width, descr->height );
1075 if (descr->style & LBS_MULTICOLUMN)
1076 rect.right = rect.left + descr->column_width;
1077 else if (descr->horz_pos)
1079 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
1080 rect.right += descr->horz_pos;
1083 if (descr->font) oldFont = SelectObject( hdc, descr->font );
1084 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1085 (WPARAM)hdc, (LPARAM)descr->self );
1086 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1087 if (!IsWindowEnabled(descr->self)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1089 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1092 /* Special case for empty listbox: paint focus rect */
1093 rect.bottom = rect.top + descr->item_height;
1094 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1095 &rect, NULL, 0, NULL );
1096 LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, FALSE );
1097 rect.top = rect.bottom;
1100 /* Paint all the item, regarding the selection
1101 Focus state will be painted after */
1103 for (i = descr->top_item; i < descr->nb_items; i++)
1105 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1106 rect.bottom = rect.top + descr->item_height;
1108 rect.bottom = rect.top + descr->items[i].height;
1110 if (i == descr->focus_item)
1112 /* keep the focus rect, to paint the focus item after */
1113 focusRect.left = rect.left;
1114 focusRect.right = rect.right;
1115 focusRect.top = rect.top;
1116 focusRect.bottom = rect.bottom;
1118 LISTBOX_PaintItem( descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1119 rect.top = rect.bottom;
1121 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1123 if (!IS_OWNERDRAW(descr))
1125 /* Clear the bottom of the column */
1126 if (rect.top < descr->height)
1128 rect.bottom = descr->height;
1129 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1130 &rect, NULL, 0, NULL );
1134 /* Go to the next column */
1135 rect.left += descr->column_width;
1136 rect.right += descr->column_width;
1138 col_pos = descr->page_size - 1;
1143 if (rect.top >= descr->height) break;
1147 /* Paint the focus item now */
1148 if (focusRect.top != focusRect.bottom &&
1149 descr->caret_on && descr->in_focus)
1150 LISTBOX_PaintItem( descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1152 if (!IS_OWNERDRAW(descr))
1154 /* Clear the remainder of the client area */
1155 if (rect.top < descr->height)
1157 rect.bottom = descr->height;
1158 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1159 &rect, NULL, 0, NULL );
1161 if (rect.right < descr->width)
1163 rect.left = rect.right;
1164 rect.right = descr->width;
1166 rect.bottom = descr->height;
1167 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1168 &rect, NULL, 0, NULL );
1171 if (oldFont) SelectObject( hdc, oldFont );
1172 if (oldBrush) SelectObject( hdc, oldBrush );
1177 /***********************************************************************
1178 * LISTBOX_InvalidateItems
1180 * Invalidate all items from a given item. If the specified item is not
1181 * visible, nothing happens.
1183 static void LISTBOX_InvalidateItems( LB_DESCR *descr, INT index )
1187 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1189 if (descr->style & LBS_NOREDRAW)
1191 descr->style |= LBS_DISPLAYCHANGED;
1194 rect.bottom = descr->height;
1195 InvalidateRect( descr->self, &rect, TRUE );
1196 if (descr->style & LBS_MULTICOLUMN)
1198 /* Repaint the other columns */
1199 rect.left = rect.right;
1200 rect.right = descr->width;
1202 InvalidateRect( descr->self, &rect, TRUE );
1207 static void LISTBOX_InvalidateItemRect( LB_DESCR *descr, INT index )
1211 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1212 InvalidateRect( descr->self, &rect, TRUE );
1215 /***********************************************************************
1216 * LISTBOX_GetItemHeight
1218 static LRESULT LISTBOX_GetItemHeight( const LB_DESCR *descr, INT index )
1220 if (descr->style & LBS_OWNERDRAWVARIABLE && descr->nb_items > 0)
1222 if ((index < 0) || (index >= descr->nb_items))
1224 SetLastError(ERROR_INVALID_INDEX);
1227 return descr->items[index].height;
1229 else return descr->item_height;
1233 /***********************************************************************
1234 * LISTBOX_SetItemHeight
1236 static LRESULT LISTBOX_SetItemHeight( LB_DESCR *descr, INT index, INT height, BOOL repaint )
1238 if (height > MAXBYTE)
1241 if (!height) height = 1;
1243 if (descr->style & LBS_OWNERDRAWVARIABLE)
1245 if ((index < 0) || (index >= descr->nb_items))
1247 SetLastError(ERROR_INVALID_INDEX);
1250 TRACE("[%p]: item %d height = %d\n", descr->self, index, height );
1251 descr->items[index].height = height;
1252 LISTBOX_UpdateScroll( descr );
1254 LISTBOX_InvalidateItems( descr, index );
1256 else if (height != descr->item_height)
1258 TRACE("[%p]: new height = %d\n", descr->self, height );
1259 descr->item_height = height;
1260 LISTBOX_UpdatePage( descr );
1261 LISTBOX_UpdateScroll( descr );
1263 InvalidateRect( descr->self, 0, TRUE );
1269 /***********************************************************************
1270 * LISTBOX_SetHorizontalPos
1272 static void LISTBOX_SetHorizontalPos( LB_DESCR *descr, INT pos )
1276 if (pos > descr->horz_extent - descr->width)
1277 pos = descr->horz_extent - descr->width;
1278 if (pos < 0) pos = 0;
1279 if (!(diff = descr->horz_pos - pos)) return;
1280 TRACE("[%p]: new horz pos = %d\n", descr->self, pos );
1281 descr->horz_pos = pos;
1282 LISTBOX_UpdateScroll( descr );
1283 if (abs(diff) < descr->width)
1286 /* Invalidate the focused item so it will be repainted correctly */
1287 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
1288 InvalidateRect( descr->self, &rect, TRUE );
1289 ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
1290 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1293 InvalidateRect( descr->self, NULL, TRUE );
1297 /***********************************************************************
1298 * LISTBOX_SetHorizontalExtent
1300 static LRESULT LISTBOX_SetHorizontalExtent( LB_DESCR *descr, INT extent )
1302 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1304 if (extent <= 0) extent = 1;
1305 if (extent == descr->horz_extent) return LB_OKAY;
1306 TRACE("[%p]: new horz extent = %d\n", descr->self, extent );
1307 descr->horz_extent = extent;
1308 if (descr->horz_pos > extent - descr->width)
1309 LISTBOX_SetHorizontalPos( descr, extent - descr->width );
1311 LISTBOX_UpdateScroll( descr );
1316 /***********************************************************************
1317 * LISTBOX_SetColumnWidth
1319 static LRESULT LISTBOX_SetColumnWidth( LB_DESCR *descr, INT width)
1321 if (width == descr->column_width) return LB_OKAY;
1322 TRACE("[%p]: new column width = %d\n", descr->self, width );
1323 descr->column_width = width;
1324 LISTBOX_UpdatePage( descr );
1329 /***********************************************************************
1332 * Returns the item height.
1334 static INT LISTBOX_SetFont( LB_DESCR *descr, HFONT font )
1338 const char *alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1343 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE )))
1345 ERR("unable to get DC.\n" );
1348 if (font) oldFont = SelectObject( hdc, font );
1349 GetTextExtentPointA( hdc, alphabet, 52, &sz);
1350 if (oldFont) SelectObject( hdc, oldFont );
1351 ReleaseDC( descr->self, hdc );
1353 descr->avg_char_width = (sz.cx / 26 + 1) / 2;
1354 if (!IS_OWNERDRAW(descr))
1355 LISTBOX_SetItemHeight( descr, 0, sz.cy, FALSE );
1360 /***********************************************************************
1361 * LISTBOX_MakeItemVisible
1363 * Make sure that a given item is partially or fully visible.
1365 static void LISTBOX_MakeItemVisible( LB_DESCR *descr, INT index, BOOL fully )
1369 TRACE("current top item %d, index %d, fully %d\n", descr->top_item, index, fully);
1371 if (index <= descr->top_item) top = index;
1372 else if (descr->style & LBS_MULTICOLUMN)
1374 INT cols = descr->width;
1375 if (!fully) cols += descr->column_width - 1;
1376 if (cols >= descr->column_width) cols /= descr->column_width;
1378 if (index < descr->top_item + (descr->page_size * cols)) return;
1379 top = index - descr->page_size * (cols - 1);
1381 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1383 INT height = fully ? descr->items[index].height : 1;
1384 for (top = index; top > descr->top_item; top--)
1385 if ((height += descr->items[top-1].height) > descr->height) break;
1389 if (index < descr->top_item + descr->page_size) return;
1390 if (!fully && (index == descr->top_item + descr->page_size) &&
1391 (descr->height > (descr->page_size * descr->item_height))) return;
1392 top = index - descr->page_size + 1;
1394 LISTBOX_SetTopItem( descr, top, TRUE );
1397 /***********************************************************************
1398 * LISTBOX_SetCaretIndex
1401 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1404 static LRESULT LISTBOX_SetCaretIndex( LB_DESCR *descr, INT index, BOOL fully_visible )
1406 INT oldfocus = descr->focus_item;
1408 TRACE("old focus %d, index %d\n", oldfocus, index);
1410 if (descr->style & LBS_NOSEL) return LB_ERR;
1411 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1412 if (index == oldfocus) return LB_OKAY;
1414 LISTBOX_DrawFocusRect( descr, FALSE );
1415 descr->focus_item = index;
1417 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1418 LISTBOX_DrawFocusRect( descr, TRUE );
1424 /***********************************************************************
1425 * LISTBOX_SelectItemRange
1427 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1429 static LRESULT LISTBOX_SelectItemRange( LB_DESCR *descr, INT first,
1434 /* A few sanity checks */
1436 if (descr->style & LBS_NOSEL) return LB_ERR;
1437 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1439 if (!descr->nb_items) return LB_OKAY;
1441 if (last == -1 || last >= descr->nb_items) last = descr->nb_items - 1;
1442 if (first < 0) first = 0;
1443 if (last < first) return LB_OKAY;
1445 if (on) /* Turn selection on */
1447 for (i = first; i <= last; i++)
1449 if (descr->items[i].selected) continue;
1450 descr->items[i].selected = TRUE;
1451 LISTBOX_InvalidateItemRect(descr, i);
1454 else /* Turn selection off */
1456 for (i = first; i <= last; i++)
1458 if (!descr->items[i].selected) continue;
1459 descr->items[i].selected = FALSE;
1460 LISTBOX_InvalidateItemRect(descr, i);
1466 /***********************************************************************
1467 * LISTBOX_SetSelection
1469 static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index,
1470 BOOL on, BOOL send_notify )
1472 TRACE( "cur_sel=%d index=%d notify=%s\n",
1473 descr->selected_item, index, send_notify ? "YES" : "NO" );
1475 if (descr->style & LBS_NOSEL)
1477 descr->selected_item = index;
1480 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1481 if (descr->style & LBS_MULTIPLESEL)
1483 if (index == -1) /* Select all items */
1484 return LISTBOX_SelectItemRange( descr, 0, descr->nb_items, on );
1485 else /* Only one item */
1486 return LISTBOX_SelectItemRange( descr, index, index, on );
1490 INT oldsel = descr->selected_item;
1491 if (index == oldsel) return LB_OKAY;
1492 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1493 if (index != -1) descr->items[index].selected = TRUE;
1494 if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT );
1495 descr->selected_item = index;
1496 if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT );
1497 if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr,
1498 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1500 if( descr->lphc ) /* set selection change flag for parent combo */
1501 descr->lphc->wState |= CBF_SELCHANGE;
1507 /***********************************************************************
1510 * Change the caret position and extend the selection to the new caret.
1512 static void LISTBOX_MoveCaret( LB_DESCR *descr, INT index, BOOL fully_visible )
1514 TRACE("old focus %d, index %d\n", descr->focus_item, index);
1516 if ((index < 0) || (index >= descr->nb_items))
1519 /* Important, repaint needs to be done in this order if
1520 you want to mimic Windows behavior:
1521 1. Remove the focus and paint the item
1522 2. Remove the selection and paint the item(s)
1523 3. Set the selection and repaint the item(s)
1524 4. Set the focus to 'index' and repaint the item */
1526 /* 1. remove the focus and repaint the item */
1527 LISTBOX_DrawFocusRect( descr, FALSE );
1529 /* 2. then turn off the previous selection */
1530 /* 3. repaint the new selected item */
1531 if (descr->style & LBS_EXTENDEDSEL)
1533 if (descr->anchor_item != -1)
1535 INT first = min( index, descr->anchor_item );
1536 INT last = max( index, descr->anchor_item );
1538 LISTBOX_SelectItemRange( descr, 0, first - 1, FALSE );
1539 LISTBOX_SelectItemRange( descr, last + 1, -1, FALSE );
1540 LISTBOX_SelectItemRange( descr, first, last, TRUE );
1543 else if (!(descr->style & LBS_MULTIPLESEL))
1545 /* Set selection to new caret item */
1546 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
1549 /* 4. repaint the new item with the focus */
1550 descr->focus_item = index;
1551 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1552 LISTBOX_DrawFocusRect( descr, TRUE );
1556 /***********************************************************************
1557 * LISTBOX_InsertItem
1559 static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index,
1560 LPWSTR str, ULONG_PTR data )
1564 INT oldfocus = descr->focus_item;
1566 if (index == -1) index = descr->nb_items;
1567 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1568 if (!descr->items) max_items = 0;
1569 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1570 if (descr->nb_items == max_items)
1572 /* We need to grow the array */
1573 max_items += LB_ARRAY_GRANULARITY;
1575 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1576 max_items * sizeof(LB_ITEMDATA) );
1578 item = HeapAlloc( GetProcessHeap(), 0,
1579 max_items * sizeof(LB_ITEMDATA) );
1582 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1585 descr->items = item;
1588 /* Insert the item structure */
1590 item = &descr->items[index];
1591 if (index < descr->nb_items)
1592 RtlMoveMemory( item + 1, item,
1593 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1597 item->selected = FALSE;
1600 /* Get item height */
1602 if (descr->style & LBS_OWNERDRAWVARIABLE)
1604 MEASUREITEMSTRUCT mis;
1605 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1607 mis.CtlType = ODT_LISTBOX;
1610 mis.itemData = descr->items[index].data;
1611 mis.itemHeight = descr->item_height;
1612 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1613 item->height = mis.itemHeight ? mis.itemHeight : 1;
1614 TRACE("[%p]: measure item %d (%s) = %d\n",
1615 descr->self, index, str ? debugstr_w(str) : "", item->height );
1618 /* Repaint the items */
1620 LISTBOX_UpdateScroll( descr );
1621 LISTBOX_InvalidateItems( descr, index );
1623 /* Move selection and focused item */
1624 /* If listbox was empty, set focus to the first item */
1625 if (descr->nb_items == 1)
1626 LISTBOX_SetCaretIndex( descr, 0, FALSE );
1627 /* single select don't change selection index in win31 */
1628 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1630 descr->selected_item++;
1631 LISTBOX_SetSelection( descr, descr->selected_item-1, TRUE, FALSE );
1635 if (index <= descr->selected_item)
1637 descr->selected_item++;
1638 descr->focus_item = oldfocus; /* focus not changed */
1645 /***********************************************************************
1646 * LISTBOX_InsertString
1648 static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index, LPCWSTR str )
1650 LPWSTR new_str = NULL;
1654 if (HAS_STRINGS(descr))
1656 static const WCHAR empty_stringW[] = { 0 };
1657 if (!str) str = empty_stringW;
1658 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1660 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1663 strcpyW(new_str, str);
1665 else data = (ULONG_PTR)str;
1667 if (index == -1) index = descr->nb_items;
1668 if ((ret = LISTBOX_InsertItem( descr, index, new_str, data )) != 0)
1670 HeapFree( GetProcessHeap(), 0, new_str );
1674 TRACE("[%p]: added item %d %s\n",
1675 descr->self, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1680 /***********************************************************************
1681 * LISTBOX_DeleteItem
1683 * Delete the content of an item. 'index' must be a valid index.
1685 static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index )
1687 /* save the item data before it gets freed by LB_RESETCONTENT */
1688 ULONG_PTR item_data = descr->items[index].data;
1689 LPWSTR item_str = descr->items[index].str;
1691 if (!descr->nb_items)
1692 SendMessageW( descr->self, LB_RESETCONTENT, 0, 0 );
1694 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1695 * while Win95 sends it for all items with user data.
1696 * It's probably better to send it too often than not
1697 * often enough, so this is what we do here.
1699 if (IS_OWNERDRAW(descr) || item_data)
1701 DELETEITEMSTRUCT dis;
1702 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1704 dis.CtlType = ODT_LISTBOX;
1707 dis.hwndItem = descr->self;
1708 dis.itemData = item_data;
1709 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1711 if (HAS_STRINGS(descr))
1712 HeapFree( GetProcessHeap(), 0, item_str );
1716 /***********************************************************************
1717 * LISTBOX_RemoveItem
1719 * Remove an item from the listbox and delete its content.
1721 static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index )
1726 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1728 /* We need to invalidate the original rect instead of the updated one. */
1729 LISTBOX_InvalidateItems( descr, index );
1732 LISTBOX_DeleteItem( descr, index );
1734 if (!descr->nb_items) return LB_OKAY;
1736 /* Remove the item */
1738 item = &descr->items[index];
1739 if (index < descr->nb_items)
1740 RtlMoveMemory( item, item + 1,
1741 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1742 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1744 /* Shrink the item array if possible */
1746 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1747 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1749 max_items -= LB_ARRAY_GRANULARITY;
1750 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1751 max_items * sizeof(LB_ITEMDATA) );
1752 if (item) descr->items = item;
1754 /* Repaint the items */
1756 LISTBOX_UpdateScroll( descr );
1757 /* if we removed the scrollbar, reset the top of the list
1758 (correct for owner-drawn ???) */
1759 if (descr->nb_items == descr->page_size)
1760 LISTBOX_SetTopItem( descr, 0, TRUE );
1762 /* Move selection and focused item */
1763 if (!IS_MULTISELECT(descr))
1765 if (index == descr->selected_item)
1766 descr->selected_item = -1;
1767 else if (index < descr->selected_item)
1769 descr->selected_item--;
1770 if (ISWIN31) /* win 31 do not change the selected item number */
1771 LISTBOX_SetSelection( descr, descr->selected_item + 1, TRUE, FALSE);
1775 if (descr->focus_item >= descr->nb_items)
1777 descr->focus_item = descr->nb_items - 1;
1778 if (descr->focus_item < 0) descr->focus_item = 0;
1784 /***********************************************************************
1785 * LISTBOX_ResetContent
1787 static void LISTBOX_ResetContent( LB_DESCR *descr )
1791 for(i = descr->nb_items - 1; i>=0; i--) LISTBOX_DeleteItem( descr, i);
1792 HeapFree( GetProcessHeap(), 0, descr->items );
1793 descr->nb_items = 0;
1794 descr->top_item = 0;
1795 descr->selected_item = -1;
1796 descr->focus_item = 0;
1797 descr->anchor_item = -1;
1798 descr->items = NULL;
1802 /***********************************************************************
1805 static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count )
1809 if (HAS_STRINGS(descr))
1811 SetLastError(ERROR_SETCOUNT_ON_BAD_LB);
1815 /* FIXME: this is far from optimal... */
1816 if (count > descr->nb_items)
1818 while (count > descr->nb_items)
1819 if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0)
1822 else if (count < descr->nb_items)
1824 while (count < descr->nb_items)
1825 if ((ret = LISTBOX_RemoveItem( descr, (descr->nb_items - 1) )) < 0)
1832 /***********************************************************************
1835 static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib,
1836 LPCWSTR filespec, BOOL long_names )
1839 LRESULT ret = LB_OKAY;
1840 WIN32_FIND_DATAW entry;
1842 LRESULT maxinsert = LB_ERR;
1844 /* don't scan directory if we just want drives exclusively */
1845 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1846 /* scan directory */
1847 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1849 int le = GetLastError();
1850 if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR;
1857 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1859 static const WCHAR bracketW[] = { ']',0 };
1860 static const WCHAR dotW[] = { '.',0 };
1861 if (!(attrib & DDL_DIRECTORY) ||
1862 !strcmpW( entry.cFileName, dotW )) continue;
1864 if (!long_names && entry.cAlternateFileName[0])
1865 strcpyW( buffer + 1, entry.cAlternateFileName );
1867 strcpyW( buffer + 1, entry.cFileName );
1868 strcatW(buffer, bracketW);
1870 else /* not a directory */
1872 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1873 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1875 if ((attrib & DDL_EXCLUSIVE) &&
1876 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1879 if (!long_names && entry.cAlternateFileName[0])
1880 strcpyW( buffer, entry.cAlternateFileName );
1882 strcpyW( buffer, entry.cFileName );
1884 if (!long_names) CharLowerW( buffer );
1885 pos = LISTBOX_FindFileStrPos( descr, buffer );
1886 if ((ret = LISTBOX_InsertString( descr, pos, buffer )) < 0)
1888 if (ret <= maxinsert) maxinsert++; else maxinsert = ret;
1889 } while (FindNextFileW( handle, &entry ));
1890 FindClose( handle );
1898 if (attrib & DDL_DRIVES)
1900 WCHAR buffer[] = {'[','-','a','-',']',0};
1901 WCHAR root[] = {'A',':','\\',0};
1903 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1905 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1906 if ((ret = LISTBOX_InsertString( descr, -1, buffer )) < 0)
1915 /***********************************************************************
1916 * LISTBOX_HandleVScroll
1918 static LRESULT LISTBOX_HandleVScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1922 if (descr->style & LBS_MULTICOLUMN) return 0;
1926 LISTBOX_SetTopItem( descr, descr->top_item - 1, TRUE );
1929 LISTBOX_SetTopItem( descr, descr->top_item + 1, TRUE );
1932 LISTBOX_SetTopItem( descr, descr->top_item -
1933 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1936 LISTBOX_SetTopItem( descr, descr->top_item +
1937 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1939 case SB_THUMBPOSITION:
1940 LISTBOX_SetTopItem( descr, pos, TRUE );
1943 info.cbSize = sizeof(info);
1944 info.fMask = SIF_TRACKPOS;
1945 GetScrollInfo( descr->self, SB_VERT, &info );
1946 LISTBOX_SetTopItem( descr, info.nTrackPos, TRUE );
1949 LISTBOX_SetTopItem( descr, 0, TRUE );
1952 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1959 /***********************************************************************
1960 * LISTBOX_HandleHScroll
1962 static LRESULT LISTBOX_HandleHScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1967 if (descr->style & LBS_MULTICOLUMN)
1972 LISTBOX_SetTopItem( descr, descr->top_item-descr->page_size,
1976 LISTBOX_SetTopItem( descr, descr->top_item+descr->page_size,
1980 page = descr->width / descr->column_width;
1981 if (page < 1) page = 1;
1982 LISTBOX_SetTopItem( descr,
1983 descr->top_item - page * descr->page_size, TRUE );
1986 page = descr->width / descr->column_width;
1987 if (page < 1) page = 1;
1988 LISTBOX_SetTopItem( descr,
1989 descr->top_item + page * descr->page_size, TRUE );
1991 case SB_THUMBPOSITION:
1992 LISTBOX_SetTopItem( descr, pos*descr->page_size, TRUE );
1995 info.cbSize = sizeof(info);
1996 info.fMask = SIF_TRACKPOS;
1997 GetScrollInfo( descr->self, SB_VERT, &info );
1998 LISTBOX_SetTopItem( descr, info.nTrackPos*descr->page_size,
2002 LISTBOX_SetTopItem( descr, 0, TRUE );
2005 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
2009 else if (descr->horz_extent)
2014 LISTBOX_SetHorizontalPos( descr, descr->horz_pos - 1 );
2017 LISTBOX_SetHorizontalPos( descr, descr->horz_pos + 1 );
2020 LISTBOX_SetHorizontalPos( descr,
2021 descr->horz_pos - descr->width );
2024 LISTBOX_SetHorizontalPos( descr,
2025 descr->horz_pos + descr->width );
2027 case SB_THUMBPOSITION:
2028 LISTBOX_SetHorizontalPos( descr, pos );
2031 info.cbSize = sizeof(info);
2032 info.fMask = SIF_TRACKPOS;
2033 GetScrollInfo( descr->self, SB_HORZ, &info );
2034 LISTBOX_SetHorizontalPos( descr, info.nTrackPos );
2037 LISTBOX_SetHorizontalPos( descr, 0 );
2040 LISTBOX_SetHorizontalPos( descr,
2041 descr->horz_extent - descr->width );
2048 static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta )
2050 short gcWheelDelta = 0;
2051 UINT pulScrollLines = 3;
2053 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
2055 gcWheelDelta -= delta;
2057 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
2059 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
2060 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
2061 LISTBOX_SetTopItem( descr, descr->top_item + cLineScroll, TRUE );
2066 /***********************************************************************
2067 * LISTBOX_HandleLButtonDown
2069 static LRESULT LISTBOX_HandleLButtonDown( LB_DESCR *descr, DWORD keys, INT x, INT y )
2071 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
2073 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2074 descr->self, x, y, index, descr->focus_item);
2076 if (!descr->caret_on && (descr->in_focus)) return 0;
2078 if (!descr->in_focus)
2080 if( !descr->lphc ) SetFocus( descr->self );
2081 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
2084 if (index == -1) return 0;
2088 if (descr->style & LBS_NOTIFY )
2089 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
2090 MAKELPARAM( x, y ) );
2093 descr->captured = TRUE;
2094 SetCapture( descr->self );
2096 if (descr->style & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL))
2098 /* we should perhaps make sure that all items are deselected
2099 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2100 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2101 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2104 if (!(keys & MK_SHIFT)) descr->anchor_item = index;
2105 if (keys & MK_CONTROL)
2107 LISTBOX_SetCaretIndex( descr, index, FALSE );
2108 LISTBOX_SetSelection( descr, index,
2109 !descr->items[index].selected,
2110 (descr->style & LBS_NOTIFY) != 0);
2114 LISTBOX_MoveCaret( descr, index, FALSE );
2116 if (descr->style & LBS_EXTENDEDSEL)
2118 LISTBOX_SetSelection( descr, index,
2119 descr->items[index].selected,
2120 (descr->style & LBS_NOTIFY) != 0 );
2124 LISTBOX_SetSelection( descr, index,
2125 !descr->items[index].selected,
2126 (descr->style & LBS_NOTIFY) != 0 );
2132 descr->anchor_item = index;
2133 LISTBOX_MoveCaret( descr, index, FALSE );
2134 LISTBOX_SetSelection( descr, index,
2135 TRUE, (descr->style & LBS_NOTIFY) != 0 );
2140 if (GetWindowLongW( descr->self, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
2147 if (DragDetect( descr->self, pt ))
2148 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
2155 /*************************************************************************
2156 * LISTBOX_HandleLButtonDownCombo [Internal]
2158 * Process LButtonDown message for the ComboListBox
2161 * pWnd [I] The windows internal structure
2162 * pDescr [I] The ListBox internal structure
2163 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2164 * x [I] X Mouse Coordinate
2165 * y [I] Y Mouse Coordinate
2168 * 0 since we are processing the WM_LBUTTONDOWN Message
2171 * This function is only to be used when a ListBox is a ComboListBox
2174 static LRESULT LISTBOX_HandleLButtonDownCombo( LB_DESCR *descr, UINT msg, DWORD keys, INT x, INT y)
2176 RECT clientRect, screenRect;
2182 GetClientRect(descr->self, &clientRect);
2184 if(PtInRect(&clientRect, mousePos))
2186 /* MousePos is in client, resume normal processing */
2187 if (msg == WM_LBUTTONDOWN)
2189 descr->lphc->droppedIndex = descr->nb_items ? descr->selected_item : -1;
2190 return LISTBOX_HandleLButtonDown( descr, keys, x, y);
2192 else if (descr->style & LBS_NOTIFY)
2193 SEND_NOTIFICATION( descr, LBN_DBLCLK );
2197 POINT screenMousePos;
2198 HWND hWndOldCapture;
2200 /* Check the Non-Client Area */
2201 screenMousePos = mousePos;
2202 hWndOldCapture = GetCapture();
2204 GetWindowRect(descr->self, &screenRect);
2205 ClientToScreen(descr->self, &screenMousePos);
2207 if(!PtInRect(&screenRect, screenMousePos))
2209 LISTBOX_SetCaretIndex( descr, descr->lphc->droppedIndex, FALSE );
2210 LISTBOX_SetSelection( descr, descr->lphc->droppedIndex, FALSE, FALSE );
2211 COMBO_FlipListbox( descr->lphc, FALSE, FALSE );
2215 /* Check to see the NC is a scrollbar */
2217 LONG style = GetWindowLongW( descr->self, GWL_STYLE );
2218 /* Check Vertical scroll bar */
2219 if (style & WS_VSCROLL)
2221 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2222 if (PtInRect( &clientRect, mousePos ))
2223 nHitTestType = HTVSCROLL;
2225 /* Check horizontal scroll bar */
2226 if (style & WS_HSCROLL)
2228 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2229 if (PtInRect( &clientRect, mousePos ))
2230 nHitTestType = HTHSCROLL;
2232 /* Windows sends this message when a scrollbar is clicked
2235 if(nHitTestType != 0)
2237 SendMessageW(descr->self, WM_NCLBUTTONDOWN, nHitTestType,
2238 MAKELONG(screenMousePos.x, screenMousePos.y));
2240 /* Resume the Capture after scrolling is complete
2242 if(hWndOldCapture != 0)
2243 SetCapture(hWndOldCapture);
2249 /***********************************************************************
2250 * LISTBOX_HandleLButtonUp
2252 static LRESULT LISTBOX_HandleLButtonUp( LB_DESCR *descr )
2254 if (LISTBOX_Timer != LB_TIMER_NONE)
2255 KillSystemTimer( descr->self, LB_TIMER_ID );
2256 LISTBOX_Timer = LB_TIMER_NONE;
2257 if (descr->captured)
2259 descr->captured = FALSE;
2260 if (GetCapture() == descr->self) ReleaseCapture();
2261 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2262 SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2268 /***********************************************************************
2269 * LISTBOX_HandleTimer
2271 * Handle scrolling upon a timer event.
2272 * Return TRUE if scrolling should continue.
2274 static LRESULT LISTBOX_HandleTimer( LB_DESCR *descr, INT index, TIMER_DIRECTION dir )
2279 if (descr->top_item) index = descr->top_item - 1;
2283 if (descr->top_item) index -= descr->page_size;
2286 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2287 if (index == descr->focus_item) index++;
2288 if (index >= descr->nb_items) index = descr->nb_items - 1;
2290 case LB_TIMER_RIGHT:
2291 if (index + descr->page_size < descr->nb_items)
2292 index += descr->page_size;
2297 if (index == descr->focus_item) return FALSE;
2298 LISTBOX_MoveCaret( descr, index, FALSE );
2303 /***********************************************************************
2304 * LISTBOX_HandleSystemTimer
2306 * WM_SYSTIMER handler.
2308 static LRESULT LISTBOX_HandleSystemTimer( LB_DESCR *descr )
2310 if (!LISTBOX_HandleTimer( descr, descr->focus_item, LISTBOX_Timer ))
2312 KillSystemTimer( descr->self, LB_TIMER_ID );
2313 LISTBOX_Timer = LB_TIMER_NONE;
2319 /***********************************************************************
2320 * LISTBOX_HandleMouseMove
2322 * WM_MOUSEMOVE handler.
2324 static void LISTBOX_HandleMouseMove( LB_DESCR *descr,
2328 TIMER_DIRECTION dir = LB_TIMER_NONE;
2330 if (!descr->captured) return;
2332 if (descr->style & LBS_MULTICOLUMN)
2335 else if (y >= descr->item_height * descr->page_size)
2336 y = descr->item_height * descr->page_size - 1;
2340 dir = LB_TIMER_LEFT;
2343 else if (x >= descr->width)
2345 dir = LB_TIMER_RIGHT;
2346 x = descr->width - 1;
2351 if (y < 0) dir = LB_TIMER_UP; /* above */
2352 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2355 index = LISTBOX_GetItemFromPoint( descr, x, y );
2356 if (index == -1) index = descr->focus_item;
2357 if (!LISTBOX_HandleTimer( descr, index, dir )) dir = LB_TIMER_NONE;
2359 /* Start/stop the system timer */
2361 if (dir != LB_TIMER_NONE)
2362 SetSystemTimer( descr->self, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2363 else if (LISTBOX_Timer != LB_TIMER_NONE)
2364 KillSystemTimer( descr->self, LB_TIMER_ID );
2365 LISTBOX_Timer = dir;
2369 /***********************************************************************
2370 * LISTBOX_HandleKeyDown
2372 static LRESULT LISTBOX_HandleKeyDown( LB_DESCR *descr, DWORD key )
2375 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2376 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2377 bForceSelection = FALSE; /* only for single select list */
2379 if (descr->style & LBS_WANTKEYBOARDINPUT)
2381 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2382 MAKEWPARAM(LOWORD(key), descr->focus_item),
2383 (LPARAM)descr->self );
2384 if (caret == -2) return 0;
2386 if (caret == -1) switch(key)
2389 if (descr->style & LBS_MULTICOLUMN)
2391 bForceSelection = FALSE;
2392 if (descr->focus_item >= descr->page_size)
2393 caret = descr->focus_item - descr->page_size;
2398 caret = descr->focus_item - 1;
2399 if (caret < 0) caret = 0;
2402 if (descr->style & LBS_MULTICOLUMN)
2404 bForceSelection = FALSE;
2405 if (descr->focus_item + descr->page_size < descr->nb_items)
2406 caret = descr->focus_item + descr->page_size;
2411 caret = descr->focus_item + 1;
2412 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2416 if (descr->style & LBS_MULTICOLUMN)
2418 INT page = descr->width / descr->column_width;
2419 if (page < 1) page = 1;
2420 caret = descr->focus_item - (page * descr->page_size) + 1;
2422 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2423 if (caret < 0) caret = 0;
2426 if (descr->style & LBS_MULTICOLUMN)
2428 INT page = descr->width / descr->column_width;
2429 if (page < 1) page = 1;
2430 caret = descr->focus_item + (page * descr->page_size) - 1;
2432 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2433 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2439 caret = descr->nb_items - 1;
2442 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2443 else if (descr->style & LBS_MULTIPLESEL)
2445 LISTBOX_SetSelection( descr, descr->focus_item,
2446 !descr->items[descr->focus_item].selected,
2447 (descr->style & LBS_NOTIFY) != 0 );
2451 bForceSelection = FALSE;
2453 if (bForceSelection) /* focused item is used instead of key */
2454 caret = descr->focus_item;
2457 if (((descr->style & LBS_EXTENDEDSEL) &&
2458 !(GetKeyState( VK_SHIFT ) & 0x8000)) ||
2459 !IS_MULTISELECT(descr))
2460 descr->anchor_item = caret;
2461 LISTBOX_MoveCaret( descr, caret, TRUE );
2463 if (descr->style & LBS_MULTIPLESEL)
2464 descr->selected_item = caret;
2466 LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2467 if (descr->style & LBS_NOTIFY)
2469 if (descr->lphc && IsWindowVisible( descr->self ))
2471 /* make sure that combo parent doesn't hide us */
2472 descr->lphc->wState |= CBF_NOROLLUP;
2474 if (descr->nb_items) SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2481 /***********************************************************************
2482 * LISTBOX_HandleChar
2484 static LRESULT LISTBOX_HandleChar( LB_DESCR *descr, WCHAR charW )
2492 if (descr->style & LBS_WANTKEYBOARDINPUT)
2494 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2495 MAKEWPARAM(charW, descr->focus_item),
2496 (LPARAM)descr->self );
2497 if (caret == -2) return 0;
2500 caret = LISTBOX_FindString( descr, descr->focus_item, str, FALSE);
2503 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2504 LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2505 LISTBOX_MoveCaret( descr, caret, TRUE );
2506 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2507 SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2513 /***********************************************************************
2516 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2519 MEASUREITEMSTRUCT mis;
2522 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2525 GetClientRect( hwnd, &rect );
2527 descr->owner = GetParent( descr->self );
2528 descr->style = GetWindowLongW( descr->self, GWL_STYLE );
2529 descr->width = rect.right - rect.left;
2530 descr->height = rect.bottom - rect.top;
2531 descr->items = NULL;
2532 descr->nb_items = 0;
2533 descr->top_item = 0;
2534 descr->selected_item = -1;
2535 descr->focus_item = 0;
2536 descr->anchor_item = -1;
2537 descr->item_height = 1;
2538 descr->page_size = 1;
2539 descr->column_width = 150;
2540 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0;
2541 descr->horz_pos = 0;
2544 descr->caret_on = lphc ? FALSE : TRUE;
2545 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2546 descr->in_focus = FALSE;
2547 descr->captured = FALSE;
2549 descr->locale = GetUserDefaultLCID();
2552 if (is_old_app(descr) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2554 /* Win95 document "List Box Differences" from MSDN:
2555 If a list box in a version 3.x application has either the
2556 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2557 horizontal and vertical scroll bars.
2559 descr->style |= WS_VSCROLL | WS_HSCROLL;
2564 TRACE("[%p]: resetting owner %p -> %p\n", descr->self, descr->owner, lphc->self );
2565 descr->owner = lphc->self;
2568 SetWindowLongPtrW( descr->self, 0, (LONG_PTR)descr );
2570 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2572 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2573 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2574 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2575 descr->item_height = LISTBOX_SetFont( descr, 0 );
2577 if (descr->style & LBS_OWNERDRAWFIXED)
2579 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2581 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2582 descr->item_height = lphc->fixedOwnerDrawHeight;
2586 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
2587 mis.CtlType = ODT_LISTBOX;
2592 mis.itemHeight = descr->item_height;
2593 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2594 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2598 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2603 /***********************************************************************
2606 static BOOL LISTBOX_Destroy( LB_DESCR *descr )
2608 LISTBOX_ResetContent( descr );
2609 SetWindowLongPtrW( descr->self, 0, 0 );
2610 HeapFree( GetProcessHeap(), 0, descr );
2615 /***********************************************************************
2616 * ListBoxWndProc_common
2618 static LRESULT ListBoxWndProc_common( HWND hwnd, UINT msg,
2619 WPARAM wParam, LPARAM lParam, BOOL unicode )
2621 LB_DESCR *descr = (LB_DESCR *)GetWindowLongPtrW( hwnd, 0 );
2622 LPHEADCOMBO lphc = 0;
2627 if (!IsWindow(hwnd)) return 0;
2629 if (msg == WM_CREATE)
2631 CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam;
2632 if (lpcs->style & LBS_COMBOBOX) lphc = lpcs->lpCreateParams;
2633 if (!LISTBOX_Create( hwnd, lphc )) return -1;
2634 TRACE("creating hwnd %p descr %p\n", hwnd, (void *)GetWindowLongPtrW( hwnd, 0 ) );
2637 /* Ignore all other messages before we get a WM_CREATE */
2638 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2639 DefWindowProcA( hwnd, msg, wParam, lParam );
2641 if (descr->style & LBS_COMBOBOX) lphc = descr->lphc;
2643 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2644 descr->self, SPY_GetMsgName(msg, descr->self), wParam, lParam );
2648 case LB_RESETCONTENT16:
2649 case LB_RESETCONTENT:
2650 LISTBOX_ResetContent( descr );
2651 LISTBOX_UpdateScroll( descr );
2652 InvalidateRect( descr->self, NULL, TRUE );
2655 case LB_ADDSTRING16:
2656 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2662 if(unicode || !HAS_STRINGS(descr))
2663 textW = (LPWSTR)lParam;
2666 LPSTR textA = (LPSTR)lParam;
2667 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2668 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2669 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2673 wParam = LISTBOX_FindStringPos( descr, textW, FALSE );
2674 ret = LISTBOX_InsertString( descr, wParam, textW );
2675 if (!unicode && HAS_STRINGS(descr))
2676 HeapFree(GetProcessHeap(), 0, textW);
2680 case LB_INSERTSTRING16:
2681 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2682 wParam = (INT)(INT16)wParam;
2684 case LB_INSERTSTRING:
2688 if(unicode || !HAS_STRINGS(descr))
2689 textW = (LPWSTR)lParam;
2692 LPSTR textA = (LPSTR)lParam;
2693 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2694 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2695 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2699 ret = LISTBOX_InsertString( descr, wParam, textW );
2700 if(!unicode && HAS_STRINGS(descr))
2701 HeapFree(GetProcessHeap(), 0, textW);
2706 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2712 if(unicode || !HAS_STRINGS(descr))
2713 textW = (LPWSTR)lParam;
2716 LPSTR textA = (LPSTR)lParam;
2717 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2718 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2719 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2723 wParam = LISTBOX_FindFileStrPos( descr, textW );
2724 ret = LISTBOX_InsertString( descr, wParam, textW );
2725 if(!unicode && HAS_STRINGS(descr))
2726 HeapFree(GetProcessHeap(), 0, textW);
2730 case LB_DELETESTRING16:
2731 case LB_DELETESTRING:
2732 if (LISTBOX_RemoveItem( descr, wParam) != LB_ERR)
2733 return descr->nb_items;
2736 SetLastError(ERROR_INVALID_INDEX);
2740 case LB_GETITEMDATA16:
2741 case LB_GETITEMDATA:
2742 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2744 SetLastError(ERROR_INVALID_INDEX);
2747 return descr->items[wParam].data;
2749 case LB_SETITEMDATA16:
2750 case LB_SETITEMDATA:
2751 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2753 SetLastError(ERROR_INVALID_INDEX);
2756 descr->items[wParam].data = lParam;
2757 /* undocumented: returns TRUE, not LB_OKAY (0) */
2762 return descr->nb_items;
2765 lParam = (LPARAM)MapSL(lParam);
2768 return LISTBOX_GetText( descr, wParam, (LPWSTR)lParam, unicode );
2770 case LB_GETTEXTLEN16:
2773 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2775 SetLastError(ERROR_INVALID_INDEX);
2778 if (!HAS_STRINGS(descr)) return sizeof(DWORD);
2779 if (unicode) return strlenW( descr->items[wParam].str );
2780 return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
2781 strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
2783 case LB_GETCURSEL16:
2785 if (descr->nb_items == 0)
2787 if (!IS_MULTISELECT(descr))
2788 return descr->selected_item;
2789 if (descr->selected_item != -1)
2790 return descr->selected_item;
2791 return descr->focus_item;
2792 /* otherwise, if the user tries to move the selection with the */
2793 /* arrow keys, we will give the application something to choke on */
2794 case LB_GETTOPINDEX16:
2795 case LB_GETTOPINDEX:
2796 return descr->top_item;
2798 case LB_GETITEMHEIGHT16:
2799 case LB_GETITEMHEIGHT:
2800 return LISTBOX_GetItemHeight( descr, wParam );
2802 case LB_SETITEMHEIGHT16:
2803 lParam = LOWORD(lParam);
2805 case LB_SETITEMHEIGHT:
2806 return LISTBOX_SetItemHeight( descr, wParam, lParam, TRUE );
2808 case LB_ITEMFROMPOINT:
2815 /* The hiword of the return value is not a client area
2816 hittest as suggested by MSDN, but rather a hittest on
2817 the returned listbox item. */
2819 if(descr->nb_items == 0)
2820 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
2822 pt.x = (short)LOWORD(lParam);
2823 pt.y = (short)HIWORD(lParam);
2825 SetRect(&rect, 0, 0, descr->width, descr->height);
2827 if(!PtInRect(&rect, pt))
2829 pt.x = min(pt.x, rect.right - 1);
2830 pt.x = max(pt.x, 0);
2831 pt.y = min(pt.y, rect.bottom - 1);
2832 pt.y = max(pt.y, 0);
2836 index = LISTBOX_GetItemFromPoint(descr, pt.x, pt.y);
2840 index = descr->nb_items - 1;
2843 return MAKELONG(index, hit ? 0 : 1);
2846 case LB_SETCARETINDEX16:
2847 case LB_SETCARETINDEX:
2848 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2849 if (LISTBOX_SetCaretIndex( descr, wParam, !lParam ) == LB_ERR)
2856 case LB_GETCARETINDEX16:
2857 case LB_GETCARETINDEX:
2858 return descr->focus_item;
2860 case LB_SETTOPINDEX16:
2861 case LB_SETTOPINDEX:
2862 return LISTBOX_SetTopItem( descr, wParam, TRUE );
2864 case LB_SETCOLUMNWIDTH16:
2865 case LB_SETCOLUMNWIDTH:
2866 return LISTBOX_SetColumnWidth( descr, wParam );
2868 case LB_GETITEMRECT16:
2871 RECT16 *r16 = MapSL(lParam);
2872 ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2873 r16->left = rect.left;
2874 r16->top = rect.top;
2875 r16->right = rect.right;
2876 r16->bottom = rect.bottom;
2880 case LB_GETITEMRECT:
2881 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2883 case LB_FINDSTRING16:
2884 wParam = (INT)(INT16)wParam;
2885 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2891 if(unicode || !HAS_STRINGS(descr))
2892 textW = (LPWSTR)lParam;
2895 LPSTR textA = (LPSTR)lParam;
2896 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2897 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2898 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2900 ret = LISTBOX_FindString( descr, wParam, textW, FALSE );
2901 if(!unicode && HAS_STRINGS(descr))
2902 HeapFree(GetProcessHeap(), 0, textW);
2906 case LB_FINDSTRINGEXACT16:
2907 wParam = (INT)(INT16)wParam;
2908 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2910 case LB_FINDSTRINGEXACT:
2914 if(unicode || !HAS_STRINGS(descr))
2915 textW = (LPWSTR)lParam;
2918 LPSTR textA = (LPSTR)lParam;
2919 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2920 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2921 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2923 ret = LISTBOX_FindString( descr, wParam, textW, TRUE );
2924 if(!unicode && HAS_STRINGS(descr))
2925 HeapFree(GetProcessHeap(), 0, textW);
2929 case LB_SELECTSTRING16:
2930 wParam = (INT)(INT16)wParam;
2931 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2933 case LB_SELECTSTRING:
2938 if(HAS_STRINGS(descr))
2939 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2940 debugstr_a((LPSTR)lParam));
2941 if(unicode || !HAS_STRINGS(descr))
2942 textW = (LPWSTR)lParam;
2945 LPSTR textA = (LPSTR)lParam;
2946 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2947 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2948 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2950 index = LISTBOX_FindString( descr, wParam, textW, FALSE );
2951 if(!unicode && HAS_STRINGS(descr))
2952 HeapFree(GetProcessHeap(), 0, textW);
2953 if (index != LB_ERR)
2955 LISTBOX_MoveCaret( descr, index, TRUE );
2956 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
2962 wParam = (INT)(INT16)wParam;
2965 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2967 return descr->items[wParam].selected;
2970 lParam = (INT)(INT16)lParam;
2973 return LISTBOX_SetSelection( descr, lParam, wParam, FALSE );
2975 case LB_SETCURSEL16:
2976 wParam = (INT)(INT16)wParam;
2979 if (IS_MULTISELECT(descr)) return LB_ERR;
2980 LISTBOX_SetCaretIndex( descr, wParam, FALSE );
2981 ret = LISTBOX_SetSelection( descr, wParam, TRUE, FALSE );
2982 if (ret != LB_ERR) ret = descr->selected_item;
2985 case LB_GETSELCOUNT16:
2986 case LB_GETSELCOUNT:
2987 return LISTBOX_GetSelCount( descr );
2989 case LB_GETSELITEMS16:
2990 return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2992 case LB_GETSELITEMS:
2993 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2995 case LB_SELITEMRANGE16:
2996 case LB_SELITEMRANGE:
2997 if (LOWORD(lParam) <= HIWORD(lParam))
2998 return LISTBOX_SelectItemRange( descr, LOWORD(lParam),
2999 HIWORD(lParam), wParam );
3001 return LISTBOX_SelectItemRange( descr, HIWORD(lParam),
3002 LOWORD(lParam), wParam );
3004 case LB_SELITEMRANGEEX16:
3005 case LB_SELITEMRANGEEX:
3006 if ((INT)lParam >= (INT)wParam)
3007 return LISTBOX_SelectItemRange( descr, wParam, lParam, TRUE );
3009 return LISTBOX_SelectItemRange( descr, lParam, wParam, FALSE);
3011 case LB_GETHORIZONTALEXTENT16:
3012 case LB_GETHORIZONTALEXTENT:
3013 return descr->horz_extent;
3015 case LB_SETHORIZONTALEXTENT16:
3016 case LB_SETHORIZONTALEXTENT:
3017 return LISTBOX_SetHorizontalExtent( descr, wParam );
3019 case LB_GETANCHORINDEX16:
3020 case LB_GETANCHORINDEX:
3021 return descr->anchor_item;
3023 case LB_SETANCHORINDEX16:
3024 wParam = (INT)(INT16)wParam;
3026 case LB_SETANCHORINDEX:
3027 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
3029 SetLastError(ERROR_INVALID_INDEX);
3032 descr->anchor_item = (INT)wParam;
3036 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
3037 * be set automatically (this is different in Win32) */
3038 if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
3039 lParam = (LPARAM)MapSL(lParam);
3046 textW = (LPWSTR)lParam;
3049 LPSTR textA = (LPSTR)lParam;
3050 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
3051 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
3052 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
3054 ret = LISTBOX_Directory( descr, wParam, textW, msg == LB_DIR );
3056 HeapFree(GetProcessHeap(), 0, textW);
3061 return descr->locale;
3066 if (!IsValidLocale((LCID)wParam, LCID_INSTALLED))
3068 ret = descr->locale;
3069 descr->locale = (LCID)wParam;
3073 case LB_INITSTORAGE:
3074 return LISTBOX_InitStorage( descr, wParam );
3077 return LISTBOX_SetCount( descr, (INT)wParam );
3079 case LB_SETTABSTOPS16:
3080 return LISTBOX_SetTabStops( descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
3082 case LB_SETTABSTOPS:
3083 return LISTBOX_SetTabStops( descr, wParam, (LPINT)lParam, FALSE );
3087 if (descr->caret_on)
3089 descr->caret_on = TRUE;
3090 if ((descr->focus_item != -1) && (descr->in_focus))
3091 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3096 if (!descr->caret_on)
3098 descr->caret_on = FALSE;
3099 if ((descr->focus_item != -1) && (descr->in_focus))
3100 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3103 case LB_GETLISTBOXINFO:
3104 FIXME("LB_GETLISTBOXINFO: stub!\n");
3108 return LISTBOX_Destroy( descr );
3111 InvalidateRect( descr->self, NULL, TRUE );
3115 LISTBOX_SetRedraw( descr, wParam != 0 );
3119 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3121 case WM_PRINTCLIENT:
3125 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( descr->self, &ps );
3126 ret = LISTBOX_Paint( descr, hdc );
3127 if( !wParam ) EndPaint( descr->self, &ps );
3131 LISTBOX_UpdateSize( descr );
3134 return (LRESULT)descr->font;
3136 LISTBOX_SetFont( descr, (HFONT)wParam );
3137 if (lParam) InvalidateRect( descr->self, 0, TRUE );
3140 descr->in_focus = TRUE;
3141 descr->caret_on = TRUE;
3142 if (descr->focus_item != -1)
3143 LISTBOX_DrawFocusRect( descr, TRUE );
3144 SEND_NOTIFICATION( descr, LBN_SETFOCUS );
3147 descr->in_focus = FALSE;
3148 if ((descr->focus_item != -1) && descr->caret_on)
3149 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3150 SEND_NOTIFICATION( descr, LBN_KILLFOCUS );
3153 return LISTBOX_HandleHScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3155 return LISTBOX_HandleVScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3157 if (wParam & (MK_SHIFT | MK_CONTROL))
3158 return DefWindowProcW( descr->self, msg, wParam, lParam );
3159 return LISTBOX_HandleMouseWheel( descr, (SHORT)HIWORD(wParam) );
3160 case WM_LBUTTONDOWN:
3162 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3163 (INT16)LOWORD(lParam),
3164 (INT16)HIWORD(lParam) );
3165 return LISTBOX_HandleLButtonDown( descr, wParam,
3166 (INT16)LOWORD(lParam),
3167 (INT16)HIWORD(lParam) );
3168 case WM_LBUTTONDBLCLK:
3170 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3171 (INT16)LOWORD(lParam),
3172 (INT16)HIWORD(lParam) );
3173 if (descr->style & LBS_NOTIFY)
3174 SEND_NOTIFICATION( descr, LBN_DBLCLK );
3177 if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) )
3179 BOOL captured = descr->captured;
3183 mousePos.x = (INT16)LOWORD(lParam);
3184 mousePos.y = (INT16)HIWORD(lParam);
3187 * If we are in a dropdown combobox, we simulate that
3188 * the mouse is captured to show the tracking of the item.
3190 if (GetClientRect(descr->self, &clientRect) && PtInRect( &clientRect, mousePos ))
3191 descr->captured = TRUE;
3193 LISTBOX_HandleMouseMove( descr, mousePos.x, mousePos.y);
3195 descr->captured = captured;
3197 else if (GetCapture() == descr->self)
3199 LISTBOX_HandleMouseMove( descr, (INT16)LOWORD(lParam),
3200 (INT16)HIWORD(lParam) );
3210 * If the mouse button "up" is not in the listbox,
3211 * we make sure there is no selection by re-selecting the
3212 * item that was selected when the listbox was made visible.
3214 mousePos.x = (INT16)LOWORD(lParam);
3215 mousePos.y = (INT16)HIWORD(lParam);
3217 GetClientRect(descr->self, &clientRect);
3220 * When the user clicks outside the combobox and the focus
3221 * is lost, the owning combobox will send a fake buttonup with
3222 * 0xFFFFFFF as the mouse location, we must also revert the
3223 * selection to the original selection.
3225 if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) )
3226 LISTBOX_MoveCaret( descr, lphc->droppedIndex, FALSE );
3228 return LISTBOX_HandleLButtonUp( descr );
3230 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3232 /* for some reason Windows makes it possible to
3233 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3235 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3236 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3237 && (wParam == VK_DOWN || wParam == VK_UP)) )
3239 COMBO_FlipListbox( lphc, FALSE, FALSE );
3243 return LISTBOX_HandleKeyDown( descr, wParam );
3248 charW = (WCHAR)wParam;
3251 CHAR charA = (CHAR)wParam;
3252 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
3254 return LISTBOX_HandleChar( descr, charW );
3257 return LISTBOX_HandleSystemTimer( descr );
3259 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
3262 HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
3263 wParam, (LPARAM)descr->self );
3264 TRACE("hbrush = %p\n", hbrush);
3266 hbrush = GetSysColorBrush(COLOR_WINDOW);
3269 GetClientRect(descr->self, &rect);
3270 FillRect((HDC)wParam, &rect, hbrush);
3275 if( lphc ) return 0;
3276 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3277 SendMessageA( descr->owner, msg, wParam, lParam );
3280 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3289 if ((msg >= WM_USER) && (msg < 0xc000))
3290 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3291 hwnd, msg, wParam, lParam );
3294 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3295 DefWindowProcA( hwnd, msg, wParam, lParam );
3298 /***********************************************************************
3301 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3303 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3306 /***********************************************************************
3309 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3311 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE );