4 * Copyright 1996 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * This code was audited for completeness against the documented features
23 * of Comctl32.dll version 6.0 on Oct. 9, 2004, by Dimitrie O. Paun.
25 * Unless otherwise noted, we believe this code to be complete, as per
26 * the specification mentioned above.
27 * If you discover missing features, or bugs, please note them below.
42 #include "wine/winuser16.h"
43 #include "wine/winbase16.h"
44 #include "wine/unicode.h"
45 #include "user_private.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 DWORD 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( LB_DESCR *descr, INT index, RECT *rect );
137 /*********************************************************************
138 * listbox class descriptor
140 const struct builtin_class_descr LISTBOX_builtin_class =
142 "ListBox", /* name */
143 CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
144 ListBoxWndProcA, /* procA */
145 ListBoxWndProcW, /* procW */
146 sizeof(LB_DESCR *), /* extra */
147 IDC_ARROW, /* cursor */
152 /*********************************************************************
153 * combolbox class descriptor
155 const struct builtin_class_descr COMBOLBOX_builtin_class =
157 "ComboLBox", /* name */
158 CS_DBLCLKS | CS_SAVEBITS, /* style */
159 ListBoxWndProcA, /* procA */
160 ListBoxWndProcW, /* procW */
161 sizeof(LB_DESCR *), /* extra */
162 IDC_ARROW, /* cursor */
167 /* check whether app is a Win 3.1 app */
168 inline static BOOL is_old_app( LB_DESCR *descr )
170 return (GetExpWinVer16( GetWindowLongPtrW(descr->self, GWLP_HINSTANCE) ) & 0xFF00 ) == 0x0300;
174 /***********************************************************************
175 * LISTBOX_GetCurrentPageSize
177 * Return the current page size
179 static INT LISTBOX_GetCurrentPageSize( LB_DESCR *descr )
182 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
183 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
185 if ((height += descr->items[i].height) > descr->height) break;
187 if (i == descr->top_item) return 1;
188 else return i - descr->top_item;
192 /***********************************************************************
193 * LISTBOX_GetMaxTopIndex
195 * Return the maximum possible index for the top of the listbox.
197 static INT LISTBOX_GetMaxTopIndex( LB_DESCR *descr )
201 if (descr->style & LBS_OWNERDRAWVARIABLE)
203 page = descr->height;
204 for (max = descr->nb_items - 1; max >= 0; max--)
205 if ((page -= descr->items[max].height) < 0) break;
206 if (max < descr->nb_items - 1) max++;
208 else if (descr->style & LBS_MULTICOLUMN)
210 if ((page = descr->width / descr->column_width) < 1) page = 1;
211 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
212 max = (max - page) * descr->page_size;
216 max = descr->nb_items - descr->page_size;
218 if (max < 0) max = 0;
223 /***********************************************************************
224 * LISTBOX_UpdateScroll
226 * Update the scrollbars. Should be called whenever the content
227 * of the listbox changes.
229 static void LISTBOX_UpdateScroll( LB_DESCR *descr )
233 /* Check the listbox scroll bar flags individually before we call
234 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
235 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
236 scroll bar when we do not need one.
237 if (!(descr->style & WS_VSCROLL)) return;
240 /* It is important that we check descr->style, and not wnd->dwStyle,
241 for WS_VSCROLL, as the former is exactly the one passed in
242 argument to CreateWindow.
243 In Windows (and from now on in Wine :) a listbox created
244 with such a style (no WS_SCROLL) does not update
245 the scrollbar with listbox-related data, thus letting
246 the programmer use it for his/her own purposes. */
248 if (descr->style & LBS_NOREDRAW) return;
249 info.cbSize = sizeof(info);
251 if (descr->style & LBS_MULTICOLUMN)
254 info.nMax = (descr->nb_items - 1) / descr->page_size;
255 info.nPos = descr->top_item / descr->page_size;
256 info.nPage = descr->width / descr->column_width;
257 if (info.nPage < 1) info.nPage = 1;
258 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
259 if (descr->style & LBS_DISABLENOSCROLL)
260 info.fMask |= SIF_DISABLENOSCROLL;
261 if (descr->style & WS_HSCROLL)
262 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
264 info.fMask = SIF_RANGE;
265 if (descr->style & WS_VSCROLL)
266 SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
271 info.nMax = descr->nb_items - 1;
272 info.nPos = descr->top_item;
273 info.nPage = LISTBOX_GetCurrentPageSize( descr );
274 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
275 if (descr->style & LBS_DISABLENOSCROLL)
276 info.fMask |= SIF_DISABLENOSCROLL;
277 if (descr->style & WS_VSCROLL)
278 SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
280 if (descr->horz_extent)
283 info.nMax = descr->horz_extent - 1;
284 info.nPos = descr->horz_pos;
285 info.nPage = descr->width;
286 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
287 if (descr->style & LBS_DISABLENOSCROLL)
288 info.fMask |= SIF_DISABLENOSCROLL;
289 if (descr->style & WS_HSCROLL)
290 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
296 /***********************************************************************
299 * Set the top item of the listbox, scrolling up or down if necessary.
301 static LRESULT LISTBOX_SetTopItem( LB_DESCR *descr, INT index, BOOL scroll )
303 INT max = LISTBOX_GetMaxTopIndex( descr );
304 if (index > max) index = max;
305 if (index < 0) index = 0;
306 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
307 if (descr->top_item == index) return LB_OKAY;
308 if (descr->style & LBS_MULTICOLUMN)
310 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
311 if (scroll && (abs(diff) < descr->width))
312 ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
313 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
321 if (descr->style & LBS_OWNERDRAWVARIABLE)
325 if (index > descr->top_item)
327 for (i = index - 1; i >= descr->top_item; i--)
328 diff -= descr->items[i].height;
332 for (i = index; i < descr->top_item; i++)
333 diff += descr->items[i].height;
337 diff = (descr->top_item - index) * descr->item_height;
339 if (abs(diff) < descr->height)
340 ScrollWindowEx( descr->self, 0, diff, NULL, NULL, 0, NULL,
341 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
345 if (!scroll) InvalidateRect( descr->self, NULL, TRUE );
346 descr->top_item = index;
347 LISTBOX_UpdateScroll( descr );
352 /***********************************************************************
355 * Update the page size. Should be called when the size of
356 * the client area or the item height changes.
358 static void LISTBOX_UpdatePage( LB_DESCR *descr )
362 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
364 if (page_size == descr->page_size) return;
365 descr->page_size = page_size;
366 if (descr->style & LBS_MULTICOLUMN)
367 InvalidateRect( descr->self, NULL, TRUE );
368 LISTBOX_SetTopItem( descr, descr->top_item, FALSE );
372 /***********************************************************************
375 * Update the size of the listbox. Should be called when the size of
376 * the client area changes.
378 static void LISTBOX_UpdateSize( LB_DESCR *descr )
382 GetClientRect( descr->self, &rect );
383 descr->width = rect.right - rect.left;
384 descr->height = rect.bottom - rect.top;
385 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
390 GetWindowRect( descr->self, &rect );
391 if(descr->item_height != 0)
392 remaining = descr->height % descr->item_height;
395 if ((descr->height > descr->item_height) && remaining)
397 if (is_old_app(descr))
398 { /* give a margin for error to 16 bits programs - if we need
399 less than the height of the nonclient area, round to the
400 *next* number of items */
401 int ncheight = rect.bottom - rect.top - descr->height;
402 if ((descr->item_height - remaining) <= ncheight)
403 remaining = remaining - descr->item_height;
405 TRACE("[%p]: changing height %d -> %d\n",
406 descr->self, descr->height, descr->height - remaining );
407 SetWindowPos( descr->self, 0, 0, 0, rect.right - rect.left,
408 rect.bottom - rect.top - remaining,
409 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
413 TRACE("[%p]: new size = %d,%d\n", descr->self, descr->width, descr->height );
414 LISTBOX_UpdatePage( descr );
415 LISTBOX_UpdateScroll( descr );
417 /* Invalidate the focused item so it will be repainted correctly */
418 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
420 InvalidateRect( descr->self, &rect, FALSE );
425 /***********************************************************************
426 * LISTBOX_GetItemRect
428 * Get the rectangle enclosing an item, in listbox client coordinates.
429 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
431 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect )
433 /* Index <= 0 is legal even on empty listboxes */
434 if (index && (index >= descr->nb_items))
436 memset(rect, 0, sizeof(*rect));
437 SetLastError(ERROR_INVALID_INDEX);
440 SetRect( rect, 0, 0, descr->width, descr->height );
441 if (descr->style & LBS_MULTICOLUMN)
443 INT col = (index / descr->page_size) -
444 (descr->top_item / descr->page_size);
445 rect->left += col * descr->column_width;
446 rect->right = rect->left + descr->column_width;
447 rect->top += (index % descr->page_size) * descr->item_height;
448 rect->bottom = rect->top + descr->item_height;
450 else if (descr->style & LBS_OWNERDRAWVARIABLE)
453 rect->right += descr->horz_pos;
454 if ((index >= 0) && (index < descr->nb_items))
456 if (index < descr->top_item)
458 for (i = descr->top_item-1; i >= index; i--)
459 rect->top -= descr->items[i].height;
463 for (i = descr->top_item; i < index; i++)
464 rect->top += descr->items[i].height;
466 rect->bottom = rect->top + descr->items[index].height;
472 rect->top += (index - descr->top_item) * descr->item_height;
473 rect->bottom = rect->top + descr->item_height;
474 rect->right += descr->horz_pos;
477 return ((rect->left < descr->width) && (rect->right > 0) &&
478 (rect->top < descr->height) && (rect->bottom > 0));
482 /***********************************************************************
483 * LISTBOX_GetItemFromPoint
485 * Return the item nearest from point (x,y) (in client coordinates).
487 static INT LISTBOX_GetItemFromPoint( LB_DESCR *descr, INT x, INT y )
489 INT index = descr->top_item;
491 if (!descr->nb_items) return -1; /* No items */
492 if (descr->style & LBS_OWNERDRAWVARIABLE)
497 while (index < descr->nb_items)
499 if ((pos += descr->items[index].height) > y) break;
508 if ((pos -= descr->items[index].height) <= y) break;
512 else if (descr->style & LBS_MULTICOLUMN)
514 if (y >= descr->item_height * descr->page_size) return -1;
515 if (y >= 0) index += y / descr->item_height;
516 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
517 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
521 index += (y / descr->item_height);
523 if (index < 0) return 0;
524 if (index >= descr->nb_items) return -1;
529 /***********************************************************************
534 static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect,
535 INT index, UINT action, BOOL ignoreFocus )
537 LB_ITEMDATA *item = NULL;
538 if (index < descr->nb_items) item = &descr->items[index];
540 if (IS_OWNERDRAW(descr))
545 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
549 if (action == ODA_FOCUS)
550 DrawFocusRect( hdc, rect );
552 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
556 /* some programs mess with the clipping region when
557 drawing the item, *and* restore the previous region
558 after they are done, so a region has better to exist
559 else everything ends clipped */
560 GetClientRect(descr->self, &r);
561 hrgn = CreateRectRgnIndirect(&r);
562 SelectClipRgn( hdc, hrgn);
563 DeleteObject( hrgn );
565 dis.CtlType = ODT_LISTBOX;
567 dis.hwndItem = descr->self;
568 dis.itemAction = action;
572 if (item && item->selected) dis.itemState |= ODS_SELECTED;
573 if (!ignoreFocus && (descr->focus_item == index) &&
575 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
576 if (!IsWindowEnabled(descr->self)) dis.itemState |= ODS_DISABLED;
577 dis.itemData = item ? item->data : 0;
579 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%ld,%ld-%ld,%ld\n",
580 descr->self, index, item ? debugstr_w(item->str) : "", action,
581 dis.itemState, rect->left, rect->top, rect->right, rect->bottom );
582 SendMessageW(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
586 COLORREF oldText = 0, oldBk = 0;
588 if (action == ODA_FOCUS)
590 DrawFocusRect( hdc, rect );
593 if (item && item->selected)
595 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
596 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
599 TRACE("[%p]: painting %d (%s) action=%02x rect=%ld,%ld-%ld,%ld\n",
600 descr->self, index, item ? debugstr_w(item->str) : "", action,
601 rect->left, rect->top, rect->right, rect->bottom );
603 ExtTextOutW( hdc, rect->left + 1, rect->top,
604 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
605 else if (!(descr->style & LBS_USETABSTOPS))
606 ExtTextOutW( hdc, rect->left + 1, rect->top,
607 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
608 strlenW(item->str), NULL );
611 /* Output empty string to paint background in the full width. */
612 ExtTextOutW( hdc, rect->left + 1, rect->top,
613 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
614 TabbedTextOutW( hdc, rect->left + 1 , rect->top,
615 item->str, strlenW(item->str),
616 descr->nb_tabs, descr->tabs, 0);
618 if (item && item->selected)
620 SetBkColor( hdc, oldBk );
621 SetTextColor( hdc, oldText );
623 if (!ignoreFocus && (descr->focus_item == index) &&
625 (descr->in_focus)) DrawFocusRect( hdc, rect );
630 /***********************************************************************
633 * Change the redraw flag.
635 static void LISTBOX_SetRedraw( LB_DESCR *descr, BOOL on )
639 if (!(descr->style & LBS_NOREDRAW)) return;
640 descr->style &= ~LBS_NOREDRAW;
641 if (descr->style & LBS_DISPLAYCHANGED)
642 { /* page was changed while setredraw false, refresh automatically */
643 InvalidateRect(descr->self, NULL, TRUE);
644 if ((descr->top_item + descr->page_size) > descr->nb_items)
645 { /* reset top of page if less than number of items/page */
646 descr->top_item = descr->nb_items - descr->page_size;
647 if (descr->top_item < 0) descr->top_item = 0;
649 descr->style &= ~LBS_DISPLAYCHANGED;
651 LISTBOX_UpdateScroll( descr );
653 else descr->style |= LBS_NOREDRAW;
657 /***********************************************************************
658 * LISTBOX_RepaintItem
660 * Repaint a single item synchronously.
662 static void LISTBOX_RepaintItem( LB_DESCR *descr, INT index, UINT action )
667 HBRUSH hbrush, oldBrush = 0;
669 /* Do not repaint the item if the item is not visible */
670 if (!IsWindowVisible(descr->self)) return;
671 if (descr->style & LBS_NOREDRAW)
673 descr->style |= LBS_DISPLAYCHANGED;
676 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
677 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
678 if (descr->font) oldFont = SelectObject( hdc, descr->font );
679 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
680 (WPARAM)hdc, (LPARAM)descr->self );
681 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
682 if (!IsWindowEnabled(descr->self))
683 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
684 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
685 LISTBOX_PaintItem( descr, hdc, &rect, index, action, FALSE );
686 if (oldFont) SelectObject( hdc, oldFont );
687 if (oldBrush) SelectObject( hdc, oldBrush );
688 ReleaseDC( descr->self, hdc );
692 /***********************************************************************
693 * LISTBOX_InitStorage
695 static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items )
699 nb_items += LB_ARRAY_GRANULARITY - 1;
700 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
702 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
703 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
704 nb_items * sizeof(LB_ITEMDATA));
707 item = HeapAlloc( GetProcessHeap(), 0,
708 nb_items * sizeof(LB_ITEMDATA));
713 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
721 /***********************************************************************
722 * LISTBOX_SetTabStops
724 static BOOL LISTBOX_SetTabStops( LB_DESCR *descr, INT count, LPINT tabs, BOOL short_ints )
728 if (!(descr->style & LBS_USETABSTOPS))
730 SetLastError(ERROR_LB_WITHOUT_TABSTOPS);
734 HeapFree( GetProcessHeap(), 0, descr->tabs );
735 if (!(descr->nb_tabs = count))
740 if (!(descr->tabs = HeapAlloc( GetProcessHeap(), 0,
741 descr->nb_tabs * sizeof(INT) )))
746 LPINT16 p = (LPINT16)tabs;
748 TRACE("[%p]: settabstops ", descr->self );
749 for (i = 0; i < descr->nb_tabs; i++) {
750 descr->tabs[i] = *p++<<1; /* FIXME */
751 TRACE("%hd ", descr->tabs[i]);
755 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
757 /* convert into "dialog units"*/
758 for (i = 0; i < descr->nb_tabs; i++)
759 descr->tabs[i] = MulDiv(descr->tabs[i], descr->avg_char_width, 4);
765 /***********************************************************************
768 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPWSTR buffer, BOOL unicode )
770 if ((index < 0) || (index >= descr->nb_items))
772 SetLastError(ERROR_INVALID_INDEX);
775 if (HAS_STRINGS(descr))
779 DWORD len = strlenW(descr->items[index].str);
782 return WideCharToMultiByte( CP_ACP, 0, descr->items[index].str, len,
783 NULL, 0, NULL, NULL );
786 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
790 strcpyW( buffer, descr->items[index].str );
791 return strlenW(buffer);
795 return WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, (LPSTR)buffer, 0x7FFFFFFF, NULL, NULL) - 1;
799 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
800 return sizeof(DWORD);
804 static inline INT LISTBOX_lstrcmpiW( LCID lcid, LPCWSTR str1, LPCWSTR str2 )
806 INT ret = CompareStringW( lcid, NORM_IGNORECASE, str1, -1, str2, -1 );
807 if (ret == CSTR_LESS_THAN)
809 if (ret == CSTR_EQUAL)
811 if (ret == CSTR_GREATER_THAN)
816 /***********************************************************************
817 * LISTBOX_FindStringPos
819 * Find the nearest string located before a given string in sort order.
820 * If 'exact' is TRUE, return an error if we don't get an exact match.
822 static INT LISTBOX_FindStringPos( LB_DESCR *descr, LPCWSTR str, BOOL exact )
824 INT index, min, max, res = -1;
826 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
828 max = descr->nb_items;
831 index = (min + max) / 2;
832 if (HAS_STRINGS(descr))
833 res = LISTBOX_lstrcmpiW( descr->locale, str, descr->items[index].str);
836 COMPAREITEMSTRUCT cis;
837 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
839 cis.CtlType = ODT_LISTBOX;
841 cis.hwndItem = descr->self;
842 /* note that some application (MetaStock) expects the second item
843 * to be in the listbox */
845 cis.itemData1 = (DWORD)str;
847 cis.itemData2 = descr->items[index].data;
848 cis.dwLocaleId = descr->locale;
849 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
851 if (!res) return index;
852 if (res < 0) max = index;
853 else min = index + 1;
855 return exact ? -1 : max;
859 /***********************************************************************
860 * LISTBOX_FindFileStrPos
862 * Find the nearest string located before a given string in directory
863 * sort order (i.e. first files, then directories, then drives).
865 static INT LISTBOX_FindFileStrPos( LB_DESCR *descr, LPCWSTR str )
867 INT min, max, res = -1;
869 if (!HAS_STRINGS(descr))
870 return LISTBOX_FindStringPos( descr, str, FALSE );
872 max = descr->nb_items;
875 INT index = (min + max) / 2;
876 LPCWSTR p = descr->items[index].str;
877 if (*p == '[') /* drive or directory */
879 if (*str != '[') res = -1;
880 else if (p[1] == '-') /* drive */
882 if (str[1] == '-') res = str[2] - p[2];
887 if (str[1] == '-') res = 1;
888 else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
893 if (*str == '[') res = 1;
894 else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
896 if (!res) return index;
897 if (res < 0) max = index;
898 else min = index + 1;
904 /***********************************************************************
907 * Find the item beginning with a given string.
909 static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exact )
914 if (start >= descr->nb_items) start = -1;
915 item = descr->items + start + 1;
916 if (HAS_STRINGS(descr))
918 if (!str || ! str[0] ) return LB_ERR;
921 for (i = start + 1; i < descr->nb_items; i++, item++)
922 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
923 for (i = 0, item = descr->items; i <= start; i++, item++)
924 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
928 /* Special case for drives and directories: ignore prefix */
929 #define CHECK_DRIVE(item) \
930 if ((item)->str[0] == '[') \
932 if (!strncmpiW( str, (item)->str+1, len )) return i; \
933 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
937 INT len = strlenW(str);
938 for (i = start + 1; i < descr->nb_items; i++, item++)
940 if (!strncmpiW( str, item->str, len )) return i;
943 for (i = 0, item = descr->items; i <= start; i++, item++)
945 if (!strncmpiW( str, item->str, len )) return i;
953 if (exact && (descr->style & LBS_SORT))
954 /* If sorted, use a WM_COMPAREITEM binary search */
955 return LISTBOX_FindStringPos( descr, str, TRUE );
957 /* Otherwise use a linear search */
958 for (i = start + 1; i < descr->nb_items; i++, item++)
959 if (item->data == (DWORD)str) return i;
960 for (i = 0, item = descr->items; i <= start; i++, item++)
961 if (item->data == (DWORD)str) return i;
967 /***********************************************************************
968 * LISTBOX_GetSelCount
970 static LRESULT LISTBOX_GetSelCount( LB_DESCR *descr )
973 LB_ITEMDATA *item = descr->items;
975 if (!(descr->style & LBS_MULTIPLESEL) ||
976 (descr->style & LBS_NOSEL))
978 for (i = count = 0; i < descr->nb_items; i++, item++)
979 if (item->selected) count++;
984 /***********************************************************************
985 * LISTBOX_GetSelItems16
987 static LRESULT LISTBOX_GetSelItems16( LB_DESCR *descr, INT16 max, LPINT16 array )
990 LB_ITEMDATA *item = descr->items;
992 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
993 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
994 if (item->selected) array[count++] = (INT16)i;
999 /***********************************************************************
1000 * LISTBOX_GetSelItems
1002 static LRESULT LISTBOX_GetSelItems( LB_DESCR *descr, INT max, LPINT array )
1005 LB_ITEMDATA *item = descr->items;
1007 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1008 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
1009 if (item->selected) array[count++] = i;
1014 /***********************************************************************
1017 static LRESULT LISTBOX_Paint( LB_DESCR *descr, HDC hdc )
1019 INT i, col_pos = descr->page_size - 1;
1021 RECT focusRect = {-1, -1, -1, -1};
1023 HBRUSH hbrush, oldBrush = 0;
1025 if (descr->style & LBS_NOREDRAW) return 0;
1027 SetRect( &rect, 0, 0, descr->width, descr->height );
1028 if (descr->style & LBS_MULTICOLUMN)
1029 rect.right = rect.left + descr->column_width;
1030 else if (descr->horz_pos)
1032 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
1033 rect.right += descr->horz_pos;
1036 if (descr->font) oldFont = SelectObject( hdc, descr->font );
1037 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1038 (WPARAM)hdc, (LPARAM)descr->self );
1039 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1040 if (!IsWindowEnabled(descr->self)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1042 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1045 /* Special case for empty listbox: paint focus rect */
1046 rect.bottom = rect.top + descr->item_height;
1047 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1048 &rect, NULL, 0, NULL );
1049 LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, FALSE );
1050 rect.top = rect.bottom;
1053 /* Paint all the item, regarding the selection
1054 Focus state will be painted after */
1056 for (i = descr->top_item; i < descr->nb_items; i++)
1058 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1059 rect.bottom = rect.top + descr->item_height;
1061 rect.bottom = rect.top + descr->items[i].height;
1063 if (i == descr->focus_item)
1065 /* keep the focus rect, to paint the focus item after */
1066 focusRect.left = rect.left;
1067 focusRect.right = rect.right;
1068 focusRect.top = rect.top;
1069 focusRect.bottom = rect.bottom;
1071 LISTBOX_PaintItem( descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1072 rect.top = rect.bottom;
1074 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1076 if (!IS_OWNERDRAW(descr))
1078 /* Clear the bottom of the column */
1079 if (rect.top < descr->height)
1081 rect.bottom = descr->height;
1082 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1083 &rect, NULL, 0, NULL );
1087 /* Go to the next column */
1088 rect.left += descr->column_width;
1089 rect.right += descr->column_width;
1091 col_pos = descr->page_size - 1;
1096 if (rect.top >= descr->height) break;
1100 /* Paint the focus item now */
1101 if (focusRect.top != focusRect.bottom &&
1102 descr->caret_on && descr->in_focus)
1103 LISTBOX_PaintItem( descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1105 if (!IS_OWNERDRAW(descr))
1107 /* Clear the remainder of the client area */
1108 if (rect.top < descr->height)
1110 rect.bottom = descr->height;
1111 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1112 &rect, NULL, 0, NULL );
1114 if (rect.right < descr->width)
1116 rect.left = rect.right;
1117 rect.right = descr->width;
1119 rect.bottom = descr->height;
1120 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1121 &rect, NULL, 0, NULL );
1124 if (oldFont) SelectObject( hdc, oldFont );
1125 if (oldBrush) SelectObject( hdc, oldBrush );
1130 /***********************************************************************
1131 * LISTBOX_InvalidateItems
1133 * Invalidate all items from a given item. If the specified item is not
1134 * visible, nothing happens.
1136 static void LISTBOX_InvalidateItems( LB_DESCR *descr, INT index )
1140 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1142 if (descr->style & LBS_NOREDRAW)
1144 descr->style |= LBS_DISPLAYCHANGED;
1147 rect.bottom = descr->height;
1148 InvalidateRect( descr->self, &rect, TRUE );
1149 if (descr->style & LBS_MULTICOLUMN)
1151 /* Repaint the other columns */
1152 rect.left = rect.right;
1153 rect.right = descr->width;
1155 InvalidateRect( descr->self, &rect, TRUE );
1160 static void LISTBOX_InvalidateItemRect( LB_DESCR *descr, INT index )
1164 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1165 InvalidateRect( descr->self, &rect, TRUE );
1168 /***********************************************************************
1169 * LISTBOX_GetItemHeight
1171 static LRESULT LISTBOX_GetItemHeight( LB_DESCR *descr, INT index )
1173 if (descr->style & LBS_OWNERDRAWVARIABLE)
1175 if ((index < 0) || (index >= descr->nb_items))
1177 SetLastError(ERROR_INVALID_INDEX);
1180 return descr->items[index].height;
1182 else return descr->item_height;
1186 /***********************************************************************
1187 * LISTBOX_SetItemHeight
1189 static LRESULT LISTBOX_SetItemHeight( LB_DESCR *descr, INT index, INT height, BOOL repaint )
1191 if (!height) height = 1;
1193 if (descr->style & LBS_OWNERDRAWVARIABLE)
1195 if ((index < 0) || (index >= descr->nb_items))
1197 SetLastError(ERROR_INVALID_INDEX);
1200 TRACE("[%p]: item %d height = %d\n", descr->self, index, height );
1201 descr->items[index].height = height;
1202 LISTBOX_UpdateScroll( descr );
1204 LISTBOX_InvalidateItems( descr, index );
1206 else if (height != descr->item_height)
1208 TRACE("[%p]: new height = %d\n", descr->self, height );
1209 descr->item_height = height;
1210 LISTBOX_UpdatePage( descr );
1211 LISTBOX_UpdateScroll( descr );
1213 InvalidateRect( descr->self, 0, TRUE );
1219 /***********************************************************************
1220 * LISTBOX_SetHorizontalPos
1222 static void LISTBOX_SetHorizontalPos( LB_DESCR *descr, INT pos )
1226 if (pos > descr->horz_extent - descr->width)
1227 pos = descr->horz_extent - descr->width;
1228 if (pos < 0) pos = 0;
1229 if (!(diff = descr->horz_pos - pos)) return;
1230 TRACE("[%p]: new horz pos = %d\n", descr->self, pos );
1231 descr->horz_pos = pos;
1232 LISTBOX_UpdateScroll( descr );
1233 if (abs(diff) < descr->width)
1236 /* Invalidate the focused item so it will be repainted correctly */
1237 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
1238 InvalidateRect( descr->self, &rect, TRUE );
1239 ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
1240 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1243 InvalidateRect( descr->self, NULL, TRUE );
1247 /***********************************************************************
1248 * LISTBOX_SetHorizontalExtent
1250 static LRESULT LISTBOX_SetHorizontalExtent( LB_DESCR *descr, INT extent )
1252 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1254 if (extent <= 0) extent = 1;
1255 if (extent == descr->horz_extent) return LB_OKAY;
1256 TRACE("[%p]: new horz extent = %d\n", descr->self, extent );
1257 descr->horz_extent = extent;
1258 if (descr->horz_pos > extent - descr->width)
1259 LISTBOX_SetHorizontalPos( descr, extent - descr->width );
1261 LISTBOX_UpdateScroll( descr );
1266 /***********************************************************************
1267 * LISTBOX_SetColumnWidth
1269 static LRESULT LISTBOX_SetColumnWidth( LB_DESCR *descr, INT width)
1271 if (width == descr->column_width) return LB_OKAY;
1272 TRACE("[%p]: new column width = %d\n", descr->self, width );
1273 descr->column_width = width;
1274 LISTBOX_UpdatePage( descr );
1279 /***********************************************************************
1282 * Returns the item height.
1284 static INT LISTBOX_SetFont( LB_DESCR *descr, HFONT font )
1288 const char *alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1293 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE )))
1295 ERR("unable to get DC.\n" );
1298 if (font) oldFont = SelectObject( hdc, font );
1299 GetTextExtentPointA( hdc, alphabet, 52, &sz);
1300 if (oldFont) SelectObject( hdc, oldFont );
1301 ReleaseDC( descr->self, hdc );
1303 descr->avg_char_width = (sz.cx / 26 + 1) / 2;
1304 if (!IS_OWNERDRAW(descr))
1305 LISTBOX_SetItemHeight( descr, 0, sz.cy, FALSE );
1310 /***********************************************************************
1311 * LISTBOX_MakeItemVisible
1313 * Make sure that a given item is partially or fully visible.
1315 static void LISTBOX_MakeItemVisible( LB_DESCR *descr, INT index, BOOL fully )
1319 if (index <= descr->top_item) top = index;
1320 else if (descr->style & LBS_MULTICOLUMN)
1322 INT cols = descr->width;
1323 if (!fully) cols += descr->column_width - 1;
1324 if (cols >= descr->column_width) cols /= descr->column_width;
1326 if (index < descr->top_item + (descr->page_size * cols)) return;
1327 top = index - descr->page_size * (cols - 1);
1329 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1331 INT height = fully ? descr->items[index].height : 1;
1332 for (top = index; top > descr->top_item; top--)
1333 if ((height += descr->items[top-1].height) > descr->height) break;
1337 if (index < descr->top_item + descr->page_size) return;
1338 if (!fully && (index == descr->top_item + descr->page_size) &&
1339 (descr->height > (descr->page_size * descr->item_height))) return;
1340 top = index - descr->page_size + 1;
1342 LISTBOX_SetTopItem( descr, top, TRUE );
1345 /***********************************************************************
1346 * LISTBOX_SetCaretIndex
1349 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1352 static LRESULT LISTBOX_SetCaretIndex( LB_DESCR *descr, INT index, BOOL fully_visible )
1354 INT oldfocus = descr->focus_item;
1356 if (descr->style & LBS_NOSEL) return LB_ERR;
1357 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1358 if (index == oldfocus) return LB_OKAY;
1359 descr->focus_item = index;
1360 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1361 LISTBOX_RepaintItem( descr, oldfocus, ODA_FOCUS );
1363 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1364 if (descr->caret_on && (descr->in_focus))
1365 LISTBOX_RepaintItem( descr, index, ODA_FOCUS );
1371 /***********************************************************************
1372 * LISTBOX_SelectItemRange
1374 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1376 static LRESULT LISTBOX_SelectItemRange( LB_DESCR *descr, INT first,
1381 /* A few sanity checks */
1383 if (descr->style & LBS_NOSEL) return LB_ERR;
1384 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1385 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1386 if (last == -1) last = descr->nb_items - 1;
1387 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1388 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1390 if (on) /* Turn selection on */
1392 for (i = first; i <= last; i++)
1394 if (descr->items[i].selected) continue;
1395 descr->items[i].selected = TRUE;
1396 LISTBOX_InvalidateItemRect(descr, i);
1399 else /* Turn selection off */
1401 for (i = first; i <= last; i++)
1403 if (!descr->items[i].selected) continue;
1404 descr->items[i].selected = FALSE;
1405 LISTBOX_InvalidateItemRect(descr, i);
1411 /***********************************************************************
1412 * LISTBOX_SetSelection
1414 static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index,
1415 BOOL on, BOOL send_notify )
1417 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1419 if (descr->style & LBS_NOSEL)
1421 descr->selected_item = index;
1424 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1425 if (descr->style & LBS_MULTIPLESEL)
1427 if (index == -1) /* Select all items */
1428 return LISTBOX_SelectItemRange( descr, 0, -1, on );
1429 else /* Only one item */
1430 return LISTBOX_SelectItemRange( descr, index, index, on );
1434 INT oldsel = descr->selected_item;
1435 if (index == oldsel) return LB_OKAY;
1436 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1437 if (index != -1) descr->items[index].selected = TRUE;
1438 descr->selected_item = index;
1439 if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT );
1440 if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT );
1441 if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr,
1442 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1444 if( descr->lphc ) /* set selection change flag for parent combo */
1445 descr->lphc->wState |= CBF_SELCHANGE;
1451 /***********************************************************************
1454 * Change the caret position and extend the selection to the new caret.
1456 static void LISTBOX_MoveCaret( LB_DESCR *descr, INT index, BOOL fully_visible )
1458 INT oldfocus = descr->focus_item;
1460 if ((index < 0) || (index >= descr->nb_items))
1463 /* Important, repaint needs to be done in this order if
1464 you want to mimic Windows behavior:
1465 1. Remove the focus and paint the item
1466 2. Remove the selection and paint the item(s)
1467 3. Set the selection and repaint the item(s)
1468 4. Set the focus to 'index' and repaint the item */
1470 /* 1. remove the focus and repaint the item */
1471 descr->focus_item = -1;
1472 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1473 LISTBOX_RepaintItem( descr, oldfocus, ODA_FOCUS );
1475 /* 2. then turn off the previous selection */
1476 /* 3. repaint the new selected item */
1477 if (descr->style & LBS_EXTENDEDSEL)
1479 if (descr->anchor_item != -1)
1481 INT first = min( index, descr->anchor_item );
1482 INT last = max( index, descr->anchor_item );
1484 LISTBOX_SelectItemRange( descr, 0, first - 1, FALSE );
1485 LISTBOX_SelectItemRange( descr, last + 1, -1, FALSE );
1486 LISTBOX_SelectItemRange( descr, first, last, TRUE );
1489 else if (!(descr->style & LBS_MULTIPLESEL))
1491 /* Set selection to new caret item */
1492 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
1495 /* 4. repaint the new item with the focus */
1496 descr->focus_item = index;
1497 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1498 if (descr->caret_on && (descr->in_focus))
1499 LISTBOX_RepaintItem( descr, index, ODA_FOCUS );
1503 /***********************************************************************
1504 * LISTBOX_InsertItem
1506 static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index,
1507 LPWSTR str, DWORD data )
1511 INT oldfocus = descr->focus_item;
1513 if (index == -1) index = descr->nb_items;
1514 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1515 if (!descr->items) max_items = 0;
1516 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1517 if (descr->nb_items == max_items)
1519 /* We need to grow the array */
1520 max_items += LB_ARRAY_GRANULARITY;
1522 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1523 max_items * sizeof(LB_ITEMDATA) );
1525 item = HeapAlloc( GetProcessHeap(), 0,
1526 max_items * sizeof(LB_ITEMDATA) );
1529 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1532 descr->items = item;
1535 /* Insert the item structure */
1537 item = &descr->items[index];
1538 if (index < descr->nb_items)
1539 RtlMoveMemory( item + 1, item,
1540 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1544 item->selected = FALSE;
1547 /* Get item height */
1549 if (descr->style & LBS_OWNERDRAWVARIABLE)
1551 MEASUREITEMSTRUCT mis;
1552 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1554 mis.CtlType = ODT_LISTBOX;
1557 mis.itemData = descr->items[index].data;
1558 mis.itemHeight = descr->item_height;
1559 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1560 item->height = mis.itemHeight ? mis.itemHeight : 1;
1561 TRACE("[%p]: measure item %d (%s) = %d\n",
1562 descr->self, index, str ? debugstr_w(str) : "", item->height );
1565 /* Repaint the items */
1567 LISTBOX_UpdateScroll( descr );
1568 LISTBOX_InvalidateItems( descr, index );
1570 /* Move selection and focused item */
1571 /* If listbox was empty, set focus to the first item */
1572 if (descr->nb_items == 1)
1573 LISTBOX_SetCaretIndex( descr, 0, FALSE );
1574 /* single select don't change selection index in win31 */
1575 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1577 descr->selected_item++;
1578 LISTBOX_SetSelection( descr, descr->selected_item-1, TRUE, FALSE );
1582 if (index <= descr->selected_item)
1584 descr->selected_item++;
1585 descr->focus_item = oldfocus; /* focus not changed */
1592 /***********************************************************************
1593 * LISTBOX_InsertString
1595 static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index, LPCWSTR str )
1597 LPWSTR new_str = NULL;
1601 if (HAS_STRINGS(descr))
1603 static const WCHAR empty_stringW[] = { 0 };
1604 if (!str) str = empty_stringW;
1605 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1607 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1610 strcpyW(new_str, str);
1612 else data = (DWORD)str;
1614 if (index == -1) index = descr->nb_items;
1615 if ((ret = LISTBOX_InsertItem( descr, index, new_str, data )) != 0)
1617 HeapFree( GetProcessHeap(), 0, new_str );
1621 TRACE("[%p]: added item %d %s\n",
1622 descr->self, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1627 /***********************************************************************
1628 * LISTBOX_DeleteItem
1630 * Delete the content of an item. 'index' must be a valid index.
1632 static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index )
1634 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1635 * while Win95 sends it for all items with user data.
1636 * It's probably better to send it too often than not
1637 * often enough, so this is what we do here.
1639 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1641 DELETEITEMSTRUCT dis;
1642 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1644 dis.CtlType = ODT_LISTBOX;
1647 dis.hwndItem = descr->self;
1648 dis.itemData = descr->items[index].data;
1649 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1651 if (HAS_STRINGS(descr) && descr->items[index].str)
1652 HeapFree( GetProcessHeap(), 0, descr->items[index].str );
1656 /***********************************************************************
1657 * LISTBOX_RemoveItem
1659 * Remove an item from the listbox and delete its content.
1661 static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index )
1666 if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1667 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1669 /* We need to invalidate the original rect instead of the updated one. */
1670 LISTBOX_InvalidateItems( descr, index );
1672 LISTBOX_DeleteItem( descr, index );
1674 /* Remove the item */
1676 item = &descr->items[index];
1677 if (index < descr->nb_items-1)
1678 RtlMoveMemory( item, item + 1,
1679 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1681 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1683 /* Shrink the item array if possible */
1685 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1686 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1688 max_items -= LB_ARRAY_GRANULARITY;
1689 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1690 max_items * sizeof(LB_ITEMDATA) );
1691 if (item) descr->items = item;
1693 /* Repaint the items */
1695 LISTBOX_UpdateScroll( descr );
1696 /* if we removed the scrollbar, reset the top of the list
1697 (correct for owner-drawn ???) */
1698 if (descr->nb_items == descr->page_size)
1699 LISTBOX_SetTopItem( descr, 0, TRUE );
1701 /* Move selection and focused item */
1702 if (!IS_MULTISELECT(descr))
1704 if (index == descr->selected_item)
1705 descr->selected_item = -1;
1706 else if (index < descr->selected_item)
1708 descr->selected_item--;
1709 if (ISWIN31) /* win 31 do not change the selected item number */
1710 LISTBOX_SetSelection( descr, descr->selected_item + 1, TRUE, FALSE);
1714 if (descr->focus_item >= descr->nb_items)
1716 descr->focus_item = descr->nb_items - 1;
1717 if (descr->focus_item < 0) descr->focus_item = 0;
1723 /***********************************************************************
1724 * LISTBOX_ResetContent
1726 static void LISTBOX_ResetContent( LB_DESCR *descr )
1730 for(i = descr->nb_items - 1; i>=0; i--) LISTBOX_DeleteItem( descr, i);
1731 HeapFree( GetProcessHeap(), 0, descr->items );
1732 descr->nb_items = 0;
1733 descr->top_item = 0;
1734 descr->selected_item = -1;
1735 descr->focus_item = 0;
1736 descr->anchor_item = -1;
1737 descr->items = NULL;
1741 /***********************************************************************
1744 static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count )
1748 if (HAS_STRINGS(descr))
1750 SetLastError(ERROR_SETCOUNT_ON_BAD_LB);
1754 /* FIXME: this is far from optimal... */
1755 if (count > descr->nb_items)
1757 while (count > descr->nb_items)
1758 if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0)
1761 else if (count < descr->nb_items)
1763 while (count < descr->nb_items)
1764 if ((ret = LISTBOX_RemoveItem( descr, -1 )) < 0)
1771 /***********************************************************************
1774 static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib,
1775 LPCWSTR filespec, BOOL long_names )
1778 LRESULT ret = LB_OKAY;
1779 WIN32_FIND_DATAW entry;
1782 /* don't scan directory if we just want drives exclusively */
1783 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1784 /* scan directory */
1785 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1787 int le = GetLastError();
1788 if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR;
1795 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1797 static const WCHAR bracketW[] = { ']',0 };
1798 static const WCHAR dotW[] = { '.',0 };
1799 if (!(attrib & DDL_DIRECTORY) ||
1800 !strcmpW( entry.cFileName, dotW )) continue;
1802 if (!long_names && entry.cAlternateFileName[0])
1803 strcpyW( buffer + 1, entry.cAlternateFileName );
1805 strcpyW( buffer + 1, entry.cFileName );
1806 strcatW(buffer, bracketW);
1808 else /* not a directory */
1810 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1811 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1813 if ((attrib & DDL_EXCLUSIVE) &&
1814 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1817 if (!long_names && entry.cAlternateFileName[0])
1818 strcpyW( buffer, entry.cAlternateFileName );
1820 strcpyW( buffer, entry.cFileName );
1822 if (!long_names) CharLowerW( buffer );
1823 pos = LISTBOX_FindFileStrPos( descr, buffer );
1824 if ((ret = LISTBOX_InsertString( descr, pos, buffer )) < 0)
1826 } while (FindNextFileW( handle, &entry ));
1827 FindClose( handle );
1832 if ((ret >= 0) && (attrib & DDL_DRIVES))
1834 WCHAR buffer[] = {'[','-','a','-',']',0};
1835 WCHAR root[] = {'A',':','\\',0};
1837 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1839 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1840 if ((ret = LISTBOX_InsertString( descr, -1, buffer )) < 0)
1848 /***********************************************************************
1849 * LISTBOX_HandleVScroll
1851 static LRESULT LISTBOX_HandleVScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1855 if (descr->style & LBS_MULTICOLUMN) return 0;
1859 LISTBOX_SetTopItem( descr, descr->top_item - 1, TRUE );
1862 LISTBOX_SetTopItem( descr, descr->top_item + 1, TRUE );
1865 LISTBOX_SetTopItem( descr, descr->top_item -
1866 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1869 LISTBOX_SetTopItem( descr, descr->top_item +
1870 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1872 case SB_THUMBPOSITION:
1873 LISTBOX_SetTopItem( descr, pos, TRUE );
1876 info.cbSize = sizeof(info);
1877 info.fMask = SIF_TRACKPOS;
1878 GetScrollInfo( descr->self, SB_VERT, &info );
1879 LISTBOX_SetTopItem( descr, info.nTrackPos, TRUE );
1882 LISTBOX_SetTopItem( descr, 0, TRUE );
1885 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1892 /***********************************************************************
1893 * LISTBOX_HandleHScroll
1895 static LRESULT LISTBOX_HandleHScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1900 if (descr->style & LBS_MULTICOLUMN)
1905 LISTBOX_SetTopItem( descr, descr->top_item-descr->page_size,
1909 LISTBOX_SetTopItem( descr, descr->top_item+descr->page_size,
1913 page = descr->width / descr->column_width;
1914 if (page < 1) page = 1;
1915 LISTBOX_SetTopItem( descr,
1916 descr->top_item - page * descr->page_size, TRUE );
1919 page = descr->width / descr->column_width;
1920 if (page < 1) page = 1;
1921 LISTBOX_SetTopItem( descr,
1922 descr->top_item + page * descr->page_size, TRUE );
1924 case SB_THUMBPOSITION:
1925 LISTBOX_SetTopItem( descr, pos*descr->page_size, TRUE );
1928 info.cbSize = sizeof(info);
1929 info.fMask = SIF_TRACKPOS;
1930 GetScrollInfo( descr->self, SB_VERT, &info );
1931 LISTBOX_SetTopItem( descr, info.nTrackPos*descr->page_size,
1935 LISTBOX_SetTopItem( descr, 0, TRUE );
1938 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1942 else if (descr->horz_extent)
1947 LISTBOX_SetHorizontalPos( descr, descr->horz_pos - 1 );
1950 LISTBOX_SetHorizontalPos( descr, descr->horz_pos + 1 );
1953 LISTBOX_SetHorizontalPos( descr,
1954 descr->horz_pos - descr->width );
1957 LISTBOX_SetHorizontalPos( descr,
1958 descr->horz_pos + descr->width );
1960 case SB_THUMBPOSITION:
1961 LISTBOX_SetHorizontalPos( descr, pos );
1964 info.cbSize = sizeof(info);
1965 info.fMask = SIF_TRACKPOS;
1966 GetScrollInfo( descr->self, SB_HORZ, &info );
1967 LISTBOX_SetHorizontalPos( descr, info.nTrackPos );
1970 LISTBOX_SetHorizontalPos( descr, 0 );
1973 LISTBOX_SetHorizontalPos( descr,
1974 descr->horz_extent - descr->width );
1981 static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta )
1983 short gcWheelDelta = 0;
1984 UINT pulScrollLines = 3;
1986 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1988 gcWheelDelta -= delta;
1990 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1992 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1993 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1994 LISTBOX_SetTopItem( descr, descr->top_item + cLineScroll, TRUE );
1999 /***********************************************************************
2000 * LISTBOX_HandleLButtonDown
2002 static LRESULT LISTBOX_HandleLButtonDown( LB_DESCR *descr, DWORD keys, INT x, INT y )
2004 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
2005 TRACE("[%p]: lbuttondown %d,%d item %d\n", descr->self, x, y, index );
2006 if (!descr->caret_on && (descr->in_focus)) return 0;
2008 if (!descr->in_focus)
2010 if( !descr->lphc ) SetFocus( descr->self );
2011 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
2014 if (index == -1) return 0;
2016 if (descr->style & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL))
2018 /* we should perhaps make sure that all items are deselected
2019 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2020 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2021 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2024 if (!(keys & MK_SHIFT)) descr->anchor_item = index;
2025 if (keys & MK_CONTROL)
2027 LISTBOX_SetCaretIndex( descr, index, FALSE );
2028 LISTBOX_SetSelection( descr, index,
2029 !descr->items[index].selected,
2030 (descr->style & LBS_NOTIFY) != 0);
2034 LISTBOX_MoveCaret( descr, index, FALSE );
2036 if (descr->style & LBS_EXTENDEDSEL)
2038 LISTBOX_SetSelection( descr, index,
2039 descr->items[index].selected,
2040 (descr->style & LBS_NOTIFY) != 0 );
2044 LISTBOX_SetSelection( descr, index,
2045 !descr->items[index].selected,
2046 (descr->style & LBS_NOTIFY) != 0 );
2052 descr->anchor_item = index;
2053 LISTBOX_MoveCaret( descr, index, FALSE );
2054 LISTBOX_SetSelection( descr, index,
2055 TRUE, (descr->style & LBS_NOTIFY) != 0 );
2058 descr->captured = TRUE;
2059 SetCapture( descr->self );
2063 if (descr->style & LBS_NOTIFY )
2064 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
2065 MAKELPARAM( x, y ) );
2066 if (GetWindowLongW( descr->self, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
2073 if (DragDetect( descr->self, pt ))
2074 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
2081 /*************************************************************************
2082 * LISTBOX_HandleLButtonDownCombo [Internal]
2084 * Process LButtonDown message for the ComboListBox
2087 * pWnd [I] The windows internal structure
2088 * pDescr [I] The ListBox internal structure
2089 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2090 * x [I] X Mouse Coordinate
2091 * y [I] Y Mouse Coordinate
2094 * 0 since we are processing the WM_LBUTTONDOWN Message
2097 * This function is only to be used when a ListBox is a ComboListBox
2100 static LRESULT LISTBOX_HandleLButtonDownCombo( LB_DESCR *descr, UINT msg, DWORD keys, INT x, INT y)
2102 RECT clientRect, screenRect;
2108 GetClientRect(descr->self, &clientRect);
2110 if(PtInRect(&clientRect, mousePos))
2112 /* MousePos is in client, resume normal processing */
2113 if (msg == WM_LBUTTONDOWN)
2115 descr->lphc->droppedIndex = descr->nb_items ? descr->selected_item : -1;
2116 return LISTBOX_HandleLButtonDown( descr, keys, x, y);
2118 else if (descr->style & LBS_NOTIFY)
2119 SEND_NOTIFICATION( descr, LBN_DBLCLK );
2123 POINT screenMousePos;
2124 HWND hWndOldCapture;
2126 /* Check the Non-Client Area */
2127 screenMousePos = mousePos;
2128 hWndOldCapture = GetCapture();
2130 GetWindowRect(descr->self, &screenRect);
2131 ClientToScreen(descr->self, &screenMousePos);
2133 if(!PtInRect(&screenRect, screenMousePos))
2135 LISTBOX_SetCaretIndex( descr, descr->lphc->droppedIndex, FALSE );
2136 LISTBOX_SetSelection( descr, descr->lphc->droppedIndex, FALSE, FALSE );
2137 COMBO_FlipListbox( descr->lphc, FALSE, FALSE );
2141 /* Check to see the NC is a scrollbar */
2143 LONG style = GetWindowLongW( descr->self, GWL_STYLE );
2144 /* Check Vertical scroll bar */
2145 if (style & WS_VSCROLL)
2147 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2148 if (PtInRect( &clientRect, mousePos ))
2149 nHitTestType = HTVSCROLL;
2151 /* Check horizontal scroll bar */
2152 if (style & WS_HSCROLL)
2154 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2155 if (PtInRect( &clientRect, mousePos ))
2156 nHitTestType = HTHSCROLL;
2158 /* Windows sends this message when a scrollbar is clicked
2161 if(nHitTestType != 0)
2163 SendMessageW(descr->self, WM_NCLBUTTONDOWN, nHitTestType,
2164 MAKELONG(screenMousePos.x, screenMousePos.y));
2166 /* Resume the Capture after scrolling is complete
2168 if(hWndOldCapture != 0)
2169 SetCapture(hWndOldCapture);
2175 /***********************************************************************
2176 * LISTBOX_HandleLButtonUp
2178 static LRESULT LISTBOX_HandleLButtonUp( LB_DESCR *descr )
2180 if (LISTBOX_Timer != LB_TIMER_NONE)
2181 KillSystemTimer( descr->self, LB_TIMER_ID );
2182 LISTBOX_Timer = LB_TIMER_NONE;
2183 if (descr->captured)
2185 descr->captured = FALSE;
2186 if (GetCapture() == descr->self) ReleaseCapture();
2187 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2188 SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2194 /***********************************************************************
2195 * LISTBOX_HandleTimer
2197 * Handle scrolling upon a timer event.
2198 * Return TRUE if scrolling should continue.
2200 static LRESULT LISTBOX_HandleTimer( LB_DESCR *descr, INT index, TIMER_DIRECTION dir )
2205 if (descr->top_item) index = descr->top_item - 1;
2209 if (descr->top_item) index -= descr->page_size;
2212 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2213 if (index == descr->focus_item) index++;
2214 if (index >= descr->nb_items) index = descr->nb_items - 1;
2216 case LB_TIMER_RIGHT:
2217 if (index + descr->page_size < descr->nb_items)
2218 index += descr->page_size;
2223 if (index == descr->focus_item) return FALSE;
2224 LISTBOX_MoveCaret( descr, index, FALSE );
2229 /***********************************************************************
2230 * LISTBOX_HandleSystemTimer
2232 * WM_SYSTIMER handler.
2234 static LRESULT LISTBOX_HandleSystemTimer( LB_DESCR *descr )
2236 if (!LISTBOX_HandleTimer( descr, descr->focus_item, LISTBOX_Timer ))
2238 KillSystemTimer( descr->self, LB_TIMER_ID );
2239 LISTBOX_Timer = LB_TIMER_NONE;
2245 /***********************************************************************
2246 * LISTBOX_HandleMouseMove
2248 * WM_MOUSEMOVE handler.
2250 static void LISTBOX_HandleMouseMove( LB_DESCR *descr,
2254 TIMER_DIRECTION dir = LB_TIMER_NONE;
2256 if (!descr->captured) return;
2258 if (descr->style & LBS_MULTICOLUMN)
2261 else if (y >= descr->item_height * descr->page_size)
2262 y = descr->item_height * descr->page_size - 1;
2266 dir = LB_TIMER_LEFT;
2269 else if (x >= descr->width)
2271 dir = LB_TIMER_RIGHT;
2272 x = descr->width - 1;
2277 if (y < 0) dir = LB_TIMER_UP; /* above */
2278 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2281 index = LISTBOX_GetItemFromPoint( descr, x, y );
2282 if (index == -1) index = descr->focus_item;
2283 if (!LISTBOX_HandleTimer( descr, index, dir )) dir = LB_TIMER_NONE;
2285 /* Start/stop the system timer */
2287 if (dir != LB_TIMER_NONE)
2288 SetSystemTimer( descr->self, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2289 else if (LISTBOX_Timer != LB_TIMER_NONE)
2290 KillSystemTimer( descr->self, LB_TIMER_ID );
2291 LISTBOX_Timer = dir;
2295 /***********************************************************************
2296 * LISTBOX_HandleKeyDown
2298 static LRESULT LISTBOX_HandleKeyDown( LB_DESCR *descr, DWORD key )
2301 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2302 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2303 bForceSelection = FALSE; /* only for single select list */
2305 if (descr->style & LBS_WANTKEYBOARDINPUT)
2307 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2308 MAKEWPARAM(LOWORD(key), descr->focus_item),
2309 (LPARAM)descr->self );
2310 if (caret == -2) return 0;
2312 if (caret == -1) switch(key)
2315 if (descr->style & LBS_MULTICOLUMN)
2317 bForceSelection = FALSE;
2318 if (descr->focus_item >= descr->page_size)
2319 caret = descr->focus_item - descr->page_size;
2324 caret = descr->focus_item - 1;
2325 if (caret < 0) caret = 0;
2328 if (descr->style & LBS_MULTICOLUMN)
2330 bForceSelection = FALSE;
2331 if (descr->focus_item + descr->page_size < descr->nb_items)
2332 caret = descr->focus_item + descr->page_size;
2337 caret = descr->focus_item + 1;
2338 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2342 if (descr->style & LBS_MULTICOLUMN)
2344 INT page = descr->width / descr->column_width;
2345 if (page < 1) page = 1;
2346 caret = descr->focus_item - (page * descr->page_size) + 1;
2348 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2349 if (caret < 0) caret = 0;
2352 if (descr->style & LBS_MULTICOLUMN)
2354 INT page = descr->width / descr->column_width;
2355 if (page < 1) page = 1;
2356 caret = descr->focus_item + (page * descr->page_size) - 1;
2358 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2359 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2365 caret = descr->nb_items - 1;
2368 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2369 else if (descr->style & LBS_MULTIPLESEL)
2371 LISTBOX_SetSelection( descr, descr->focus_item,
2372 !descr->items[descr->focus_item].selected,
2373 (descr->style & LBS_NOTIFY) != 0 );
2377 bForceSelection = FALSE;
2379 if (bForceSelection) /* focused item is used instead of key */
2380 caret = descr->focus_item;
2383 if (((descr->style & LBS_EXTENDEDSEL) &&
2384 !(GetKeyState( VK_SHIFT ) & 0x8000)) ||
2385 !IS_MULTISELECT(descr))
2386 descr->anchor_item = caret;
2387 LISTBOX_MoveCaret( descr, caret, TRUE );
2389 if (descr->style & LBS_MULTIPLESEL)
2390 descr->selected_item = caret;
2392 LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2393 if (descr->style & LBS_NOTIFY)
2397 /* make sure that combo parent doesn't hide us */
2398 descr->lphc->wState |= CBF_NOROLLUP;
2400 if (descr->nb_items) SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2407 /***********************************************************************
2408 * LISTBOX_HandleChar
2410 static LRESULT LISTBOX_HandleChar( LB_DESCR *descr, WCHAR charW )
2418 if (descr->style & LBS_WANTKEYBOARDINPUT)
2420 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2421 MAKEWPARAM(charW, descr->focus_item),
2422 (LPARAM)descr->self );
2423 if (caret == -2) return 0;
2426 caret = LISTBOX_FindString( descr, descr->focus_item, str, FALSE);
2429 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2430 LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2431 LISTBOX_MoveCaret( descr, caret, TRUE );
2432 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2433 SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2439 /***********************************************************************
2442 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2445 MEASUREITEMSTRUCT mis;
2448 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2451 GetClientRect( hwnd, &rect );
2453 descr->owner = GetParent( descr->self );
2454 descr->style = GetWindowLongW( descr->self, GWL_STYLE );
2455 descr->width = rect.right - rect.left;
2456 descr->height = rect.bottom - rect.top;
2457 descr->items = NULL;
2458 descr->nb_items = 0;
2459 descr->top_item = 0;
2460 descr->selected_item = -1;
2461 descr->focus_item = 0;
2462 descr->anchor_item = -1;
2463 descr->item_height = 1;
2464 descr->page_size = 1;
2465 descr->column_width = 150;
2466 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0;
2467 descr->horz_pos = 0;
2470 descr->caret_on = lphc ? FALSE : TRUE;
2471 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2472 descr->in_focus = FALSE;
2473 descr->captured = FALSE;
2475 descr->locale = GetUserDefaultLCID();
2478 if (is_old_app(descr) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2480 /* Win95 document "List Box Differences" from MSDN:
2481 If a list box in a version 3.x application has either the
2482 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2483 horizontal and vertical scroll bars.
2485 descr->style |= WS_VSCROLL | WS_HSCROLL;
2490 TRACE("[%p]: resetting owner %p -> %p\n", descr->self, descr->owner, lphc->self );
2491 descr->owner = lphc->self;
2494 SetWindowLongW( descr->self, 0, (LONG)descr );
2496 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2498 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2499 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2500 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2501 descr->item_height = LISTBOX_SetFont( descr, 0 );
2503 if (descr->style & LBS_OWNERDRAWFIXED)
2505 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2507 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2508 descr->item_height = lphc->fixedOwnerDrawHeight;
2512 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
2513 mis.CtlType = ODT_LISTBOX;
2518 mis.itemHeight = descr->item_height;
2519 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2520 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2524 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2529 /***********************************************************************
2532 static BOOL LISTBOX_Destroy( LB_DESCR *descr )
2534 LISTBOX_ResetContent( descr );
2535 SetWindowLongW( descr->self, 0, 0 );
2536 HeapFree( GetProcessHeap(), 0, descr );
2541 /***********************************************************************
2542 * ListBoxWndProc_common
2544 static LRESULT WINAPI ListBoxWndProc_common( HWND hwnd, UINT msg,
2545 WPARAM wParam, LPARAM lParam, BOOL unicode )
2547 LB_DESCR *descr = (LB_DESCR *)GetWindowLongW( hwnd, 0 );
2548 LPHEADCOMBO lphc = 0;
2553 if (!IsWindow(hwnd)) return 0;
2555 if (msg == WM_CREATE)
2557 CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam;
2558 if (lpcs->style & LBS_COMBOBOX) lphc = (LPHEADCOMBO)lpcs->lpCreateParams;
2559 if (!LISTBOX_Create( hwnd, lphc )) return -1;
2560 TRACE("creating wnd=%p descr=%lx\n", hwnd, GetWindowLongW( hwnd, 0 ) );
2563 /* Ignore all other messages before we get a WM_CREATE */
2564 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2565 DefWindowProcA( hwnd, msg, wParam, lParam );
2567 if (descr->style & LBS_COMBOBOX) lphc = descr->lphc;
2569 TRACE("[%p]: msg %s wp %08x lp %08lx\n",
2570 descr->self, SPY_GetMsgName(msg, descr->self), wParam, lParam );
2574 case LB_RESETCONTENT16:
2575 case LB_RESETCONTENT:
2576 LISTBOX_ResetContent( descr );
2577 LISTBOX_UpdateScroll( descr );
2578 InvalidateRect( descr->self, NULL, TRUE );
2581 case LB_ADDSTRING16:
2582 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2588 if(unicode || !HAS_STRINGS(descr))
2589 textW = (LPWSTR)lParam;
2592 LPSTR textA = (LPSTR)lParam;
2593 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2594 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2595 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2597 wParam = LISTBOX_FindStringPos( descr, textW, FALSE );
2598 ret = LISTBOX_InsertString( descr, wParam, textW );
2599 if (!unicode && HAS_STRINGS(descr))
2600 HeapFree(GetProcessHeap(), 0, textW);
2604 case LB_INSERTSTRING16:
2605 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2606 wParam = (INT)(INT16)wParam;
2608 case LB_INSERTSTRING:
2612 if(unicode || !HAS_STRINGS(descr))
2613 textW = (LPWSTR)lParam;
2616 LPSTR textA = (LPSTR)lParam;
2617 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2618 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2619 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2621 ret = LISTBOX_InsertString( descr, wParam, textW );
2622 if(!unicode && HAS_STRINGS(descr))
2623 HeapFree(GetProcessHeap(), 0, textW);
2628 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2634 if(unicode || !HAS_STRINGS(descr))
2635 textW = (LPWSTR)lParam;
2638 LPSTR textA = (LPSTR)lParam;
2639 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2640 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2641 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2643 wParam = LISTBOX_FindFileStrPos( descr, textW );
2644 ret = LISTBOX_InsertString( descr, wParam, textW );
2645 if(!unicode && HAS_STRINGS(descr))
2646 HeapFree(GetProcessHeap(), 0, textW);
2650 case LB_DELETESTRING16:
2651 case LB_DELETESTRING:
2652 if (LISTBOX_RemoveItem( descr, wParam) != LB_ERR)
2653 return descr->nb_items;
2656 SetLastError(ERROR_INVALID_INDEX);
2660 case LB_GETITEMDATA16:
2661 case LB_GETITEMDATA:
2662 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2664 SetLastError(ERROR_INVALID_INDEX);
2667 return descr->items[wParam].data;
2669 case LB_SETITEMDATA16:
2670 case LB_SETITEMDATA:
2671 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2673 SetLastError(ERROR_INVALID_INDEX);
2676 descr->items[wParam].data = (DWORD)lParam;
2681 return descr->nb_items;
2684 lParam = (LPARAM)MapSL(lParam);
2687 return LISTBOX_GetText( descr, wParam, (LPWSTR)lParam, unicode );
2689 case LB_GETTEXTLEN16:
2692 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2694 SetLastError(ERROR_INVALID_INDEX);
2697 if (!HAS_STRINGS(descr)) return sizeof(DWORD);
2698 if (unicode) return strlenW( descr->items[wParam].str );
2699 return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
2700 strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
2702 case LB_GETCURSEL16:
2704 if (descr->nb_items == 0)
2706 if (!IS_MULTISELECT(descr))
2707 return descr->selected_item;
2708 if (descr->selected_item != -1)
2709 return descr->selected_item;
2710 return descr->focus_item;
2711 /* otherwise, if the user tries to move the selection with the */
2712 /* arrow keys, we will give the application something to choke on */
2713 case LB_GETTOPINDEX16:
2714 case LB_GETTOPINDEX:
2715 return descr->top_item;
2717 case LB_GETITEMHEIGHT16:
2718 case LB_GETITEMHEIGHT:
2719 return LISTBOX_GetItemHeight( descr, wParam );
2721 case LB_SETITEMHEIGHT16:
2722 lParam = LOWORD(lParam);
2724 case LB_SETITEMHEIGHT:
2725 return LISTBOX_SetItemHeight( descr, wParam, lParam, TRUE );
2727 case LB_ITEMFROMPOINT:
2732 pt.x = LOWORD(lParam);
2733 pt.y = HIWORD(lParam);
2736 rect.right = descr->width;
2737 rect.bottom = descr->height;
2739 return MAKELONG( LISTBOX_GetItemFromPoint(descr, pt.x, pt.y),
2740 !PtInRect( &rect, pt ) );
2743 case LB_SETCARETINDEX16:
2744 case LB_SETCARETINDEX:
2745 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2746 if (LISTBOX_SetCaretIndex( descr, wParam, !lParam ) == LB_ERR)
2753 case LB_GETCARETINDEX16:
2754 case LB_GETCARETINDEX:
2755 return descr->focus_item;
2757 case LB_SETTOPINDEX16:
2758 case LB_SETTOPINDEX:
2759 return LISTBOX_SetTopItem( descr, wParam, TRUE );
2761 case LB_SETCOLUMNWIDTH16:
2762 case LB_SETCOLUMNWIDTH:
2763 return LISTBOX_SetColumnWidth( descr, wParam );
2765 case LB_GETITEMRECT16:
2768 RECT16 *r16 = MapSL(lParam);
2769 ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2770 r16->left = rect.left;
2771 r16->top = rect.top;
2772 r16->right = rect.right;
2773 r16->bottom = rect.bottom;
2777 case LB_GETITEMRECT:
2778 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2780 case LB_FINDSTRING16:
2781 wParam = (INT)(INT16)wParam;
2782 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2788 if(unicode || !HAS_STRINGS(descr))
2789 textW = (LPWSTR)lParam;
2792 LPSTR textA = (LPSTR)lParam;
2793 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2794 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2795 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2797 ret = LISTBOX_FindString( descr, wParam, textW, FALSE );
2798 if(!unicode && HAS_STRINGS(descr))
2799 HeapFree(GetProcessHeap(), 0, textW);
2803 case LB_FINDSTRINGEXACT16:
2804 wParam = (INT)(INT16)wParam;
2805 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2807 case LB_FINDSTRINGEXACT:
2811 if(unicode || !HAS_STRINGS(descr))
2812 textW = (LPWSTR)lParam;
2815 LPSTR textA = (LPSTR)lParam;
2816 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2817 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2818 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2820 ret = LISTBOX_FindString( descr, wParam, textW, TRUE );
2821 if(!unicode && HAS_STRINGS(descr))
2822 HeapFree(GetProcessHeap(), 0, textW);
2826 case LB_SELECTSTRING16:
2827 wParam = (INT)(INT16)wParam;
2828 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2830 case LB_SELECTSTRING:
2835 if(HAS_STRINGS(descr))
2836 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2837 debugstr_a((LPSTR)lParam));
2838 if(unicode || !HAS_STRINGS(descr))
2839 textW = (LPWSTR)lParam;
2842 LPSTR textA = (LPSTR)lParam;
2843 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2844 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2845 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2847 index = LISTBOX_FindString( descr, wParam, textW, FALSE );
2848 if(!unicode && HAS_STRINGS(descr))
2849 HeapFree(GetProcessHeap(), 0, textW);
2850 if (index != LB_ERR)
2852 LISTBOX_MoveCaret( descr, index, TRUE );
2853 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
2859 wParam = (INT)(INT16)wParam;
2862 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2864 return descr->items[wParam].selected;
2867 lParam = (INT)(INT16)lParam;
2870 return LISTBOX_SetSelection( descr, lParam, wParam, FALSE );
2872 case LB_SETCURSEL16:
2873 wParam = (INT)(INT16)wParam;
2876 if (IS_MULTISELECT(descr)) return LB_ERR;
2877 LISTBOX_SetCaretIndex( descr, wParam, TRUE );
2878 ret = LISTBOX_SetSelection( descr, wParam, TRUE, FALSE );
2879 if (lphc && ret != LB_ERR) ret = descr->selected_item;
2882 case LB_GETSELCOUNT16:
2883 case LB_GETSELCOUNT:
2884 return LISTBOX_GetSelCount( descr );
2886 case LB_GETSELITEMS16:
2887 return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2889 case LB_GETSELITEMS:
2890 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2892 case LB_SELITEMRANGE16:
2893 case LB_SELITEMRANGE:
2894 if (LOWORD(lParam) <= HIWORD(lParam))
2895 return LISTBOX_SelectItemRange( descr, LOWORD(lParam),
2896 HIWORD(lParam), wParam );
2898 return LISTBOX_SelectItemRange( descr, HIWORD(lParam),
2899 LOWORD(lParam), wParam );
2901 case LB_SELITEMRANGEEX16:
2902 case LB_SELITEMRANGEEX:
2903 if ((INT)lParam >= (INT)wParam)
2904 return LISTBOX_SelectItemRange( descr, wParam, lParam, TRUE );
2906 return LISTBOX_SelectItemRange( descr, lParam, wParam, FALSE);
2908 case LB_GETHORIZONTALEXTENT16:
2909 case LB_GETHORIZONTALEXTENT:
2910 return descr->horz_extent;
2912 case LB_SETHORIZONTALEXTENT16:
2913 case LB_SETHORIZONTALEXTENT:
2914 return LISTBOX_SetHorizontalExtent( descr, wParam );
2916 case LB_GETANCHORINDEX16:
2917 case LB_GETANCHORINDEX:
2918 return descr->anchor_item;
2920 case LB_SETANCHORINDEX16:
2921 wParam = (INT)(INT16)wParam;
2923 case LB_SETANCHORINDEX:
2924 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2926 SetLastError(ERROR_INVALID_INDEX);
2929 descr->anchor_item = (INT)wParam;
2933 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2934 * be set automatically (this is different in Win32) */
2935 if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2936 lParam = (LPARAM)MapSL(lParam);
2943 textW = (LPWSTR)lParam;
2946 LPSTR textA = (LPSTR)lParam;
2947 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2948 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2949 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2951 ret = LISTBOX_Directory( descr, wParam, textW, msg == LB_DIR );
2953 HeapFree(GetProcessHeap(), 0, textW);
2958 return descr->locale;
2963 if (!IsValidLocale((LCID)wParam, LCID_INSTALLED))
2965 ret = descr->locale;
2966 descr->locale = (LCID)wParam;
2970 case LB_INITSTORAGE:
2971 return LISTBOX_InitStorage( descr, wParam );
2974 return LISTBOX_SetCount( descr, (INT)wParam );
2976 case LB_SETTABSTOPS16:
2977 return LISTBOX_SetTabStops( descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2979 case LB_SETTABSTOPS:
2980 return LISTBOX_SetTabStops( descr, wParam, (LPINT)lParam, FALSE );
2984 if (descr->caret_on)
2986 descr->caret_on = TRUE;
2987 if ((descr->focus_item != -1) && (descr->in_focus))
2988 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2993 if (!descr->caret_on)
2995 descr->caret_on = FALSE;
2996 if ((descr->focus_item != -1) && (descr->in_focus))
2997 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3001 return LISTBOX_Destroy( descr );
3004 InvalidateRect( descr->self, NULL, TRUE );
3008 LISTBOX_SetRedraw( descr, wParam != 0 );
3012 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3017 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( descr->self, &ps );
3018 ret = LISTBOX_Paint( descr, hdc );
3019 if( !wParam ) EndPaint( descr->self, &ps );
3023 LISTBOX_UpdateSize( descr );
3026 return (LRESULT)descr->font;
3028 LISTBOX_SetFont( descr, (HFONT)wParam );
3029 if (lParam) InvalidateRect( descr->self, 0, TRUE );
3032 descr->in_focus = TRUE;
3033 descr->caret_on = TRUE;
3034 if (descr->focus_item != -1)
3035 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3036 SEND_NOTIFICATION( descr, LBN_SETFOCUS );
3039 descr->in_focus = FALSE;
3040 if ((descr->focus_item != -1) && descr->caret_on)
3041 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3042 SEND_NOTIFICATION( descr, LBN_KILLFOCUS );
3045 return LISTBOX_HandleHScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3047 return LISTBOX_HandleVScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3049 if (wParam & (MK_SHIFT | MK_CONTROL))
3050 return DefWindowProcW( descr->self, msg, wParam, lParam );
3051 return LISTBOX_HandleMouseWheel( descr, (SHORT)HIWORD(wParam) );
3052 case WM_LBUTTONDOWN:
3054 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3055 (INT16)LOWORD(lParam),
3056 (INT16)HIWORD(lParam) );
3057 return LISTBOX_HandleLButtonDown( descr, wParam,
3058 (INT16)LOWORD(lParam),
3059 (INT16)HIWORD(lParam) );
3060 case WM_LBUTTONDBLCLK:
3062 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3063 (INT16)LOWORD(lParam),
3064 (INT16)HIWORD(lParam) );
3065 if (descr->style & LBS_NOTIFY)
3066 SEND_NOTIFICATION( descr, LBN_DBLCLK );
3069 if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) )
3071 BOOL captured = descr->captured;
3075 mousePos.x = (INT16)LOWORD(lParam);
3076 mousePos.y = (INT16)HIWORD(lParam);
3079 * If we are in a dropdown combobox, we simulate that
3080 * the mouse is captured to show the tracking of the item.
3082 if (GetClientRect(descr->self, &clientRect) && PtInRect( &clientRect, mousePos ))
3083 descr->captured = TRUE;
3085 LISTBOX_HandleMouseMove( descr, mousePos.x, mousePos.y);
3087 descr->captured = captured;
3089 else if (GetCapture() == descr->self)
3091 LISTBOX_HandleMouseMove( descr, (INT16)LOWORD(lParam),
3092 (INT16)HIWORD(lParam) );
3102 * If the mouse button "up" is not in the listbox,
3103 * we make sure there is no selection by re-selecting the
3104 * item that was selected when the listbox was made visible.
3106 mousePos.x = (INT16)LOWORD(lParam);
3107 mousePos.y = (INT16)HIWORD(lParam);
3109 GetClientRect(descr->self, &clientRect);
3112 * When the user clicks outside the combobox and the focus
3113 * is lost, the owning combobox will send a fake buttonup with
3114 * 0xFFFFFFF as the mouse location, we must also revert the
3115 * selection to the original selection.
3117 if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) )
3118 LISTBOX_MoveCaret( descr, lphc->droppedIndex, FALSE );
3120 return LISTBOX_HandleLButtonUp( descr );
3122 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3124 /* for some reason Windows makes it possible to
3125 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3127 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3128 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3129 && (wParam == VK_DOWN || wParam == VK_UP)) )
3131 COMBO_FlipListbox( lphc, FALSE, FALSE );
3135 return LISTBOX_HandleKeyDown( descr, wParam );
3140 charW = (WCHAR)wParam;
3143 CHAR charA = (CHAR)wParam;
3144 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
3146 return LISTBOX_HandleChar( descr, charW );
3149 return LISTBOX_HandleSystemTimer( descr );
3151 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
3154 HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
3155 wParam, (LPARAM)descr->self );
3156 TRACE("hbrush = %p\n", hbrush);
3158 hbrush = GetSysColorBrush(COLOR_WINDOW);
3161 GetClientRect(descr->self, &rect);
3162 FillRect((HDC)wParam, &rect, hbrush);
3167 if( lphc ) return 0;
3168 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3169 SendMessageA( descr->owner, msg, wParam, lParam );
3172 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3181 if ((msg >= WM_USER) && (msg < 0xc000))
3182 WARN("[%p]: unknown msg %04x wp %08x lp %08lx\n",
3183 hwnd, msg, wParam, lParam );
3186 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3187 DefWindowProcA( hwnd, msg, wParam, lParam );
3190 /***********************************************************************
3193 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3195 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3198 /***********************************************************************
3201 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3203 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE );