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))
548 if (action == ODA_FOCUS)
549 DrawFocusRect( hdc, rect );
551 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
555 /* some programs mess with the clipping region when
556 drawing the item, *and* restore the previous region
557 after they are done, so a region has better to exist
558 else everything ends clipped */
559 GetClientRect(descr->self, &r);
560 hrgn = CreateRectRgnIndirect(&r);
561 SelectClipRgn( hdc, hrgn);
562 DeleteObject( hrgn );
564 dis.CtlType = ODT_LISTBOX;
565 dis.CtlID = GetWindowLongPtrW( descr->self, GWLP_ID );
566 dis.hwndItem = descr->self;
567 dis.itemAction = action;
571 if (item && item->selected) dis.itemState |= ODS_SELECTED;
572 if (!ignoreFocus && (descr->focus_item == index) &&
574 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
575 if (!IsWindowEnabled(descr->self)) dis.itemState |= ODS_DISABLED;
576 dis.itemData = item ? item->data : 0;
578 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%ld,%ld-%ld,%ld\n",
579 descr->self, index, item ? debugstr_w(item->str) : "", action,
580 dis.itemState, rect->left, rect->top, rect->right, rect->bottom );
581 SendMessageW(descr->owner, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
585 COLORREF oldText = 0, oldBk = 0;
587 if (action == ODA_FOCUS)
589 DrawFocusRect( hdc, rect );
592 if (item && item->selected)
594 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
595 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
598 TRACE("[%p]: painting %d (%s) action=%02x rect=%ld,%ld-%ld,%ld\n",
599 descr->self, index, item ? debugstr_w(item->str) : "", action,
600 rect->left, rect->top, rect->right, rect->bottom );
602 ExtTextOutW( hdc, rect->left + 1, rect->top,
603 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
604 else if (!(descr->style & LBS_USETABSTOPS))
605 ExtTextOutW( hdc, rect->left + 1, rect->top,
606 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
607 strlenW(item->str), NULL );
610 /* Output empty string to paint background in the full width. */
611 ExtTextOutW( hdc, rect->left + 1, rect->top,
612 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
613 TabbedTextOutW( hdc, rect->left + 1 , rect->top,
614 item->str, strlenW(item->str),
615 descr->nb_tabs, descr->tabs, 0);
617 if (item && item->selected)
619 SetBkColor( hdc, oldBk );
620 SetTextColor( hdc, oldText );
622 if (!ignoreFocus && (descr->focus_item == index) &&
624 (descr->in_focus)) DrawFocusRect( hdc, rect );
629 /***********************************************************************
632 * Change the redraw flag.
634 static void LISTBOX_SetRedraw( LB_DESCR *descr, BOOL on )
638 if (!(descr->style & LBS_NOREDRAW)) return;
639 descr->style &= ~LBS_NOREDRAW;
640 if (descr->style & LBS_DISPLAYCHANGED)
641 { /* page was changed while setredraw false, refresh automatically */
642 InvalidateRect(descr->self, NULL, TRUE);
643 if ((descr->top_item + descr->page_size) > descr->nb_items)
644 { /* reset top of page if less than number of items/page */
645 descr->top_item = descr->nb_items - descr->page_size;
646 if (descr->top_item < 0) descr->top_item = 0;
648 descr->style &= ~LBS_DISPLAYCHANGED;
650 LISTBOX_UpdateScroll( descr );
652 else descr->style |= LBS_NOREDRAW;
656 /***********************************************************************
657 * LISTBOX_RepaintItem
659 * Repaint a single item synchronously.
661 static void LISTBOX_RepaintItem( LB_DESCR *descr, INT index, UINT action )
666 HBRUSH hbrush, oldBrush = 0;
668 /* Do not repaint the item if the item is not visible */
669 if (!IsWindowVisible(descr->self)) return;
670 if (descr->style & LBS_NOREDRAW)
672 descr->style |= LBS_DISPLAYCHANGED;
675 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
676 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
677 if (descr->font) oldFont = SelectObject( hdc, descr->font );
678 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
679 (WPARAM)hdc, (LPARAM)descr->self );
680 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
681 if (!IsWindowEnabled(descr->self))
682 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
683 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
684 LISTBOX_PaintItem( descr, hdc, &rect, index, action, FALSE );
685 if (oldFont) SelectObject( hdc, oldFont );
686 if (oldBrush) SelectObject( hdc, oldBrush );
687 ReleaseDC( descr->self, hdc );
691 /***********************************************************************
692 * LISTBOX_InitStorage
694 static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items )
698 nb_items += LB_ARRAY_GRANULARITY - 1;
699 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
701 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
702 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
703 nb_items * sizeof(LB_ITEMDATA));
706 item = HeapAlloc( GetProcessHeap(), 0,
707 nb_items * sizeof(LB_ITEMDATA));
712 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
720 /***********************************************************************
721 * LISTBOX_SetTabStops
723 static BOOL LISTBOX_SetTabStops( LB_DESCR *descr, INT count, LPINT tabs, BOOL short_ints )
727 if (!(descr->style & LBS_USETABSTOPS))
729 SetLastError(ERROR_LB_WITHOUT_TABSTOPS);
733 HeapFree( GetProcessHeap(), 0, descr->tabs );
734 if (!(descr->nb_tabs = count))
739 if (!(descr->tabs = HeapAlloc( GetProcessHeap(), 0,
740 descr->nb_tabs * sizeof(INT) )))
745 LPINT16 p = (LPINT16)tabs;
747 TRACE("[%p]: settabstops ", descr->self );
748 for (i = 0; i < descr->nb_tabs; i++) {
749 descr->tabs[i] = *p++<<1; /* FIXME */
750 TRACE("%hd ", descr->tabs[i]);
754 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
756 /* convert into "dialog units"*/
757 for (i = 0; i < descr->nb_tabs; i++)
758 descr->tabs[i] = MulDiv(descr->tabs[i], descr->avg_char_width, 4);
764 /***********************************************************************
767 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPWSTR buffer, BOOL unicode )
769 if ((index < 0) || (index >= descr->nb_items))
771 SetLastError(ERROR_INVALID_INDEX);
774 if (HAS_STRINGS(descr))
778 DWORD len = strlenW(descr->items[index].str);
781 return WideCharToMultiByte( CP_ACP, 0, descr->items[index].str, len,
782 NULL, 0, NULL, NULL );
785 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
789 strcpyW( buffer, descr->items[index].str );
790 return strlenW(buffer);
794 return WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, (LPSTR)buffer, 0x7FFFFFFF, NULL, NULL) - 1;
798 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
799 return sizeof(DWORD);
803 static inline INT LISTBOX_lstrcmpiW( LCID lcid, LPCWSTR str1, LPCWSTR str2 )
805 INT ret = CompareStringW( lcid, NORM_IGNORECASE, str1, -1, str2, -1 );
806 if (ret == CSTR_LESS_THAN)
808 if (ret == CSTR_EQUAL)
810 if (ret == CSTR_GREATER_THAN)
815 /***********************************************************************
816 * LISTBOX_FindStringPos
818 * Find the nearest string located before a given string in sort order.
819 * If 'exact' is TRUE, return an error if we don't get an exact match.
821 static INT LISTBOX_FindStringPos( LB_DESCR *descr, LPCWSTR str, BOOL exact )
823 INT index, min, max, res = -1;
825 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
827 max = descr->nb_items;
830 index = (min + max) / 2;
831 if (HAS_STRINGS(descr))
832 res = LISTBOX_lstrcmpiW( descr->locale, str, descr->items[index].str);
835 COMPAREITEMSTRUCT cis;
836 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
838 cis.CtlType = ODT_LISTBOX;
840 cis.hwndItem = descr->self;
841 /* note that some application (MetaStock) expects the second item
842 * to be in the listbox */
844 cis.itemData1 = (DWORD)str;
846 cis.itemData2 = descr->items[index].data;
847 cis.dwLocaleId = descr->locale;
848 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
850 if (!res) return index;
851 if (res < 0) max = index;
852 else min = index + 1;
854 return exact ? -1 : max;
858 /***********************************************************************
859 * LISTBOX_FindFileStrPos
861 * Find the nearest string located before a given string in directory
862 * sort order (i.e. first files, then directories, then drives).
864 static INT LISTBOX_FindFileStrPos( LB_DESCR *descr, LPCWSTR str )
866 INT min, max, res = -1;
868 if (!HAS_STRINGS(descr))
869 return LISTBOX_FindStringPos( descr, str, FALSE );
871 max = descr->nb_items;
874 INT index = (min + max) / 2;
875 LPCWSTR p = descr->items[index].str;
876 if (*p == '[') /* drive or directory */
878 if (*str != '[') res = -1;
879 else if (p[1] == '-') /* drive */
881 if (str[1] == '-') res = str[2] - p[2];
886 if (str[1] == '-') res = 1;
887 else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
892 if (*str == '[') res = 1;
893 else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
895 if (!res) return index;
896 if (res < 0) max = index;
897 else min = index + 1;
903 /***********************************************************************
906 * Find the item beginning with a given string.
908 static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exact )
913 if (start >= descr->nb_items) start = -1;
914 item = descr->items + start + 1;
915 if (HAS_STRINGS(descr))
917 if (!str || ! str[0] ) return LB_ERR;
920 for (i = start + 1; i < descr->nb_items; i++, item++)
921 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
922 for (i = 0, item = descr->items; i <= start; i++, item++)
923 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
927 /* Special case for drives and directories: ignore prefix */
928 #define CHECK_DRIVE(item) \
929 if ((item)->str[0] == '[') \
931 if (!strncmpiW( str, (item)->str+1, len )) return i; \
932 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
936 INT len = strlenW(str);
937 for (i = start + 1; i < descr->nb_items; i++, item++)
939 if (!strncmpiW( str, item->str, len )) return i;
942 for (i = 0, item = descr->items; i <= start; i++, item++)
944 if (!strncmpiW( str, item->str, len )) return i;
952 if (exact && (descr->style & LBS_SORT))
953 /* If sorted, use a WM_COMPAREITEM binary search */
954 return LISTBOX_FindStringPos( descr, str, TRUE );
956 /* Otherwise use a linear search */
957 for (i = start + 1; i < descr->nb_items; i++, item++)
958 if (item->data == (DWORD)str) return i;
959 for (i = 0, item = descr->items; i <= start; i++, item++)
960 if (item->data == (DWORD)str) return i;
966 /***********************************************************************
967 * LISTBOX_GetSelCount
969 static LRESULT LISTBOX_GetSelCount( LB_DESCR *descr )
972 LB_ITEMDATA *item = descr->items;
974 if (!(descr->style & LBS_MULTIPLESEL) ||
975 (descr->style & LBS_NOSEL))
977 for (i = count = 0; i < descr->nb_items; i++, item++)
978 if (item->selected) count++;
983 /***********************************************************************
984 * LISTBOX_GetSelItems16
986 static LRESULT LISTBOX_GetSelItems16( LB_DESCR *descr, INT16 max, LPINT16 array )
989 LB_ITEMDATA *item = descr->items;
991 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
992 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
993 if (item->selected) array[count++] = (INT16)i;
998 /***********************************************************************
999 * LISTBOX_GetSelItems
1001 static LRESULT LISTBOX_GetSelItems( LB_DESCR *descr, INT max, LPINT array )
1004 LB_ITEMDATA *item = descr->items;
1006 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1007 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
1008 if (item->selected) array[count++] = i;
1013 /***********************************************************************
1016 static LRESULT LISTBOX_Paint( LB_DESCR *descr, HDC hdc )
1018 INT i, col_pos = descr->page_size - 1;
1020 RECT focusRect = {-1, -1, -1, -1};
1022 HBRUSH hbrush, oldBrush = 0;
1024 if (descr->style & LBS_NOREDRAW) return 0;
1026 SetRect( &rect, 0, 0, descr->width, descr->height );
1027 if (descr->style & LBS_MULTICOLUMN)
1028 rect.right = rect.left + descr->column_width;
1029 else if (descr->horz_pos)
1031 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
1032 rect.right += descr->horz_pos;
1035 if (descr->font) oldFont = SelectObject( hdc, descr->font );
1036 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1037 (WPARAM)hdc, (LPARAM)descr->self );
1038 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1039 if (!IsWindowEnabled(descr->self)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1041 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1044 /* Special case for empty listbox: paint focus rect */
1045 rect.bottom = rect.top + descr->item_height;
1046 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1047 &rect, NULL, 0, NULL );
1048 LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, FALSE );
1049 rect.top = rect.bottom;
1052 /* Paint all the item, regarding the selection
1053 Focus state will be painted after */
1055 for (i = descr->top_item; i < descr->nb_items; i++)
1057 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1058 rect.bottom = rect.top + descr->item_height;
1060 rect.bottom = rect.top + descr->items[i].height;
1062 if (i == descr->focus_item)
1064 /* keep the focus rect, to paint the focus item after */
1065 focusRect.left = rect.left;
1066 focusRect.right = rect.right;
1067 focusRect.top = rect.top;
1068 focusRect.bottom = rect.bottom;
1070 LISTBOX_PaintItem( descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1071 rect.top = rect.bottom;
1073 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1075 if (!IS_OWNERDRAW(descr))
1077 /* Clear the bottom of the column */
1078 if (rect.top < descr->height)
1080 rect.bottom = descr->height;
1081 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1082 &rect, NULL, 0, NULL );
1086 /* Go to the next column */
1087 rect.left += descr->column_width;
1088 rect.right += descr->column_width;
1090 col_pos = descr->page_size - 1;
1095 if (rect.top >= descr->height) break;
1099 /* Paint the focus item now */
1100 if (focusRect.top != focusRect.bottom &&
1101 descr->caret_on && descr->in_focus)
1102 LISTBOX_PaintItem( descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1104 if (!IS_OWNERDRAW(descr))
1106 /* Clear the remainder of the client area */
1107 if (rect.top < descr->height)
1109 rect.bottom = descr->height;
1110 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1111 &rect, NULL, 0, NULL );
1113 if (rect.right < descr->width)
1115 rect.left = rect.right;
1116 rect.right = descr->width;
1118 rect.bottom = descr->height;
1119 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1120 &rect, NULL, 0, NULL );
1123 if (oldFont) SelectObject( hdc, oldFont );
1124 if (oldBrush) SelectObject( hdc, oldBrush );
1129 /***********************************************************************
1130 * LISTBOX_InvalidateItems
1132 * Invalidate all items from a given item. If the specified item is not
1133 * visible, nothing happens.
1135 static void LISTBOX_InvalidateItems( LB_DESCR *descr, INT index )
1139 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1141 if (descr->style & LBS_NOREDRAW)
1143 descr->style |= LBS_DISPLAYCHANGED;
1146 rect.bottom = descr->height;
1147 InvalidateRect( descr->self, &rect, TRUE );
1148 if (descr->style & LBS_MULTICOLUMN)
1150 /* Repaint the other columns */
1151 rect.left = rect.right;
1152 rect.right = descr->width;
1154 InvalidateRect( descr->self, &rect, TRUE );
1159 static void LISTBOX_InvalidateItemRect( LB_DESCR *descr, INT index )
1163 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1164 InvalidateRect( descr->self, &rect, TRUE );
1167 /***********************************************************************
1168 * LISTBOX_GetItemHeight
1170 static LRESULT LISTBOX_GetItemHeight( LB_DESCR *descr, INT index )
1172 if (descr->style & LBS_OWNERDRAWVARIABLE)
1174 if ((index < 0) || (index >= descr->nb_items))
1176 SetLastError(ERROR_INVALID_INDEX);
1179 return descr->items[index].height;
1181 else return descr->item_height;
1185 /***********************************************************************
1186 * LISTBOX_SetItemHeight
1188 static LRESULT LISTBOX_SetItemHeight( LB_DESCR *descr, INT index, INT height, BOOL repaint )
1190 if (!height) height = 1;
1192 if (descr->style & LBS_OWNERDRAWVARIABLE)
1194 if ((index < 0) || (index >= descr->nb_items))
1196 SetLastError(ERROR_INVALID_INDEX);
1199 TRACE("[%p]: item %d height = %d\n", descr->self, index, height );
1200 descr->items[index].height = height;
1201 LISTBOX_UpdateScroll( descr );
1203 LISTBOX_InvalidateItems( descr, index );
1205 else if (height != descr->item_height)
1207 TRACE("[%p]: new height = %d\n", descr->self, height );
1208 descr->item_height = height;
1209 LISTBOX_UpdatePage( descr );
1210 LISTBOX_UpdateScroll( descr );
1212 InvalidateRect( descr->self, 0, TRUE );
1218 /***********************************************************************
1219 * LISTBOX_SetHorizontalPos
1221 static void LISTBOX_SetHorizontalPos( LB_DESCR *descr, INT pos )
1225 if (pos > descr->horz_extent - descr->width)
1226 pos = descr->horz_extent - descr->width;
1227 if (pos < 0) pos = 0;
1228 if (!(diff = descr->horz_pos - pos)) return;
1229 TRACE("[%p]: new horz pos = %d\n", descr->self, pos );
1230 descr->horz_pos = pos;
1231 LISTBOX_UpdateScroll( descr );
1232 if (abs(diff) < descr->width)
1235 /* Invalidate the focused item so it will be repainted correctly */
1236 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
1237 InvalidateRect( descr->self, &rect, TRUE );
1238 ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
1239 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1242 InvalidateRect( descr->self, NULL, TRUE );
1246 /***********************************************************************
1247 * LISTBOX_SetHorizontalExtent
1249 static LRESULT LISTBOX_SetHorizontalExtent( LB_DESCR *descr, INT extent )
1251 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1253 if (extent <= 0) extent = 1;
1254 if (extent == descr->horz_extent) return LB_OKAY;
1255 TRACE("[%p]: new horz extent = %d\n", descr->self, extent );
1256 descr->horz_extent = extent;
1257 if (descr->horz_pos > extent - descr->width)
1258 LISTBOX_SetHorizontalPos( descr, extent - descr->width );
1260 LISTBOX_UpdateScroll( descr );
1265 /***********************************************************************
1266 * LISTBOX_SetColumnWidth
1268 static LRESULT LISTBOX_SetColumnWidth( LB_DESCR *descr, INT width)
1270 if (width == descr->column_width) return LB_OKAY;
1271 TRACE("[%p]: new column width = %d\n", descr->self, width );
1272 descr->column_width = width;
1273 LISTBOX_UpdatePage( descr );
1278 /***********************************************************************
1281 * Returns the item height.
1283 static INT LISTBOX_SetFont( LB_DESCR *descr, HFONT font )
1287 const char *alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1292 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE )))
1294 ERR("unable to get DC.\n" );
1297 if (font) oldFont = SelectObject( hdc, font );
1298 GetTextExtentPointA( hdc, alphabet, 52, &sz);
1299 if (oldFont) SelectObject( hdc, oldFont );
1300 ReleaseDC( descr->self, hdc );
1302 descr->avg_char_width = (sz.cx / 26 + 1) / 2;
1303 if (!IS_OWNERDRAW(descr))
1304 LISTBOX_SetItemHeight( descr, 0, sz.cy, FALSE );
1309 /***********************************************************************
1310 * LISTBOX_MakeItemVisible
1312 * Make sure that a given item is partially or fully visible.
1314 static void LISTBOX_MakeItemVisible( LB_DESCR *descr, INT index, BOOL fully )
1318 if (index <= descr->top_item) top = index;
1319 else if (descr->style & LBS_MULTICOLUMN)
1321 INT cols = descr->width;
1322 if (!fully) cols += descr->column_width - 1;
1323 if (cols >= descr->column_width) cols /= descr->column_width;
1325 if (index < descr->top_item + (descr->page_size * cols)) return;
1326 top = index - descr->page_size * (cols - 1);
1328 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1330 INT height = fully ? descr->items[index].height : 1;
1331 for (top = index; top > descr->top_item; top--)
1332 if ((height += descr->items[top-1].height) > descr->height) break;
1336 if (index < descr->top_item + descr->page_size) return;
1337 if (!fully && (index == descr->top_item + descr->page_size) &&
1338 (descr->height > (descr->page_size * descr->item_height))) return;
1339 top = index - descr->page_size + 1;
1341 LISTBOX_SetTopItem( descr, top, TRUE );
1344 /***********************************************************************
1345 * LISTBOX_SetCaretIndex
1348 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1351 static LRESULT LISTBOX_SetCaretIndex( LB_DESCR *descr, INT index, BOOL fully_visible )
1353 INT oldfocus = descr->focus_item;
1355 if (descr->style & LBS_NOSEL) return LB_ERR;
1356 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1357 if (index == oldfocus) return LB_OKAY;
1358 descr->focus_item = index;
1359 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1360 LISTBOX_RepaintItem( descr, oldfocus, ODA_FOCUS );
1362 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1363 if (descr->caret_on && (descr->in_focus))
1364 LISTBOX_RepaintItem( descr, index, ODA_FOCUS );
1370 /***********************************************************************
1371 * LISTBOX_SelectItemRange
1373 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1375 static LRESULT LISTBOX_SelectItemRange( LB_DESCR *descr, INT first,
1380 /* A few sanity checks */
1382 if (descr->style & LBS_NOSEL) return LB_ERR;
1383 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1384 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1385 if (last == -1) last = descr->nb_items - 1;
1386 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1387 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1389 if (on) /* Turn selection on */
1391 for (i = first; i <= last; i++)
1393 if (descr->items[i].selected) continue;
1394 descr->items[i].selected = TRUE;
1395 LISTBOX_InvalidateItemRect(descr, i);
1398 else /* Turn selection off */
1400 for (i = first; i <= last; i++)
1402 if (!descr->items[i].selected) continue;
1403 descr->items[i].selected = FALSE;
1404 LISTBOX_InvalidateItemRect(descr, i);
1410 /***********************************************************************
1411 * LISTBOX_SetSelection
1413 static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index,
1414 BOOL on, BOOL send_notify )
1416 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1418 if (descr->style & LBS_NOSEL)
1420 descr->selected_item = index;
1423 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1424 if (descr->style & LBS_MULTIPLESEL)
1426 if (index == -1) /* Select all items */
1427 return LISTBOX_SelectItemRange( descr, 0, -1, on );
1428 else /* Only one item */
1429 return LISTBOX_SelectItemRange( descr, index, index, on );
1433 INT oldsel = descr->selected_item;
1434 if (index == oldsel) return LB_OKAY;
1435 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1436 if (index != -1) descr->items[index].selected = TRUE;
1437 descr->selected_item = index;
1438 if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT );
1439 if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT );
1440 if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr,
1441 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1443 if( descr->lphc ) /* set selection change flag for parent combo */
1444 descr->lphc->wState |= CBF_SELCHANGE;
1450 /***********************************************************************
1453 * Change the caret position and extend the selection to the new caret.
1455 static void LISTBOX_MoveCaret( LB_DESCR *descr, INT index, BOOL fully_visible )
1457 INT oldfocus = descr->focus_item;
1459 if ((index < 0) || (index >= descr->nb_items))
1462 /* Important, repaint needs to be done in this order if
1463 you want to mimic Windows behavior:
1464 1. Remove the focus and paint the item
1465 2. Remove the selection and paint the item(s)
1466 3. Set the selection and repaint the item(s)
1467 4. Set the focus to 'index' and repaint the item */
1469 /* 1. remove the focus and repaint the item */
1470 descr->focus_item = -1;
1471 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1472 LISTBOX_RepaintItem( descr, oldfocus, ODA_FOCUS );
1474 /* 2. then turn off the previous selection */
1475 /* 3. repaint the new selected item */
1476 if (descr->style & LBS_EXTENDEDSEL)
1478 if (descr->anchor_item != -1)
1480 INT first = min( index, descr->anchor_item );
1481 INT last = max( index, descr->anchor_item );
1483 LISTBOX_SelectItemRange( descr, 0, first - 1, FALSE );
1484 LISTBOX_SelectItemRange( descr, last + 1, -1, FALSE );
1485 LISTBOX_SelectItemRange( descr, first, last, TRUE );
1488 else if (!(descr->style & LBS_MULTIPLESEL))
1490 /* Set selection to new caret item */
1491 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
1494 /* 4. repaint the new item with the focus */
1495 descr->focus_item = index;
1496 LISTBOX_MakeItemVisible( descr, index, fully_visible );
1497 if (descr->caret_on && (descr->in_focus))
1498 LISTBOX_RepaintItem( descr, index, ODA_FOCUS );
1502 /***********************************************************************
1503 * LISTBOX_InsertItem
1505 static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index,
1506 LPWSTR str, DWORD data )
1510 INT oldfocus = descr->focus_item;
1512 if (index == -1) index = descr->nb_items;
1513 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1514 if (!descr->items) max_items = 0;
1515 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1516 if (descr->nb_items == max_items)
1518 /* We need to grow the array */
1519 max_items += LB_ARRAY_GRANULARITY;
1521 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1522 max_items * sizeof(LB_ITEMDATA) );
1524 item = HeapAlloc( GetProcessHeap(), 0,
1525 max_items * sizeof(LB_ITEMDATA) );
1528 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1531 descr->items = item;
1534 /* Insert the item structure */
1536 item = &descr->items[index];
1537 if (index < descr->nb_items)
1538 RtlMoveMemory( item + 1, item,
1539 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1543 item->selected = FALSE;
1546 /* Get item height */
1548 if (descr->style & LBS_OWNERDRAWVARIABLE)
1550 MEASUREITEMSTRUCT mis;
1551 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1553 mis.CtlType = ODT_LISTBOX;
1556 mis.itemData = descr->items[index].data;
1557 mis.itemHeight = descr->item_height;
1558 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1559 item->height = mis.itemHeight ? mis.itemHeight : 1;
1560 TRACE("[%p]: measure item %d (%s) = %d\n",
1561 descr->self, index, str ? debugstr_w(str) : "", item->height );
1564 /* Repaint the items */
1566 LISTBOX_UpdateScroll( descr );
1567 LISTBOX_InvalidateItems( descr, index );
1569 /* Move selection and focused item */
1570 /* If listbox was empty, set focus to the first item */
1571 if (descr->nb_items == 1)
1572 LISTBOX_SetCaretIndex( descr, 0, FALSE );
1573 /* single select don't change selection index in win31 */
1574 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1576 descr->selected_item++;
1577 LISTBOX_SetSelection( descr, descr->selected_item-1, TRUE, FALSE );
1581 if (index <= descr->selected_item)
1583 descr->selected_item++;
1584 descr->focus_item = oldfocus; /* focus not changed */
1591 /***********************************************************************
1592 * LISTBOX_InsertString
1594 static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index, LPCWSTR str )
1596 LPWSTR new_str = NULL;
1600 if (HAS_STRINGS(descr))
1602 static const WCHAR empty_stringW[] = { 0 };
1603 if (!str) str = empty_stringW;
1604 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1606 SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1609 strcpyW(new_str, str);
1611 else data = (DWORD)str;
1613 if (index == -1) index = descr->nb_items;
1614 if ((ret = LISTBOX_InsertItem( descr, index, new_str, data )) != 0)
1616 HeapFree( GetProcessHeap(), 0, new_str );
1620 TRACE("[%p]: added item %d %s\n",
1621 descr->self, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1626 /***********************************************************************
1627 * LISTBOX_DeleteItem
1629 * Delete the content of an item. 'index' must be a valid index.
1631 static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index )
1633 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1634 * while Win95 sends it for all items with user data.
1635 * It's probably better to send it too often than not
1636 * often enough, so this is what we do here.
1638 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1640 DELETEITEMSTRUCT dis;
1641 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1643 dis.CtlType = ODT_LISTBOX;
1646 dis.hwndItem = descr->self;
1647 dis.itemData = descr->items[index].data;
1648 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1650 if (HAS_STRINGS(descr) && descr->items[index].str)
1651 HeapFree( GetProcessHeap(), 0, descr->items[index].str );
1655 /***********************************************************************
1656 * LISTBOX_RemoveItem
1658 * Remove an item from the listbox and delete its content.
1660 static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index )
1665 if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1666 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1668 /* We need to invalidate the original rect instead of the updated one. */
1669 LISTBOX_InvalidateItems( descr, index );
1671 LISTBOX_DeleteItem( descr, index );
1673 /* Remove the item */
1675 item = &descr->items[index];
1676 if (index < descr->nb_items-1)
1677 RtlMoveMemory( item, item + 1,
1678 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1680 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1682 /* Shrink the item array if possible */
1684 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1685 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1687 max_items -= LB_ARRAY_GRANULARITY;
1688 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1689 max_items * sizeof(LB_ITEMDATA) );
1690 if (item) descr->items = item;
1692 /* Repaint the items */
1694 LISTBOX_UpdateScroll( descr );
1695 /* if we removed the scrollbar, reset the top of the list
1696 (correct for owner-drawn ???) */
1697 if (descr->nb_items == descr->page_size)
1698 LISTBOX_SetTopItem( descr, 0, TRUE );
1700 /* Move selection and focused item */
1701 if (!IS_MULTISELECT(descr))
1703 if (index == descr->selected_item)
1704 descr->selected_item = -1;
1705 else if (index < descr->selected_item)
1707 descr->selected_item--;
1708 if (ISWIN31) /* win 31 do not change the selected item number */
1709 LISTBOX_SetSelection( descr, descr->selected_item + 1, TRUE, FALSE);
1713 if (descr->focus_item >= descr->nb_items)
1715 descr->focus_item = descr->nb_items - 1;
1716 if (descr->focus_item < 0) descr->focus_item = 0;
1722 /***********************************************************************
1723 * LISTBOX_ResetContent
1725 static void LISTBOX_ResetContent( LB_DESCR *descr )
1729 for(i = descr->nb_items - 1; i>=0; i--) LISTBOX_DeleteItem( descr, i);
1730 HeapFree( GetProcessHeap(), 0, descr->items );
1731 descr->nb_items = 0;
1732 descr->top_item = 0;
1733 descr->selected_item = -1;
1734 descr->focus_item = 0;
1735 descr->anchor_item = -1;
1736 descr->items = NULL;
1740 /***********************************************************************
1743 static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count )
1747 if (HAS_STRINGS(descr))
1749 SetLastError(ERROR_SETCOUNT_ON_BAD_LB);
1753 /* FIXME: this is far from optimal... */
1754 if (count > descr->nb_items)
1756 while (count > descr->nb_items)
1757 if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0)
1760 else if (count < descr->nb_items)
1762 while (count < descr->nb_items)
1763 if ((ret = LISTBOX_RemoveItem( descr, -1 )) < 0)
1770 /***********************************************************************
1773 static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib,
1774 LPCWSTR filespec, BOOL long_names )
1777 LRESULT ret = LB_OKAY;
1778 WIN32_FIND_DATAW entry;
1781 /* don't scan directory if we just want drives exclusively */
1782 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1783 /* scan directory */
1784 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1786 int le = GetLastError();
1787 if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR;
1794 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1796 static const WCHAR bracketW[] = { ']',0 };
1797 static const WCHAR dotW[] = { '.',0 };
1798 if (!(attrib & DDL_DIRECTORY) ||
1799 !strcmpW( entry.cFileName, dotW )) continue;
1801 if (!long_names && entry.cAlternateFileName[0])
1802 strcpyW( buffer + 1, entry.cAlternateFileName );
1804 strcpyW( buffer + 1, entry.cFileName );
1805 strcatW(buffer, bracketW);
1807 else /* not a directory */
1809 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1810 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1812 if ((attrib & DDL_EXCLUSIVE) &&
1813 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1816 if (!long_names && entry.cAlternateFileName[0])
1817 strcpyW( buffer, entry.cAlternateFileName );
1819 strcpyW( buffer, entry.cFileName );
1821 if (!long_names) CharLowerW( buffer );
1822 pos = LISTBOX_FindFileStrPos( descr, buffer );
1823 if ((ret = LISTBOX_InsertString( descr, pos, buffer )) < 0)
1825 } while (FindNextFileW( handle, &entry ));
1826 FindClose( handle );
1831 if ((ret >= 0) && (attrib & DDL_DRIVES))
1833 WCHAR buffer[] = {'[','-','a','-',']',0};
1834 WCHAR root[] = {'A',':','\\',0};
1836 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1838 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1839 if ((ret = LISTBOX_InsertString( descr, -1, buffer )) < 0)
1847 /***********************************************************************
1848 * LISTBOX_HandleVScroll
1850 static LRESULT LISTBOX_HandleVScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1854 if (descr->style & LBS_MULTICOLUMN) return 0;
1858 LISTBOX_SetTopItem( descr, descr->top_item - 1, TRUE );
1861 LISTBOX_SetTopItem( descr, descr->top_item + 1, TRUE );
1864 LISTBOX_SetTopItem( descr, descr->top_item -
1865 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1868 LISTBOX_SetTopItem( descr, descr->top_item +
1869 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1871 case SB_THUMBPOSITION:
1872 LISTBOX_SetTopItem( descr, pos, TRUE );
1875 info.cbSize = sizeof(info);
1876 info.fMask = SIF_TRACKPOS;
1877 GetScrollInfo( descr->self, SB_VERT, &info );
1878 LISTBOX_SetTopItem( descr, info.nTrackPos, TRUE );
1881 LISTBOX_SetTopItem( descr, 0, TRUE );
1884 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1891 /***********************************************************************
1892 * LISTBOX_HandleHScroll
1894 static LRESULT LISTBOX_HandleHScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1899 if (descr->style & LBS_MULTICOLUMN)
1904 LISTBOX_SetTopItem( descr, descr->top_item-descr->page_size,
1908 LISTBOX_SetTopItem( descr, descr->top_item+descr->page_size,
1912 page = descr->width / descr->column_width;
1913 if (page < 1) page = 1;
1914 LISTBOX_SetTopItem( descr,
1915 descr->top_item - page * descr->page_size, TRUE );
1918 page = descr->width / descr->column_width;
1919 if (page < 1) page = 1;
1920 LISTBOX_SetTopItem( descr,
1921 descr->top_item + page * descr->page_size, TRUE );
1923 case SB_THUMBPOSITION:
1924 LISTBOX_SetTopItem( descr, pos*descr->page_size, TRUE );
1927 info.cbSize = sizeof(info);
1928 info.fMask = SIF_TRACKPOS;
1929 GetScrollInfo( descr->self, SB_VERT, &info );
1930 LISTBOX_SetTopItem( descr, info.nTrackPos*descr->page_size,
1934 LISTBOX_SetTopItem( descr, 0, TRUE );
1937 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1941 else if (descr->horz_extent)
1946 LISTBOX_SetHorizontalPos( descr, descr->horz_pos - 1 );
1949 LISTBOX_SetHorizontalPos( descr, descr->horz_pos + 1 );
1952 LISTBOX_SetHorizontalPos( descr,
1953 descr->horz_pos - descr->width );
1956 LISTBOX_SetHorizontalPos( descr,
1957 descr->horz_pos + descr->width );
1959 case SB_THUMBPOSITION:
1960 LISTBOX_SetHorizontalPos( descr, pos );
1963 info.cbSize = sizeof(info);
1964 info.fMask = SIF_TRACKPOS;
1965 GetScrollInfo( descr->self, SB_HORZ, &info );
1966 LISTBOX_SetHorizontalPos( descr, info.nTrackPos );
1969 LISTBOX_SetHorizontalPos( descr, 0 );
1972 LISTBOX_SetHorizontalPos( descr,
1973 descr->horz_extent - descr->width );
1980 static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta )
1982 short gcWheelDelta = 0;
1983 UINT pulScrollLines = 3;
1985 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1987 gcWheelDelta -= delta;
1989 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1991 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1992 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1993 LISTBOX_SetTopItem( descr, descr->top_item + cLineScroll, TRUE );
1998 /***********************************************************************
1999 * LISTBOX_HandleLButtonDown
2001 static LRESULT LISTBOX_HandleLButtonDown( LB_DESCR *descr, DWORD keys, INT x, INT y )
2003 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
2004 TRACE("[%p]: lbuttondown %d,%d item %d\n", descr->self, x, y, index );
2005 if (!descr->caret_on && (descr->in_focus)) return 0;
2007 if (!descr->in_focus)
2009 if( !descr->lphc ) SetFocus( descr->self );
2010 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
2013 if (index == -1) return 0;
2015 if (descr->style & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL))
2017 /* we should perhaps make sure that all items are deselected
2018 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2019 if (!(keys & (MK_SHIFT|MK_CONTROL)))
2020 LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2023 if (!(keys & MK_SHIFT)) descr->anchor_item = index;
2024 if (keys & MK_CONTROL)
2026 LISTBOX_SetCaretIndex( descr, index, FALSE );
2027 LISTBOX_SetSelection( descr, index,
2028 !descr->items[index].selected,
2029 (descr->style & LBS_NOTIFY) != 0);
2033 LISTBOX_MoveCaret( descr, index, FALSE );
2035 if (descr->style & LBS_EXTENDEDSEL)
2037 LISTBOX_SetSelection( descr, index,
2038 descr->items[index].selected,
2039 (descr->style & LBS_NOTIFY) != 0 );
2043 LISTBOX_SetSelection( descr, index,
2044 !descr->items[index].selected,
2045 (descr->style & LBS_NOTIFY) != 0 );
2051 descr->anchor_item = index;
2052 LISTBOX_MoveCaret( descr, index, FALSE );
2053 LISTBOX_SetSelection( descr, index,
2054 TRUE, (descr->style & LBS_NOTIFY) != 0 );
2057 descr->captured = TRUE;
2058 SetCapture( descr->self );
2062 if (descr->style & LBS_NOTIFY )
2063 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
2064 MAKELPARAM( x, y ) );
2065 if (GetWindowLongW( descr->self, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
2072 if (DragDetect( descr->self, pt ))
2073 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
2080 /*************************************************************************
2081 * LISTBOX_HandleLButtonDownCombo [Internal]
2083 * Process LButtonDown message for the ComboListBox
2086 * pWnd [I] The windows internal structure
2087 * pDescr [I] The ListBox internal structure
2088 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2089 * x [I] X Mouse Coordinate
2090 * y [I] Y Mouse Coordinate
2093 * 0 since we are processing the WM_LBUTTONDOWN Message
2096 * This function is only to be used when a ListBox is a ComboListBox
2099 static LRESULT LISTBOX_HandleLButtonDownCombo( LB_DESCR *descr, UINT msg, DWORD keys, INT x, INT y)
2101 RECT clientRect, screenRect;
2107 GetClientRect(descr->self, &clientRect);
2109 if(PtInRect(&clientRect, mousePos))
2111 /* MousePos is in client, resume normal processing */
2112 if (msg == WM_LBUTTONDOWN)
2114 descr->lphc->droppedIndex = descr->nb_items ? descr->selected_item : -1;
2115 return LISTBOX_HandleLButtonDown( descr, keys, x, y);
2117 else if (descr->style & LBS_NOTIFY)
2118 SEND_NOTIFICATION( descr, LBN_DBLCLK );
2122 POINT screenMousePos;
2123 HWND hWndOldCapture;
2125 /* Check the Non-Client Area */
2126 screenMousePos = mousePos;
2127 hWndOldCapture = GetCapture();
2129 GetWindowRect(descr->self, &screenRect);
2130 ClientToScreen(descr->self, &screenMousePos);
2132 if(!PtInRect(&screenRect, screenMousePos))
2134 LISTBOX_SetCaretIndex( descr, descr->lphc->droppedIndex, FALSE );
2135 LISTBOX_SetSelection( descr, descr->lphc->droppedIndex, FALSE, FALSE );
2136 COMBO_FlipListbox( descr->lphc, FALSE, FALSE );
2140 /* Check to see the NC is a scrollbar */
2142 LONG style = GetWindowLongW( descr->self, GWL_STYLE );
2143 /* Check Vertical scroll bar */
2144 if (style & WS_VSCROLL)
2146 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2147 if (PtInRect( &clientRect, mousePos ))
2148 nHitTestType = HTVSCROLL;
2150 /* Check horizontal scroll bar */
2151 if (style & WS_HSCROLL)
2153 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2154 if (PtInRect( &clientRect, mousePos ))
2155 nHitTestType = HTHSCROLL;
2157 /* Windows sends this message when a scrollbar is clicked
2160 if(nHitTestType != 0)
2162 SendMessageW(descr->self, WM_NCLBUTTONDOWN, nHitTestType,
2163 MAKELONG(screenMousePos.x, screenMousePos.y));
2165 /* Resume the Capture after scrolling is complete
2167 if(hWndOldCapture != 0)
2168 SetCapture(hWndOldCapture);
2174 /***********************************************************************
2175 * LISTBOX_HandleLButtonUp
2177 static LRESULT LISTBOX_HandleLButtonUp( LB_DESCR *descr )
2179 if (LISTBOX_Timer != LB_TIMER_NONE)
2180 KillSystemTimer( descr->self, LB_TIMER_ID );
2181 LISTBOX_Timer = LB_TIMER_NONE;
2182 if (descr->captured)
2184 descr->captured = FALSE;
2185 if (GetCapture() == descr->self) ReleaseCapture();
2186 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2187 SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2193 /***********************************************************************
2194 * LISTBOX_HandleTimer
2196 * Handle scrolling upon a timer event.
2197 * Return TRUE if scrolling should continue.
2199 static LRESULT LISTBOX_HandleTimer( LB_DESCR *descr, INT index, TIMER_DIRECTION dir )
2204 if (descr->top_item) index = descr->top_item - 1;
2208 if (descr->top_item) index -= descr->page_size;
2211 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2212 if (index == descr->focus_item) index++;
2213 if (index >= descr->nb_items) index = descr->nb_items - 1;
2215 case LB_TIMER_RIGHT:
2216 if (index + descr->page_size < descr->nb_items)
2217 index += descr->page_size;
2222 if (index == descr->focus_item) return FALSE;
2223 LISTBOX_MoveCaret( descr, index, FALSE );
2228 /***********************************************************************
2229 * LISTBOX_HandleSystemTimer
2231 * WM_SYSTIMER handler.
2233 static LRESULT LISTBOX_HandleSystemTimer( LB_DESCR *descr )
2235 if (!LISTBOX_HandleTimer( descr, descr->focus_item, LISTBOX_Timer ))
2237 KillSystemTimer( descr->self, LB_TIMER_ID );
2238 LISTBOX_Timer = LB_TIMER_NONE;
2244 /***********************************************************************
2245 * LISTBOX_HandleMouseMove
2247 * WM_MOUSEMOVE handler.
2249 static void LISTBOX_HandleMouseMove( LB_DESCR *descr,
2253 TIMER_DIRECTION dir = LB_TIMER_NONE;
2255 if (!descr->captured) return;
2257 if (descr->style & LBS_MULTICOLUMN)
2260 else if (y >= descr->item_height * descr->page_size)
2261 y = descr->item_height * descr->page_size - 1;
2265 dir = LB_TIMER_LEFT;
2268 else if (x >= descr->width)
2270 dir = LB_TIMER_RIGHT;
2271 x = descr->width - 1;
2276 if (y < 0) dir = LB_TIMER_UP; /* above */
2277 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2280 index = LISTBOX_GetItemFromPoint( descr, x, y );
2281 if (index == -1) index = descr->focus_item;
2282 if (!LISTBOX_HandleTimer( descr, index, dir )) dir = LB_TIMER_NONE;
2284 /* Start/stop the system timer */
2286 if (dir != LB_TIMER_NONE)
2287 SetSystemTimer( descr->self, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2288 else if (LISTBOX_Timer != LB_TIMER_NONE)
2289 KillSystemTimer( descr->self, LB_TIMER_ID );
2290 LISTBOX_Timer = dir;
2294 /***********************************************************************
2295 * LISTBOX_HandleKeyDown
2297 static LRESULT LISTBOX_HandleKeyDown( LB_DESCR *descr, DWORD key )
2300 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2301 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2302 bForceSelection = FALSE; /* only for single select list */
2304 if (descr->style & LBS_WANTKEYBOARDINPUT)
2306 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2307 MAKEWPARAM(LOWORD(key), descr->focus_item),
2308 (LPARAM)descr->self );
2309 if (caret == -2) return 0;
2311 if (caret == -1) switch(key)
2314 if (descr->style & LBS_MULTICOLUMN)
2316 bForceSelection = FALSE;
2317 if (descr->focus_item >= descr->page_size)
2318 caret = descr->focus_item - descr->page_size;
2323 caret = descr->focus_item - 1;
2324 if (caret < 0) caret = 0;
2327 if (descr->style & LBS_MULTICOLUMN)
2329 bForceSelection = FALSE;
2330 if (descr->focus_item + descr->page_size < descr->nb_items)
2331 caret = descr->focus_item + descr->page_size;
2336 caret = descr->focus_item + 1;
2337 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2341 if (descr->style & LBS_MULTICOLUMN)
2343 INT page = descr->width / descr->column_width;
2344 if (page < 1) page = 1;
2345 caret = descr->focus_item - (page * descr->page_size) + 1;
2347 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2348 if (caret < 0) caret = 0;
2351 if (descr->style & LBS_MULTICOLUMN)
2353 INT page = descr->width / descr->column_width;
2354 if (page < 1) page = 1;
2355 caret = descr->focus_item + (page * descr->page_size) - 1;
2357 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2358 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2364 caret = descr->nb_items - 1;
2367 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2368 else if (descr->style & LBS_MULTIPLESEL)
2370 LISTBOX_SetSelection( descr, descr->focus_item,
2371 !descr->items[descr->focus_item].selected,
2372 (descr->style & LBS_NOTIFY) != 0 );
2376 bForceSelection = FALSE;
2378 if (bForceSelection) /* focused item is used instead of key */
2379 caret = descr->focus_item;
2382 if (((descr->style & LBS_EXTENDEDSEL) &&
2383 !(GetKeyState( VK_SHIFT ) & 0x8000)) ||
2384 !IS_MULTISELECT(descr))
2385 descr->anchor_item = caret;
2386 LISTBOX_MoveCaret( descr, caret, TRUE );
2388 if (descr->style & LBS_MULTIPLESEL)
2389 descr->selected_item = caret;
2391 LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2392 if (descr->style & LBS_NOTIFY)
2396 /* make sure that combo parent doesn't hide us */
2397 descr->lphc->wState |= CBF_NOROLLUP;
2399 if (descr->nb_items) SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2406 /***********************************************************************
2407 * LISTBOX_HandleChar
2409 static LRESULT LISTBOX_HandleChar( LB_DESCR *descr, WCHAR charW )
2417 if (descr->style & LBS_WANTKEYBOARDINPUT)
2419 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2420 MAKEWPARAM(charW, descr->focus_item),
2421 (LPARAM)descr->self );
2422 if (caret == -2) return 0;
2425 caret = LISTBOX_FindString( descr, descr->focus_item, str, FALSE);
2428 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2429 LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2430 LISTBOX_MoveCaret( descr, caret, TRUE );
2431 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2432 SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2438 /***********************************************************************
2441 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2444 MEASUREITEMSTRUCT mis;
2447 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2450 GetClientRect( hwnd, &rect );
2452 descr->owner = GetParent( descr->self );
2453 descr->style = GetWindowLongW( descr->self, GWL_STYLE );
2454 descr->width = rect.right - rect.left;
2455 descr->height = rect.bottom - rect.top;
2456 descr->items = NULL;
2457 descr->nb_items = 0;
2458 descr->top_item = 0;
2459 descr->selected_item = -1;
2460 descr->focus_item = 0;
2461 descr->anchor_item = -1;
2462 descr->item_height = 1;
2463 descr->page_size = 1;
2464 descr->column_width = 150;
2465 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0;
2466 descr->horz_pos = 0;
2469 descr->caret_on = lphc ? FALSE : TRUE;
2470 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2471 descr->in_focus = FALSE;
2472 descr->captured = FALSE;
2474 descr->locale = GetUserDefaultLCID();
2477 if (is_old_app(descr) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2479 /* Win95 document "List Box Differences" from MSDN:
2480 If a list box in a version 3.x application has either the
2481 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2482 horizontal and vertical scroll bars.
2484 descr->style |= WS_VSCROLL | WS_HSCROLL;
2489 TRACE("[%p]: resetting owner %p -> %p\n", descr->self, descr->owner, lphc->self );
2490 descr->owner = lphc->self;
2493 SetWindowLongW( descr->self, 0, (LONG)descr );
2495 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2497 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2498 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2499 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2500 descr->item_height = LISTBOX_SetFont( descr, 0 );
2502 if (descr->style & LBS_OWNERDRAWFIXED)
2504 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2506 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2507 descr->item_height = lphc->fixedOwnerDrawHeight;
2511 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
2512 mis.CtlType = ODT_LISTBOX;
2517 mis.itemHeight = descr->item_height;
2518 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2519 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2523 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2528 /***********************************************************************
2531 static BOOL LISTBOX_Destroy( LB_DESCR *descr )
2533 LISTBOX_ResetContent( descr );
2534 SetWindowLongW( descr->self, 0, 0 );
2535 HeapFree( GetProcessHeap(), 0, descr );
2540 /***********************************************************************
2541 * ListBoxWndProc_common
2543 static LRESULT WINAPI ListBoxWndProc_common( HWND hwnd, UINT msg,
2544 WPARAM wParam, LPARAM lParam, BOOL unicode )
2546 LB_DESCR *descr = (LB_DESCR *)GetWindowLongW( hwnd, 0 );
2547 LPHEADCOMBO lphc = 0;
2552 if (!IsWindow(hwnd)) return 0;
2554 if (msg == WM_CREATE)
2556 CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam;
2557 if (lpcs->style & LBS_COMBOBOX) lphc = (LPHEADCOMBO)lpcs->lpCreateParams;
2558 if (!LISTBOX_Create( hwnd, lphc )) return -1;
2559 TRACE("creating wnd=%p descr=%lx\n", hwnd, GetWindowLongW( hwnd, 0 ) );
2562 /* Ignore all other messages before we get a WM_CREATE */
2563 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2564 DefWindowProcA( hwnd, msg, wParam, lParam );
2566 if (descr->style & LBS_COMBOBOX) lphc = descr->lphc;
2568 TRACE("[%p]: msg %s wp %08x lp %08lx\n",
2569 descr->self, SPY_GetMsgName(msg, descr->self), wParam, lParam );
2573 case LB_RESETCONTENT16:
2574 case LB_RESETCONTENT:
2575 LISTBOX_ResetContent( descr );
2576 LISTBOX_UpdateScroll( descr );
2577 InvalidateRect( descr->self, NULL, TRUE );
2580 case LB_ADDSTRING16:
2581 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2587 if(unicode || !HAS_STRINGS(descr))
2588 textW = (LPWSTR)lParam;
2591 LPSTR textA = (LPSTR)lParam;
2592 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2593 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2594 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2596 wParam = LISTBOX_FindStringPos( descr, textW, FALSE );
2597 ret = LISTBOX_InsertString( descr, wParam, textW );
2598 if (!unicode && HAS_STRINGS(descr))
2599 HeapFree(GetProcessHeap(), 0, textW);
2603 case LB_INSERTSTRING16:
2604 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2605 wParam = (INT)(INT16)wParam;
2607 case LB_INSERTSTRING:
2611 if(unicode || !HAS_STRINGS(descr))
2612 textW = (LPWSTR)lParam;
2615 LPSTR textA = (LPSTR)lParam;
2616 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2617 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2618 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2620 ret = LISTBOX_InsertString( descr, wParam, textW );
2621 if(!unicode && HAS_STRINGS(descr))
2622 HeapFree(GetProcessHeap(), 0, textW);
2627 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2633 if(unicode || !HAS_STRINGS(descr))
2634 textW = (LPWSTR)lParam;
2637 LPSTR textA = (LPSTR)lParam;
2638 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2639 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2640 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2642 wParam = LISTBOX_FindFileStrPos( descr, textW );
2643 ret = LISTBOX_InsertString( descr, wParam, textW );
2644 if(!unicode && HAS_STRINGS(descr))
2645 HeapFree(GetProcessHeap(), 0, textW);
2649 case LB_DELETESTRING16:
2650 case LB_DELETESTRING:
2651 if (LISTBOX_RemoveItem( descr, wParam) != LB_ERR)
2652 return descr->nb_items;
2655 SetLastError(ERROR_INVALID_INDEX);
2659 case LB_GETITEMDATA16:
2660 case LB_GETITEMDATA:
2661 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2663 SetLastError(ERROR_INVALID_INDEX);
2666 return descr->items[wParam].data;
2668 case LB_SETITEMDATA16:
2669 case LB_SETITEMDATA:
2670 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2672 SetLastError(ERROR_INVALID_INDEX);
2675 descr->items[wParam].data = (DWORD)lParam;
2680 return descr->nb_items;
2683 lParam = (LPARAM)MapSL(lParam);
2686 return LISTBOX_GetText( descr, wParam, (LPWSTR)lParam, unicode );
2688 case LB_GETTEXTLEN16:
2691 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2693 SetLastError(ERROR_INVALID_INDEX);
2696 if (!HAS_STRINGS(descr)) return sizeof(DWORD);
2697 if (unicode) return strlenW( descr->items[wParam].str );
2698 return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
2699 strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
2701 case LB_GETCURSEL16:
2703 if (descr->nb_items == 0)
2705 if (!IS_MULTISELECT(descr))
2706 return descr->selected_item;
2707 if (descr->selected_item != -1)
2708 return descr->selected_item;
2709 return descr->focus_item;
2710 /* otherwise, if the user tries to move the selection with the */
2711 /* arrow keys, we will give the application something to choke on */
2712 case LB_GETTOPINDEX16:
2713 case LB_GETTOPINDEX:
2714 return descr->top_item;
2716 case LB_GETITEMHEIGHT16:
2717 case LB_GETITEMHEIGHT:
2718 return LISTBOX_GetItemHeight( descr, wParam );
2720 case LB_SETITEMHEIGHT16:
2721 lParam = LOWORD(lParam);
2723 case LB_SETITEMHEIGHT:
2724 return LISTBOX_SetItemHeight( descr, wParam, lParam, TRUE );
2726 case LB_ITEMFROMPOINT:
2731 pt.x = LOWORD(lParam);
2732 pt.y = HIWORD(lParam);
2735 rect.right = descr->width;
2736 rect.bottom = descr->height;
2738 return MAKELONG( LISTBOX_GetItemFromPoint(descr, pt.x, pt.y),
2739 !PtInRect( &rect, pt ) );
2742 case LB_SETCARETINDEX16:
2743 case LB_SETCARETINDEX:
2744 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2745 if (LISTBOX_SetCaretIndex( descr, wParam, !lParam ) == LB_ERR)
2752 case LB_GETCARETINDEX16:
2753 case LB_GETCARETINDEX:
2754 return descr->focus_item;
2756 case LB_SETTOPINDEX16:
2757 case LB_SETTOPINDEX:
2758 return LISTBOX_SetTopItem( descr, wParam, TRUE );
2760 case LB_SETCOLUMNWIDTH16:
2761 case LB_SETCOLUMNWIDTH:
2762 return LISTBOX_SetColumnWidth( descr, wParam );
2764 case LB_GETITEMRECT16:
2767 RECT16 *r16 = MapSL(lParam);
2768 ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2769 r16->left = rect.left;
2770 r16->top = rect.top;
2771 r16->right = rect.right;
2772 r16->bottom = rect.bottom;
2776 case LB_GETITEMRECT:
2777 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2779 case LB_FINDSTRING16:
2780 wParam = (INT)(INT16)wParam;
2781 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2787 if(unicode || !HAS_STRINGS(descr))
2788 textW = (LPWSTR)lParam;
2791 LPSTR textA = (LPSTR)lParam;
2792 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2793 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2794 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2796 ret = LISTBOX_FindString( descr, wParam, textW, FALSE );
2797 if(!unicode && HAS_STRINGS(descr))
2798 HeapFree(GetProcessHeap(), 0, textW);
2802 case LB_FINDSTRINGEXACT16:
2803 wParam = (INT)(INT16)wParam;
2804 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2806 case LB_FINDSTRINGEXACT:
2810 if(unicode || !HAS_STRINGS(descr))
2811 textW = (LPWSTR)lParam;
2814 LPSTR textA = (LPSTR)lParam;
2815 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2816 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2817 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2819 ret = LISTBOX_FindString( descr, wParam, textW, TRUE );
2820 if(!unicode && HAS_STRINGS(descr))
2821 HeapFree(GetProcessHeap(), 0, textW);
2825 case LB_SELECTSTRING16:
2826 wParam = (INT)(INT16)wParam;
2827 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2829 case LB_SELECTSTRING:
2834 if(HAS_STRINGS(descr))
2835 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2836 debugstr_a((LPSTR)lParam));
2837 if(unicode || !HAS_STRINGS(descr))
2838 textW = (LPWSTR)lParam;
2841 LPSTR textA = (LPSTR)lParam;
2842 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2843 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2844 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2846 index = LISTBOX_FindString( descr, wParam, textW, FALSE );
2847 if(!unicode && HAS_STRINGS(descr))
2848 HeapFree(GetProcessHeap(), 0, textW);
2849 if (index != LB_ERR)
2851 LISTBOX_MoveCaret( descr, index, TRUE );
2852 LISTBOX_SetSelection( descr, index, TRUE, FALSE );
2858 wParam = (INT)(INT16)wParam;
2861 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2863 return descr->items[wParam].selected;
2866 lParam = (INT)(INT16)lParam;
2869 return LISTBOX_SetSelection( descr, lParam, wParam, FALSE );
2871 case LB_SETCURSEL16:
2872 wParam = (INT)(INT16)wParam;
2875 if (IS_MULTISELECT(descr)) return LB_ERR;
2876 LISTBOX_SetCaretIndex( descr, wParam, TRUE );
2877 ret = LISTBOX_SetSelection( descr, wParam, TRUE, FALSE );
2878 if (lphc && ret != LB_ERR) ret = descr->selected_item;
2881 case LB_GETSELCOUNT16:
2882 case LB_GETSELCOUNT:
2883 return LISTBOX_GetSelCount( descr );
2885 case LB_GETSELITEMS16:
2886 return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2888 case LB_GETSELITEMS:
2889 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2891 case LB_SELITEMRANGE16:
2892 case LB_SELITEMRANGE:
2893 if (LOWORD(lParam) <= HIWORD(lParam))
2894 return LISTBOX_SelectItemRange( descr, LOWORD(lParam),
2895 HIWORD(lParam), wParam );
2897 return LISTBOX_SelectItemRange( descr, HIWORD(lParam),
2898 LOWORD(lParam), wParam );
2900 case LB_SELITEMRANGEEX16:
2901 case LB_SELITEMRANGEEX:
2902 if ((INT)lParam >= (INT)wParam)
2903 return LISTBOX_SelectItemRange( descr, wParam, lParam, TRUE );
2905 return LISTBOX_SelectItemRange( descr, lParam, wParam, FALSE);
2907 case LB_GETHORIZONTALEXTENT16:
2908 case LB_GETHORIZONTALEXTENT:
2909 return descr->horz_extent;
2911 case LB_SETHORIZONTALEXTENT16:
2912 case LB_SETHORIZONTALEXTENT:
2913 return LISTBOX_SetHorizontalExtent( descr, wParam );
2915 case LB_GETANCHORINDEX16:
2916 case LB_GETANCHORINDEX:
2917 return descr->anchor_item;
2919 case LB_SETANCHORINDEX16:
2920 wParam = (INT)(INT16)wParam;
2922 case LB_SETANCHORINDEX:
2923 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2925 SetLastError(ERROR_INVALID_INDEX);
2928 descr->anchor_item = (INT)wParam;
2932 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2933 * be set automatically (this is different in Win32) */
2934 if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2935 lParam = (LPARAM)MapSL(lParam);
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 ret = LISTBOX_Directory( descr, wParam, textW, msg == LB_DIR );
2952 HeapFree(GetProcessHeap(), 0, textW);
2957 return descr->locale;
2962 if (!IsValidLocale((LCID)wParam, LCID_INSTALLED))
2964 ret = descr->locale;
2965 descr->locale = (LCID)wParam;
2969 case LB_INITSTORAGE:
2970 return LISTBOX_InitStorage( descr, wParam );
2973 return LISTBOX_SetCount( descr, (INT)wParam );
2975 case LB_SETTABSTOPS16:
2976 return LISTBOX_SetTabStops( descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2978 case LB_SETTABSTOPS:
2979 return LISTBOX_SetTabStops( descr, wParam, (LPINT)lParam, FALSE );
2983 if (descr->caret_on)
2985 descr->caret_on = TRUE;
2986 if ((descr->focus_item != -1) && (descr->in_focus))
2987 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2992 if (!descr->caret_on)
2994 descr->caret_on = FALSE;
2995 if ((descr->focus_item != -1) && (descr->in_focus))
2996 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3000 return LISTBOX_Destroy( descr );
3003 InvalidateRect( descr->self, NULL, TRUE );
3007 LISTBOX_SetRedraw( descr, wParam != 0 );
3011 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3016 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( descr->self, &ps );
3017 ret = LISTBOX_Paint( descr, hdc );
3018 if( !wParam ) EndPaint( descr->self, &ps );
3022 LISTBOX_UpdateSize( descr );
3025 return (LRESULT)descr->font;
3027 LISTBOX_SetFont( descr, (HFONT)wParam );
3028 if (lParam) InvalidateRect( descr->self, 0, TRUE );
3031 descr->in_focus = TRUE;
3032 descr->caret_on = TRUE;
3033 if (descr->focus_item != -1)
3034 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3035 SEND_NOTIFICATION( descr, LBN_SETFOCUS );
3038 descr->in_focus = FALSE;
3039 if ((descr->focus_item != -1) && descr->caret_on)
3040 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3041 SEND_NOTIFICATION( descr, LBN_KILLFOCUS );
3044 return LISTBOX_HandleHScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3046 return LISTBOX_HandleVScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3048 if (wParam & (MK_SHIFT | MK_CONTROL))
3049 return DefWindowProcW( descr->self, msg, wParam, lParam );
3050 return LISTBOX_HandleMouseWheel( descr, (SHORT)HIWORD(wParam) );
3051 case WM_LBUTTONDOWN:
3053 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3054 (INT16)LOWORD(lParam),
3055 (INT16)HIWORD(lParam) );
3056 return LISTBOX_HandleLButtonDown( descr, wParam,
3057 (INT16)LOWORD(lParam),
3058 (INT16)HIWORD(lParam) );
3059 case WM_LBUTTONDBLCLK:
3061 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3062 (INT16)LOWORD(lParam),
3063 (INT16)HIWORD(lParam) );
3064 if (descr->style & LBS_NOTIFY)
3065 SEND_NOTIFICATION( descr, LBN_DBLCLK );
3068 if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) )
3070 BOOL captured = descr->captured;
3074 mousePos.x = (INT16)LOWORD(lParam);
3075 mousePos.y = (INT16)HIWORD(lParam);
3078 * If we are in a dropdown combobox, we simulate that
3079 * the mouse is captured to show the tracking of the item.
3081 if (GetClientRect(descr->self, &clientRect) && PtInRect( &clientRect, mousePos ))
3082 descr->captured = TRUE;
3084 LISTBOX_HandleMouseMove( descr, mousePos.x, mousePos.y);
3086 descr->captured = captured;
3088 else if (GetCapture() == descr->self)
3090 LISTBOX_HandleMouseMove( descr, (INT16)LOWORD(lParam),
3091 (INT16)HIWORD(lParam) );
3101 * If the mouse button "up" is not in the listbox,
3102 * we make sure there is no selection by re-selecting the
3103 * item that was selected when the listbox was made visible.
3105 mousePos.x = (INT16)LOWORD(lParam);
3106 mousePos.y = (INT16)HIWORD(lParam);
3108 GetClientRect(descr->self, &clientRect);
3111 * When the user clicks outside the combobox and the focus
3112 * is lost, the owning combobox will send a fake buttonup with
3113 * 0xFFFFFFF as the mouse location, we must also revert the
3114 * selection to the original selection.
3116 if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) )
3117 LISTBOX_MoveCaret( descr, lphc->droppedIndex, FALSE );
3119 return LISTBOX_HandleLButtonUp( descr );
3121 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3123 /* for some reason Windows makes it possible to
3124 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3126 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3127 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3128 && (wParam == VK_DOWN || wParam == VK_UP)) )
3130 COMBO_FlipListbox( lphc, FALSE, FALSE );
3134 return LISTBOX_HandleKeyDown( descr, wParam );
3139 charW = (WCHAR)wParam;
3142 CHAR charA = (CHAR)wParam;
3143 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
3145 return LISTBOX_HandleChar( descr, charW );
3148 return LISTBOX_HandleSystemTimer( descr );
3150 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
3153 HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
3154 wParam, (LPARAM)descr->self );
3155 TRACE("hbrush = %p\n", hbrush);
3157 hbrush = GetSysColorBrush(COLOR_WINDOW);
3160 GetClientRect(descr->self, &rect);
3161 FillRect((HDC)wParam, &rect, hbrush);
3166 if( lphc ) return 0;
3167 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3168 SendMessageA( descr->owner, msg, wParam, lParam );
3171 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3180 if ((msg >= WM_USER) && (msg < 0xc000))
3181 WARN("[%p]: unknown msg %04x wp %08x lp %08lx\n",
3182 hwnd, msg, wParam, lParam );
3185 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3186 DefWindowProcA( hwnd, msg, wParam, lParam );
3189 /***********************************************************************
3192 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3194 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3197 /***********************************************************************
3200 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3202 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE );