4 * Copyright 1996 Alexandre Julliard
12 #include "wine/winuser16.h"
13 #include "wine/winbase16.h"
14 #include "wine/unicode.h"
21 #include "debugtools.h"
23 DEFAULT_DEBUG_CHANNEL(listbox);
24 DECLARE_DEBUG_CHANNEL(combo);
30 * Probably needs improvement:
34 /* Items array granularity */
35 #define LB_ARRAY_GRANULARITY 16
37 /* Scrolling timeout in ms */
38 #define LB_SCROLL_TIMEOUT 50
40 /* Listbox system timer id */
43 /* flag listbox changed while setredraw false - internal style */
44 #define LBS_DISPLAYCHANGED 0x80000000
49 LPWSTR str; /* Item text */
50 BOOL selected; /* Is item selected? */
51 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
52 DWORD data; /* User data */
55 /* Listbox structure */
58 HWND owner; /* Owner window to send notifications to */
59 UINT style; /* Window style */
60 INT width; /* Window width */
61 INT height; /* Window height */
62 LB_ITEMDATA *items; /* Array of items */
63 INT nb_items; /* Number of items */
64 INT top_item; /* Top visible item */
65 INT selected_item; /* Selected item */
66 INT focus_item; /* Item that has the focus */
67 INT anchor_item; /* Anchor item for extended selection */
68 INT item_height; /* Default item height */
69 INT page_size; /* Items per listbox page */
70 INT column_width; /* Column width for multi-column listboxes */
71 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
72 INT horz_pos; /* Horizontal position */
73 INT nb_tabs; /* Number of tabs in array */
74 INT *tabs; /* Array of tabs */
75 BOOL caret_on; /* Is caret on? */
76 BOOL captured; /* Is mouse captured? */
78 HFONT font; /* Current font */
79 LCID locale; /* Current locale for string comparisons */
80 LPHEADCOMBO lphc; /* ComboLBox */
84 #define IS_OWNERDRAW(descr) \
85 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
87 #define HAS_STRINGS(descr) \
88 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
91 #define IS_MULTISELECT(descr) \
92 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
94 #define SEND_NOTIFICATION(wnd,descr,code) \
95 (SendMessageW( (descr)->owner, WM_COMMAND, \
96 MAKEWPARAM((wnd)->wIDmenu, (code)), (wnd)->hwndSelf ))
98 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
100 /* Current timer status */
110 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
112 static LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
113 static LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
114 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
115 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
117 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect );
119 /*********************************************************************
120 * listbox class descriptor
122 const struct builtin_class_descr LISTBOX_builtin_class =
124 "ListBox", /* name */
125 CS_GLOBALCLASS | CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
126 ListBoxWndProcA, /* procA */
127 ListBoxWndProcW, /* procW */
128 sizeof(LB_DESCR *), /* extra */
129 IDC_ARROWA, /* cursor */
134 /*********************************************************************
135 * combolbox class descriptor
137 const struct builtin_class_descr COMBOLBOX_builtin_class =
139 "ComboLBox", /* name */
140 CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS, /* style */
141 ComboLBWndProcA, /* procA */
142 ComboLBWndProcW, /* procW */
143 sizeof(LB_DESCR *), /* extra */
144 IDC_ARROWA, /* cursor */
149 /***********************************************************************
152 void LISTBOX_Dump( WND *wnd )
156 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
158 TRACE( "Listbox:\n" );
159 TRACE( "hwnd=%04x descr=%08x items=%d top=%d\n",
160 wnd->hwndSelf, (UINT)descr, descr->nb_items,
162 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
164 TRACE( "%4d: %-40s %d %08lx %3d\n",
165 i, debugstr_w(item->str), item->selected, item->data, item->height );
170 /***********************************************************************
171 * LISTBOX_GetCurrentPageSize
173 * Return the current page size
175 static INT LISTBOX_GetCurrentPageSize( LB_DESCR *descr )
178 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
179 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
181 if ((height += descr->items[i].height) > descr->height) break;
183 if (i == descr->top_item) return 1;
184 else return i - descr->top_item;
188 /***********************************************************************
189 * LISTBOX_GetMaxTopIndex
191 * Return the maximum possible index for the top of the listbox.
193 static INT LISTBOX_GetMaxTopIndex( LB_DESCR *descr )
197 if (descr->style & LBS_OWNERDRAWVARIABLE)
199 page = descr->height;
200 for (max = descr->nb_items - 1; max >= 0; max--)
201 if ((page -= descr->items[max].height) < 0) break;
202 if (max < descr->nb_items - 1) max++;
204 else if (descr->style & LBS_MULTICOLUMN)
206 if ((page = descr->width / descr->column_width) < 1) page = 1;
207 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
208 max = (max - page) * descr->page_size;
212 max = descr->nb_items - descr->page_size;
214 if (max < 0) max = 0;
219 /***********************************************************************
220 * LISTBOX_UpdateScroll
222 * Update the scrollbars. Should be called whenever the content
223 * of the listbox changes.
225 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
229 /* Check the listbox scroll bar flags individually before we call
230 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
231 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
232 scroll bar when we do not need one.
233 if (!(descr->style & WS_VSCROLL)) return;
236 /* It is important that we check descr->style, and not wnd->dwStyle,
237 for WS_VSCROLL, as the former is exactly the one passed in
238 argument to CreateWindow.
239 In Windows (and from now on in Wine :) a listbox created
240 with such a style (no WS_SCROLL) does not update
241 the scrollbar with listbox-related data, thus letting
242 the programmer use it for his/her own purposes. */
244 if (descr->style & LBS_NOREDRAW) return;
245 info.cbSize = sizeof(info);
247 if (descr->style & LBS_MULTICOLUMN)
250 info.nMax = (descr->nb_items - 1) / descr->page_size;
251 info.nPos = descr->top_item / descr->page_size;
252 info.nPage = descr->width / descr->column_width;
253 if (info.nPage < 1) info.nPage = 1;
254 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
255 if (descr->style & LBS_DISABLENOSCROLL)
256 info.fMask |= SIF_DISABLENOSCROLL;
257 if (descr->style & WS_HSCROLL)
258 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
260 info.fMask = SIF_RANGE;
261 if (descr->style & WS_VSCROLL)
262 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
267 info.nMax = descr->nb_items - 1;
268 info.nPos = descr->top_item;
269 info.nPage = LISTBOX_GetCurrentPageSize( descr );
270 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
271 if (descr->style & LBS_DISABLENOSCROLL)
272 info.fMask |= SIF_DISABLENOSCROLL;
273 if (descr->style & WS_VSCROLL)
274 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
276 if (descr->horz_extent)
279 info.nMax = descr->horz_extent - 1;
280 info.nPos = descr->horz_pos;
281 info.nPage = descr->width;
282 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
283 if (descr->style & LBS_DISABLENOSCROLL)
284 info.fMask |= SIF_DISABLENOSCROLL;
285 if (descr->style & WS_HSCROLL)
286 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
292 /***********************************************************************
295 * Set the top item of the listbox, scrolling up or down if necessary.
297 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT index,
300 INT max = LISTBOX_GetMaxTopIndex( descr );
301 if (index > max) index = max;
302 if (index < 0) index = 0;
303 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
304 if (descr->top_item == index) return LB_OKAY;
305 if (descr->style & LBS_MULTICOLUMN)
307 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
308 if (scroll && (abs(diff) < descr->width))
309 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
310 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
318 if (descr->style & LBS_OWNERDRAWVARIABLE)
322 if (index > descr->top_item)
324 for (i = index - 1; i >= descr->top_item; i--)
325 diff -= descr->items[i].height;
329 for (i = index; i < descr->top_item; i++)
330 diff += descr->items[i].height;
334 diff = (descr->top_item - index) * descr->item_height;
336 if (abs(diff) < descr->height)
337 ScrollWindowEx( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
338 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
342 if (!scroll) InvalidateRect( wnd->hwndSelf, NULL, TRUE );
343 descr->top_item = index;
344 LISTBOX_UpdateScroll( wnd, descr );
349 /***********************************************************************
352 * Update the page size. Should be called when the size of
353 * the client area or the item height changes.
355 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
359 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
361 if (page_size == descr->page_size) return;
362 descr->page_size = page_size;
363 if (descr->style & LBS_MULTICOLUMN)
364 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
365 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
369 /***********************************************************************
372 * Update the size of the listbox. Should be called when the size of
373 * the client area changes.
375 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
379 GetClientRect( wnd->hwndSelf, &rect );
380 descr->width = rect.right - rect.left;
381 descr->height = rect.bottom - rect.top;
382 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
386 if(descr->item_height != 0)
387 remaining = descr->height % descr->item_height;
390 if ((descr->height > descr->item_height) && remaining)
392 if (!(wnd->flags & WIN_ISWIN32))
393 { /* give a margin for error to 16 bits programs - if we need
394 less than the height of the nonclient area, round to the
395 *next* number of items */
396 int ncheight = wnd->rectWindow.bottom - wnd->rectWindow.top - descr->height;
397 if ((descr->item_height - remaining) <= ncheight)
398 remaining = remaining - descr->item_height;
400 TRACE("[%04x]: changing height %d -> %d\n",
401 wnd->hwndSelf, descr->height,
402 descr->height - remaining );
403 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
404 wnd->rectWindow.right - wnd->rectWindow.left,
405 wnd->rectWindow.bottom - wnd->rectWindow.top - remaining,
406 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
410 TRACE("[%04x]: new size = %d,%d\n",
411 wnd->hwndSelf, descr->width, descr->height );
412 LISTBOX_UpdatePage( wnd, descr );
413 LISTBOX_UpdateScroll( wnd, descr );
415 /* Invalidate the focused item so it will be repainted correctly */
416 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
418 InvalidateRect( wnd->hwndSelf, &rect, FALSE );
423 /***********************************************************************
424 * LISTBOX_GetItemRect
426 * Get the rectangle enclosing an item, in listbox client coordinates.
427 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
429 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect )
431 /* Index <= 0 is legal even on empty listboxes */
432 if (index && (index >= descr->nb_items)) return -1;
433 SetRect( rect, 0, 0, descr->width, descr->height );
434 if (descr->style & LBS_MULTICOLUMN)
436 INT col = (index / descr->page_size) -
437 (descr->top_item / descr->page_size);
438 rect->left += col * descr->column_width;
439 rect->right = rect->left + descr->column_width;
440 rect->top += (index % descr->page_size) * descr->item_height;
441 rect->bottom = rect->top + descr->item_height;
443 else if (descr->style & LBS_OWNERDRAWVARIABLE)
446 rect->right += descr->horz_pos;
447 if ((index >= 0) && (index < descr->nb_items))
449 if (index < descr->top_item)
451 for (i = descr->top_item-1; i >= index; i--)
452 rect->top -= descr->items[i].height;
456 for (i = descr->top_item; i < index; i++)
457 rect->top += descr->items[i].height;
459 rect->bottom = rect->top + descr->items[index].height;
465 rect->top += (index - descr->top_item) * descr->item_height;
466 rect->bottom = rect->top + descr->item_height;
467 rect->right += descr->horz_pos;
470 return ((rect->left < descr->width) && (rect->right > 0) &&
471 (rect->top < descr->height) && (rect->bottom > 0));
475 /***********************************************************************
476 * LISTBOX_GetItemFromPoint
478 * Return the item nearest from point (x,y) (in client coordinates).
480 static INT LISTBOX_GetItemFromPoint( LB_DESCR *descr, INT x, INT y )
482 INT index = descr->top_item;
484 if (!descr->nb_items) return -1; /* No items */
485 if (descr->style & LBS_OWNERDRAWVARIABLE)
490 while (index < descr->nb_items)
492 if ((pos += descr->items[index].height) > y) break;
501 if ((pos -= descr->items[index].height) <= y) break;
505 else if (descr->style & LBS_MULTICOLUMN)
507 if (y >= descr->item_height * descr->page_size) return -1;
508 if (y >= 0) index += y / descr->item_height;
509 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
510 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
514 index += (y / descr->item_height);
516 if (index < 0) return 0;
517 if (index >= descr->nb_items) return -1;
522 /***********************************************************************
527 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
528 const RECT *rect, INT index, UINT action, BOOL ignoreFocus )
530 LB_ITEMDATA *item = NULL;
531 if (index < descr->nb_items) item = &descr->items[index];
533 if (IS_OWNERDRAW(descr))
541 if (action == ODA_FOCUS)
542 DrawFocusRect( hdc, rect );
544 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
548 /* some programs mess with the clipping region when
549 drawing the item, *and* restore the previous region
550 after they are done, so a region has better to exist
551 else everything ends clipped */
552 GetClientRect(wnd->hwndSelf, &r);
553 hrgn = CreateRectRgnIndirect(&r);
554 SelectClipRgn( hdc, hrgn);
555 DeleteObject( hrgn );
557 dis.CtlType = ODT_LISTBOX;
558 dis.CtlID = wnd->wIDmenu;
559 dis.hwndItem = wnd->hwndSelf;
560 dis.itemAction = action;
564 if (item && item->selected) dis.itemState |= ODS_SELECTED;
565 if (!ignoreFocus && (descr->focus_item == index) &&
567 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
568 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
569 dis.itemData = item ? item->data : 0;
571 TRACE("[%04x]: drawitem %d (%s) action=%02x "
572 "state=%02x rect=%d,%d-%d,%d\n",
573 wnd->hwndSelf, index, item ? debugstr_w(item->str) : "", action,
574 dis.itemState, rect->left, rect->top,
575 rect->right, rect->bottom );
576 SendMessageW(descr->owner, WM_DRAWITEM, wnd->wIDmenu, (LPARAM)&dis);
580 COLORREF oldText = 0, oldBk = 0;
582 if (action == ODA_FOCUS)
584 DrawFocusRect( hdc, rect );
587 if (item && item->selected)
589 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
590 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
593 TRACE("[%04x]: painting %d (%s) action=%02x "
594 "rect=%d,%d-%d,%d\n",
595 wnd->hwndSelf, index, item ? debugstr_w(item->str) : "", action,
596 rect->left, rect->top, rect->right, rect->bottom );
598 ExtTextOutW( hdc, rect->left + 1, rect->top,
599 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
600 else if (!(descr->style & LBS_USETABSTOPS))
601 ExtTextOutW( hdc, rect->left + 1, rect->top,
602 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
603 strlenW(item->str), NULL );
606 /* Output empty string to paint background in the full width. */
607 ExtTextOutW( hdc, rect->left + 1, rect->top,
608 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
609 TabbedTextOutW( hdc, rect->left + 1 , rect->top,
610 item->str, strlenW(item->str),
611 descr->nb_tabs, descr->tabs, 0);
613 if (item && item->selected)
615 SetBkColor( hdc, oldBk );
616 SetTextColor( hdc, oldText );
618 if (!ignoreFocus && (descr->focus_item == index) &&
620 (descr->in_focus)) DrawFocusRect( hdc, rect );
625 /***********************************************************************
628 * Change the redraw flag.
630 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
634 if (!(descr->style & LBS_NOREDRAW)) return;
635 descr->style &= ~LBS_NOREDRAW;
636 if (descr->style & LBS_DISPLAYCHANGED)
637 { /* page was changed while setredraw false, refresh automatically */
638 InvalidateRect(wnd->hwndSelf, NULL, TRUE);
639 if ((descr->top_item + descr->page_size) > descr->nb_items)
640 { /* reset top of page if less than number of items/page */
641 descr->top_item = descr->nb_items - descr->page_size;
642 if (descr->top_item < 0) descr->top_item = 0;
644 descr->style &= ~LBS_DISPLAYCHANGED;
646 LISTBOX_UpdateScroll( wnd, descr );
648 else descr->style |= LBS_NOREDRAW;
652 /***********************************************************************
653 * LISTBOX_RepaintItem
655 * Repaint a single item synchronously.
657 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
663 HBRUSH hbrush, oldBrush = 0;
665 /* Do not repaint the item if the item is not visible */
666 if (!IsWindowVisible(wnd->hwndSelf)) return;
667 if (descr->style & LBS_NOREDRAW)
669 descr->style |= LBS_DISPLAYCHANGED;
672 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
673 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
674 if (descr->font) oldFont = SelectObject( hdc, descr->font );
675 hbrush = SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
676 hdc, (LPARAM)wnd->hwndSelf );
677 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
678 if (wnd->dwStyle & WS_DISABLED)
679 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
680 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
681 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action, FALSE );
682 if (oldFont) SelectObject( hdc, oldFont );
683 if (oldBrush) SelectObject( hdc, oldBrush );
684 ReleaseDC( wnd->hwndSelf, hdc );
688 /***********************************************************************
689 * LISTBOX_InitStorage
691 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items )
695 nb_items += LB_ARRAY_GRANULARITY - 1;
696 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
698 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
699 if (!(item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
700 nb_items * sizeof(LB_ITEMDATA) )))
702 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
710 /***********************************************************************
711 * LISTBOX_SetTabStops
713 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
714 LPINT tabs, BOOL short_ints )
716 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
717 if (descr->tabs) HeapFree( GetProcessHeap(), 0, descr->tabs );
718 if (!(descr->nb_tabs = count))
723 /* FIXME: count = 1 */
724 if (!(descr->tabs = (INT *)HeapAlloc( GetProcessHeap(), 0,
725 descr->nb_tabs * sizeof(INT) )))
730 LPINT16 p = (LPINT16)tabs;
732 TRACE("[%04x]: settabstops ", wnd->hwndSelf );
733 for (i = 0; i < descr->nb_tabs; i++) {
734 descr->tabs[i] = *p++<<1; /* FIXME */
735 if (TRACE_ON(listbox)) DPRINTF("%hd ", descr->tabs[i]);
737 if (TRACE_ON(listbox)) DPRINTF("\n");
739 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
740 /* FIXME: repaint the window? */
745 /***********************************************************************
748 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPARAM lParam, BOOL unicode )
750 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
751 if (HAS_STRINGS(descr))
754 return strlenW(descr->items[index].str);
756 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
760 LPWSTR buffer = (LPWSTR)lParam;
761 strcpyW( buffer, descr->items[index].str );
762 return strlenW(buffer);
766 LPSTR buffer = (LPSTR)lParam;
767 return WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, buffer, 0x7FFFFFFF, NULL, NULL) - 1;
771 *((LPDWORD)lParam)=*(LPDWORD)(&descr->items[index].data);
772 return sizeof(DWORD);
777 /***********************************************************************
778 * LISTBOX_FindStringPos
780 * Find the nearest string located before a given string in sort order.
781 * If 'exact' is TRUE, return an error if we don't get an exact match.
783 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCWSTR str,
786 INT index, min, max, res = -1;
788 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
790 max = descr->nb_items;
793 index = (min + max) / 2;
794 if (HAS_STRINGS(descr))
795 res = lstrcmpiW( descr->items[index].str, str );
798 COMPAREITEMSTRUCT cis;
800 cis.CtlType = ODT_LISTBOX;
801 cis.CtlID = wnd->wIDmenu;
802 cis.hwndItem = wnd->hwndSelf;
804 cis.itemData1 = descr->items[index].data;
806 cis.itemData2 = (DWORD)str;
807 cis.dwLocaleId = descr->locale;
808 res = SendMessageW( descr->owner, WM_COMPAREITEM,
809 wnd->wIDmenu, (LPARAM)&cis );
811 if (!res) return index;
812 if (res > 0) max = index;
813 else min = index + 1;
815 return exact ? -1 : max;
819 /***********************************************************************
820 * LISTBOX_FindFileStrPos
822 * Find the nearest string located before a given string in directory
823 * sort order (i.e. first files, then directories, then drives).
825 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCWSTR str )
827 INT min, max, res = -1;
829 if (!HAS_STRINGS(descr))
830 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
832 max = descr->nb_items;
835 INT index = (min + max) / 2;
836 LPCWSTR p = descr->items[index].str;
837 if (*p == '[') /* drive or directory */
839 if (*str != '[') res = -1;
840 else if (p[1] == '-') /* drive */
842 if (str[1] == '-') res = str[2] - p[2];
847 if (str[1] == '-') res = 1;
848 else res = lstrcmpiW( str, p );
853 if (*str == '[') res = 1;
854 else res = lstrcmpiW( str, p );
856 if (!res) return index;
857 if (res < 0) max = index;
858 else min = index + 1;
864 /***********************************************************************
867 * Find the item beginning with a given string.
869 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
870 LPCWSTR str, BOOL exact )
875 if (start >= descr->nb_items) start = -1;
876 item = descr->items + start + 1;
877 if (HAS_STRINGS(descr))
879 if (!str || ! str[0] ) return LB_ERR;
882 for (i = start + 1; i < descr->nb_items; i++, item++)
883 if (!lstrcmpiW( str, item->str )) return i;
884 for (i = 0, item = descr->items; i <= start; i++, item++)
885 if (!lstrcmpiW( str, item->str )) return i;
889 /* Special case for drives and directories: ignore prefix */
890 #define CHECK_DRIVE(item) \
891 if ((item)->str[0] == '[') \
893 if (!strncmpiW( str, (item)->str+1, len )) return i; \
894 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
898 INT len = strlenW(str);
899 for (i = start + 1; i < descr->nb_items; i++, item++)
901 if (!strncmpiW( str, item->str, len )) return i;
904 for (i = 0, item = descr->items; i <= start; i++, item++)
906 if (!strncmpiW( str, item->str, len )) return i;
914 if (exact && (descr->style & LBS_SORT))
915 /* If sorted, use a WM_COMPAREITEM binary search */
916 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
918 /* Otherwise use a linear search */
919 for (i = start + 1; i < descr->nb_items; i++, item++)
920 if (item->data == (DWORD)str) return i;
921 for (i = 0, item = descr->items; i <= start; i++, item++)
922 if (item->data == (DWORD)str) return i;
928 /***********************************************************************
929 * LISTBOX_GetSelCount
931 static LRESULT LISTBOX_GetSelCount( LB_DESCR *descr )
934 LB_ITEMDATA *item = descr->items;
936 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
937 for (i = count = 0; i < descr->nb_items; i++, item++)
938 if (item->selected) count++;
943 /***********************************************************************
944 * LISTBOX_GetSelItems16
946 static LRESULT LISTBOX_GetSelItems16( LB_DESCR *descr, INT16 max, LPINT16 array )
949 LB_ITEMDATA *item = descr->items;
951 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
952 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
953 if (item->selected) array[count++] = (INT16)i;
958 /***********************************************************************
959 * LISTBOX_GetSelItems
961 static LRESULT LISTBOX_GetSelItems( LB_DESCR *descr, INT max, LPINT array )
964 LB_ITEMDATA *item = descr->items;
966 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
967 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
968 if (item->selected) array[count++] = i;
973 /***********************************************************************
976 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
978 INT i, col_pos = descr->page_size - 1;
980 RECT focusRect = {-1, -1, -1, -1};
982 HBRUSH hbrush, oldBrush = 0;
984 if (descr->style & LBS_NOREDRAW) return 0;
986 SetRect( &rect, 0, 0, descr->width, descr->height );
987 if (descr->style & LBS_MULTICOLUMN)
988 rect.right = rect.left + descr->column_width;
989 else if (descr->horz_pos)
991 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
992 rect.right += descr->horz_pos;
995 if (descr->font) oldFont = SelectObject( hdc, descr->font );
996 hbrush = SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
997 hdc, (LPARAM)wnd->hwndSelf );
998 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
999 if (wnd->dwStyle & WS_DISABLED)
1000 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1002 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1005 /* Special case for empty listbox: paint focus rect */
1006 rect.bottom = rect.top + descr->item_height;
1007 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
1009 rect.top = rect.bottom;
1012 /* Paint all the item, regarding the selection
1013 Focus state will be painted after */
1015 for (i = descr->top_item; i < descr->nb_items; i++)
1017 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1018 rect.bottom = rect.top + descr->item_height;
1020 rect.bottom = rect.top + descr->items[i].height;
1022 if (i == descr->focus_item)
1024 /* keep the focus rect, to paint the focus item after */
1025 focusRect.left = rect.left;
1026 focusRect.right = rect.right;
1027 focusRect.top = rect.top;
1028 focusRect.bottom = rect.bottom;
1030 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1031 rect.top = rect.bottom;
1033 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1035 if (!IS_OWNERDRAW(descr))
1037 /* Clear the bottom of the column */
1038 if (rect.top < descr->height)
1040 rect.bottom = descr->height;
1041 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1042 &rect, NULL, 0, NULL );
1046 /* Go to the next column */
1047 rect.left += descr->column_width;
1048 rect.right += descr->column_width;
1050 col_pos = descr->page_size - 1;
1055 if (rect.top >= descr->height) break;
1059 /* Paint the focus item now */
1060 if (focusRect.top != focusRect.bottom && descr->caret_on)
1061 LISTBOX_PaintItem( wnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1063 if (!IS_OWNERDRAW(descr))
1065 /* Clear the remainder of the client area */
1066 if (rect.top < descr->height)
1068 rect.bottom = descr->height;
1069 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1070 &rect, NULL, 0, NULL );
1072 if (rect.right < descr->width)
1074 rect.left = rect.right;
1075 rect.right = descr->width;
1077 rect.bottom = descr->height;
1078 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1079 &rect, NULL, 0, NULL );
1082 if (oldFont) SelectObject( hdc, oldFont );
1083 if (oldBrush) SelectObject( hdc, oldBrush );
1088 /***********************************************************************
1089 * LISTBOX_InvalidateItems
1091 * Invalidate all items from a given item. If the specified item is not
1092 * visible, nothing happens.
1094 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
1098 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1100 if (descr->style & LBS_NOREDRAW)
1102 descr->style |= LBS_DISPLAYCHANGED;
1105 rect.bottom = descr->height;
1106 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1107 if (descr->style & LBS_MULTICOLUMN)
1109 /* Repaint the other columns */
1110 rect.left = rect.right;
1111 rect.right = descr->width;
1113 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1119 /***********************************************************************
1120 * LISTBOX_GetItemHeight
1122 static LRESULT LISTBOX_GetItemHeight( LB_DESCR *descr, INT index )
1124 if (descr->style & LBS_OWNERDRAWVARIABLE)
1126 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1127 return descr->items[index].height;
1129 else return descr->item_height;
1133 /***********************************************************************
1134 * LISTBOX_SetItemHeight
1136 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1139 if (!height) height = 1;
1141 if (descr->style & LBS_OWNERDRAWVARIABLE)
1143 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1144 TRACE("[%04x]: item %d height = %d\n",
1145 wnd->hwndSelf, index, height );
1146 descr->items[index].height = height;
1147 LISTBOX_UpdateScroll( wnd, descr );
1148 LISTBOX_InvalidateItems( wnd, descr, index );
1150 else if (height != descr->item_height)
1152 TRACE("[%04x]: new height = %d\n",
1153 wnd->hwndSelf, height );
1154 descr->item_height = height;
1155 LISTBOX_UpdatePage( wnd, descr );
1156 LISTBOX_UpdateScroll( wnd, descr );
1157 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1163 /***********************************************************************
1164 * LISTBOX_SetHorizontalPos
1166 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1170 if (pos > descr->horz_extent - descr->width)
1171 pos = descr->horz_extent - descr->width;
1172 if (pos < 0) pos = 0;
1173 if (!(diff = descr->horz_pos - pos)) return;
1174 TRACE("[%04x]: new horz pos = %d\n",
1175 wnd->hwndSelf, pos );
1176 descr->horz_pos = pos;
1177 LISTBOX_UpdateScroll( wnd, descr );
1178 if (abs(diff) < descr->width)
1179 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1180 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1182 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1186 /***********************************************************************
1187 * LISTBOX_SetHorizontalExtent
1189 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1192 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1194 if (extent <= 0) extent = 1;
1195 if (extent == descr->horz_extent) return LB_OKAY;
1196 TRACE("[%04x]: new horz extent = %d\n",
1197 wnd->hwndSelf, extent );
1198 descr->horz_extent = extent;
1199 if (descr->horz_pos > extent - descr->width)
1200 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1202 LISTBOX_UpdateScroll( wnd, descr );
1207 /***********************************************************************
1208 * LISTBOX_SetColumnWidth
1210 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, INT width)
1212 if (width == descr->column_width) return LB_OKAY;
1213 TRACE("[%04x]: new column width = %d\n",
1214 wnd->hwndSelf, width );
1215 descr->column_width = width;
1216 LISTBOX_UpdatePage( wnd, descr );
1221 /***********************************************************************
1224 * Returns the item height.
1226 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1234 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1236 ERR("unable to get DC.\n" );
1239 if (font) oldFont = SelectObject( hdc, font );
1240 GetTextMetricsW( hdc, &tm );
1241 if (oldFont) SelectObject( hdc, oldFont );
1242 ReleaseDC( wnd->hwndSelf, hdc );
1243 if (!IS_OWNERDRAW(descr))
1244 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1245 return tm.tmHeight ;
1249 /***********************************************************************
1250 * LISTBOX_MakeItemVisible
1252 * Make sure that a given item is partially or fully visible.
1254 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1259 if (index <= descr->top_item) top = index;
1260 else if (descr->style & LBS_MULTICOLUMN)
1262 INT cols = descr->width;
1263 if (!fully) cols += descr->column_width - 1;
1264 if (cols >= descr->column_width) cols /= descr->column_width;
1266 if (index < descr->top_item + (descr->page_size * cols)) return;
1267 top = index - descr->page_size * (cols - 1);
1269 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1271 INT height = fully ? descr->items[index].height : 1;
1272 for (top = index; top > descr->top_item; top--)
1273 if ((height += descr->items[top-1].height) > descr->height) break;
1277 if (index < descr->top_item + descr->page_size) return;
1278 if (!fully && (index == descr->top_item + descr->page_size) &&
1279 (descr->height > (descr->page_size * descr->item_height))) return;
1280 top = index - descr->page_size + 1;
1282 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1285 /***********************************************************************
1286 * LISTBOX_SetCaretIndex
1289 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1292 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1293 BOOL fully_visible )
1295 INT oldfocus = descr->focus_item;
1297 if (descr->style & LBS_NOSEL) return LB_ERR;
1298 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1299 if (index == oldfocus) return LB_OKAY;
1300 descr->focus_item = index;
1301 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1302 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1304 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1305 if (descr->caret_on && (descr->in_focus))
1306 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1312 /***********************************************************************
1313 * LISTBOX_SelectItemRange
1315 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1317 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1322 /* A few sanity checks */
1324 if (descr->style & LBS_NOSEL) return LB_ERR;
1325 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1326 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1327 if (last == -1) last = descr->nb_items - 1;
1328 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1329 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1330 /* selected_item reflects last selected/unselected item on multiple sel */
1331 descr->selected_item = last;
1333 if (on) /* Turn selection on */
1335 for (i = first; i <= last; i++)
1337 if (descr->items[i].selected) continue;
1338 descr->items[i].selected = TRUE;
1339 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1341 LISTBOX_SetCaretIndex( wnd, descr, last, TRUE );
1343 else /* Turn selection off */
1345 for (i = first; i <= last; i++)
1347 if (!descr->items[i].selected) continue;
1348 descr->items[i].selected = FALSE;
1349 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1355 /***********************************************************************
1356 * LISTBOX_SetSelection
1358 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1359 BOOL on, BOOL send_notify )
1361 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1363 if (descr->style & LBS_NOSEL) return LB_ERR;
1364 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1365 if (descr->style & LBS_MULTIPLESEL)
1367 if (index == -1) /* Select all items */
1368 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1369 else /* Only one item */
1370 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1374 INT oldsel = descr->selected_item;
1375 if (index == oldsel) return LB_OKAY;
1376 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1377 if (index != -1) descr->items[index].selected = TRUE;
1378 descr->selected_item = index;
1379 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT );
1380 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1381 if (send_notify && descr->nb_items) SEND_NOTIFICATION( wnd, descr,
1382 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1384 if( descr->lphc ) /* set selection change flag for parent combo */
1385 descr->lphc->wState |= CBF_SELCHANGE;
1391 /***********************************************************************
1394 * Change the caret position and extend the selection to the new caret.
1396 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1397 BOOL fully_visible )
1399 INT oldfocus = descr->focus_item;
1401 if ((index < 0) || (index >= descr->nb_items))
1404 /* Important, repaint needs to be done in this order if
1405 you want to mimic Windows behavior:
1406 1. Remove the focus and paint the item
1407 2. Remove the selection and paint the item(s)
1408 3. Set the selection and repaint the item(s)
1409 4. Set the focus to 'index' and repaint the item */
1411 /* 1. remove the focus and repaint the item */
1412 descr->focus_item = -1;
1413 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1414 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1416 /* 2. then turn off the previous selection */
1417 /* 3. repaint the new selected item */
1418 if (descr->style & LBS_EXTENDEDSEL)
1420 if (descr->anchor_item != -1)
1422 INT first = min( index, descr->anchor_item );
1423 INT last = max( index, descr->anchor_item );
1425 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1426 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1427 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1430 else if (!(descr->style & LBS_MULTIPLESEL))
1432 /* Set selection to new caret item */
1433 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1436 /* 4. repaint the new item with the focus */
1437 descr->focus_item = index;
1438 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1439 if (descr->caret_on && (descr->in_focus))
1440 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1444 /***********************************************************************
1445 * LISTBOX_InsertItem
1447 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1448 LPWSTR str, DWORD data )
1452 INT oldfocus = descr->focus_item;
1454 if (index == -1) index = descr->nb_items;
1455 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1456 if (!descr->items) max_items = 0;
1457 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1458 if (descr->nb_items == max_items)
1460 /* We need to grow the array */
1461 max_items += LB_ARRAY_GRANULARITY;
1462 if (!(item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1463 max_items * sizeof(LB_ITEMDATA) )))
1465 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1468 descr->items = item;
1471 /* Insert the item structure */
1473 item = &descr->items[index];
1474 if (index < descr->nb_items)
1475 RtlMoveMemory( item + 1, item,
1476 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1480 item->selected = FALSE;
1483 /* Get item height */
1485 if (descr->style & LBS_OWNERDRAWVARIABLE)
1487 MEASUREITEMSTRUCT mis;
1489 mis.CtlType = ODT_LISTBOX;
1490 mis.CtlID = wnd->wIDmenu;
1492 mis.itemData = descr->items[index].data;
1493 mis.itemHeight = descr->item_height;
1494 SendMessageW( descr->owner, WM_MEASUREITEM, wnd->wIDmenu, (LPARAM)&mis );
1495 item->height = mis.itemHeight ? mis.itemHeight : 1;
1496 TRACE("[%04x]: measure item %d (%s) = %d\n",
1497 wnd->hwndSelf, index, str ? debugstr_w(str) : "", item->height );
1500 /* Repaint the items */
1502 LISTBOX_UpdateScroll( wnd, descr );
1503 LISTBOX_InvalidateItems( wnd, descr, index );
1505 /* Move selection and focused item */
1506 /* If listbox was empty, set focus to the first item */
1507 if (descr->nb_items == 1)
1508 LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1509 /* single select don't change selection index in win31 */
1510 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1512 descr->selected_item++;
1513 LISTBOX_SetSelection( wnd, descr, descr->selected_item-1, TRUE, FALSE );
1517 if (index <= descr->selected_item)
1519 descr->selected_item++;
1520 descr->focus_item = oldfocus; /* focus not changed */
1527 /***********************************************************************
1528 * LISTBOX_InsertString
1530 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1533 LPWSTR new_str = NULL;
1537 if (HAS_STRINGS(descr))
1539 static const WCHAR empty_stringW[] = { 0 };
1540 if (!str) str = empty_stringW;
1541 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1543 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1546 strcpyW(new_str, str);
1548 else data = (DWORD)str;
1550 if (index == -1) index = descr->nb_items;
1551 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1553 if (new_str) HeapFree( GetProcessHeap(), 0, new_str );
1557 TRACE("[%04x]: added item %d %s\n",
1558 wnd->hwndSelf, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1563 /***********************************************************************
1564 * LISTBOX_DeleteItem
1566 * Delete the content of an item. 'index' must be a valid index.
1568 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1570 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1571 * while Win95 sends it for all items with user data.
1572 * It's probably better to send it too often than not
1573 * often enough, so this is what we do here.
1575 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1577 DELETEITEMSTRUCT dis;
1579 dis.CtlType = ODT_LISTBOX;
1580 dis.CtlID = wnd->wIDmenu;
1582 dis.hwndItem = wnd->hwndSelf;
1583 dis.itemData = descr->items[index].data;
1584 SendMessageW( descr->owner, WM_DELETEITEM, wnd->wIDmenu, (LPARAM)&dis );
1586 if (HAS_STRINGS(descr) && descr->items[index].str)
1587 HeapFree( GetProcessHeap(), 0, descr->items[index].str );
1591 /***********************************************************************
1592 * LISTBOX_RemoveItem
1594 * Remove an item from the listbox and delete its content.
1596 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1601 if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1602 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1604 /* We need to invalidate the original rect instead of the updated one. */
1605 LISTBOX_InvalidateItems( wnd, descr, index );
1607 LISTBOX_DeleteItem( wnd, descr, index );
1609 /* Remove the item */
1611 item = &descr->items[index];
1612 if (index < descr->nb_items-1)
1613 RtlMoveMemory( item, item + 1,
1614 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1616 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1618 /* Shrink the item array if possible */
1620 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1621 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1623 max_items -= LB_ARRAY_GRANULARITY;
1624 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1625 max_items * sizeof(LB_ITEMDATA) );
1626 if (item) descr->items = item;
1628 /* Repaint the items */
1630 LISTBOX_UpdateScroll( wnd, descr );
1631 /* if we removed the scrollbar, reset the top of the list
1632 (correct for owner-drawn ???) */
1633 if (descr->nb_items == descr->page_size)
1634 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1636 /* Move selection and focused item */
1637 if (!IS_MULTISELECT(descr))
1639 if (index == descr->selected_item)
1640 descr->selected_item = -1;
1641 else if (index < descr->selected_item)
1643 descr->selected_item--;
1644 if (ISWIN31) /* win 31 do not change the selected item number */
1645 LISTBOX_SetSelection( wnd, descr, descr->selected_item + 1, TRUE, FALSE);
1649 if (descr->focus_item >= descr->nb_items)
1651 descr->focus_item = descr->nb_items - 1;
1652 if (descr->focus_item < 0) descr->focus_item = 0;
1658 /***********************************************************************
1659 * LISTBOX_ResetContent
1661 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1665 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1666 if (descr->items) HeapFree( GetProcessHeap(), 0, descr->items );
1667 descr->nb_items = 0;
1668 descr->top_item = 0;
1669 descr->selected_item = -1;
1670 descr->focus_item = 0;
1671 descr->anchor_item = -1;
1672 descr->items = NULL;
1676 /***********************************************************************
1679 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1683 if (HAS_STRINGS(descr)) return LB_ERR;
1684 /* FIXME: this is far from optimal... */
1685 if (count > descr->nb_items)
1687 while (count > descr->nb_items)
1688 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1691 else if (count < descr->nb_items)
1693 while (count < descr->nb_items)
1694 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1701 /***********************************************************************
1704 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1705 LPCWSTR filespec, BOOL long_names )
1708 LRESULT ret = LB_OKAY;
1709 WIN32_FIND_DATAW entry;
1712 /* don't scan directory if we just want drives exclusively */
1713 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1714 /* scan directory */
1715 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1717 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1724 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1726 static const WCHAR bracketW[] = { ']',0 };
1727 static const WCHAR dotW[] = { '.',0 };
1728 if (!(attrib & DDL_DIRECTORY) ||
1729 !strcmpW( entry.cAlternateFileName, dotW )) continue;
1731 if (long_names) strcpyW( buffer + 1, entry.cFileName );
1732 else strcpyW( buffer + 1, entry.cAlternateFileName );
1733 strcatW(buffer, bracketW);
1735 else /* not a directory */
1737 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1738 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1740 if ((attrib & DDL_EXCLUSIVE) &&
1741 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1744 if (long_names) strcpyW( buffer, entry.cFileName );
1745 else strcpyW( buffer, entry.cAlternateFileName );
1747 if (!long_names) CharLowerW( buffer );
1748 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1749 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1751 } while (FindNextFileW( handle, &entry ));
1752 FindClose( handle );
1757 if ((ret >= 0) && (attrib & DDL_DRIVES))
1759 WCHAR buffer[] = {'[','-','a','-',']',0};
1760 WCHAR root[] = {'A',':','\\',0};
1762 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1764 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1765 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1773 /***********************************************************************
1774 * LISTBOX_HandleVScroll
1776 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr, WPARAM wParam )
1780 if (descr->style & LBS_MULTICOLUMN) return 0;
1781 switch(LOWORD(wParam))
1784 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1787 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1790 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1791 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1794 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1795 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1797 case SB_THUMBPOSITION:
1798 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1801 info.cbSize = sizeof(info);
1802 info.fMask = SIF_TRACKPOS;
1803 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1804 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1807 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1810 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1817 /***********************************************************************
1818 * LISTBOX_HandleHScroll
1820 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr, WPARAM wParam )
1825 if (descr->style & LBS_MULTICOLUMN)
1827 switch(LOWORD(wParam))
1830 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1834 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1838 page = descr->width / descr->column_width;
1839 if (page < 1) page = 1;
1840 LISTBOX_SetTopItem( wnd, descr,
1841 descr->top_item - page * descr->page_size, TRUE );
1844 page = descr->width / descr->column_width;
1845 if (page < 1) page = 1;
1846 LISTBOX_SetTopItem( wnd, descr,
1847 descr->top_item + page * descr->page_size, TRUE );
1849 case SB_THUMBPOSITION:
1850 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1854 info.cbSize = sizeof(info);
1855 info.fMask = SIF_TRACKPOS;
1856 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1857 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1861 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1864 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1868 else if (descr->horz_extent)
1870 switch(LOWORD(wParam))
1873 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1876 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1879 LISTBOX_SetHorizontalPos( wnd, descr,
1880 descr->horz_pos - descr->width );
1883 LISTBOX_SetHorizontalPos( wnd, descr,
1884 descr->horz_pos + descr->width );
1886 case SB_THUMBPOSITION:
1887 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1890 info.cbSize = sizeof(info);
1891 info.fMask = SIF_TRACKPOS;
1892 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1893 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1896 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1899 LISTBOX_SetHorizontalPos( wnd, descr,
1900 descr->horz_extent - descr->width );
1907 static LRESULT LISTBOX_HandleMouseWheel(WND *wnd, LB_DESCR *descr, WPARAM wParam )
1909 short gcWheelDelta = 0;
1910 UINT pulScrollLines = 3;
1912 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1914 gcWheelDelta -= (short) HIWORD(wParam);
1916 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1918 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1919 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1920 LISTBOX_SetTopItem( wnd, descr, descr->top_item + cLineScroll, TRUE );
1925 /***********************************************************************
1926 * LISTBOX_HandleLButtonDown
1928 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1929 WPARAM wParam, INT x, INT y )
1931 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
1932 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1933 wnd->hwndSelf, x, y, index );
1934 if (!descr->caret_on && (descr->in_focus)) return 0;
1936 if (!descr->in_focus)
1938 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1939 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1940 : descr->lphc->self->hwndSelf );
1943 if (index == -1) return 0;
1945 if (descr->style & LBS_EXTENDEDSEL)
1947 /* we should perhaps make sure that all items are deselected
1948 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1949 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1950 LISTBOX_SetSelection( wnd, descr, -1, FALSE, FALSE);
1953 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1954 if (wParam & MK_CONTROL)
1956 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1957 LISTBOX_SetSelection( wnd, descr, index,
1958 !descr->items[index].selected,
1959 (descr->style & LBS_NOTIFY) != 0);
1961 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1965 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1966 LISTBOX_SetSelection( wnd, descr, index,
1967 (!(descr->style & LBS_MULTIPLESEL) ||
1968 !descr->items[index].selected),
1969 (descr->style & LBS_NOTIFY) != 0 );
1972 descr->captured = TRUE;
1973 SetCapture( wnd->hwndSelf );
1977 if (descr->style & LBS_NOTIFY )
1978 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
1979 MAKELPARAM( x, y ) );
1980 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1987 if (DragDetect( wnd->hwndSelf, pt ))
1988 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
1995 /*************************************************************************
1996 * LISTBOX_HandleLButtonDownCombo [Internal]
1998 * Process LButtonDown message for the ComboListBox
2001 * pWnd [I] The windows internal structure
2002 * pDescr [I] The ListBox internal structure
2003 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2004 * x [I] X Mouse Coordinate
2005 * y [I] Y Mouse Coordinate
2008 * 0 since we are processing the WM_LBUTTONDOWN Message
2011 * This function is only to be used when a ListBox is a ComboListBox
2014 static LRESULT LISTBOX_HandleLButtonDownCombo( WND *pWnd, LB_DESCR *pDescr,
2015 UINT msg, WPARAM wParam, INT x, INT y)
2017 RECT clientRect, screenRect;
2023 GetClientRect(pWnd->hwndSelf, &clientRect);
2025 if(PtInRect(&clientRect, mousePos))
2027 /* MousePos is in client, resume normal processing */
2028 if (msg == WM_LBUTTONDOWN)
2030 pDescr->lphc->droppedIndex = pDescr->nb_items ? pDescr->selected_item : -1;
2031 return LISTBOX_HandleLButtonDown( pWnd, pDescr, wParam, x, y);
2033 else if (pDescr->style & LBS_NOTIFY)
2034 SEND_NOTIFICATION( pWnd, pDescr, LBN_DBLCLK );
2039 POINT screenMousePos;
2040 HWND hWndOldCapture;
2042 /* Check the Non-Client Area */
2043 screenMousePos = mousePos;
2044 hWndOldCapture = GetCapture();
2046 GetWindowRect(pWnd->hwndSelf, &screenRect);
2047 ClientToScreen(pWnd->hwndSelf, &screenMousePos);
2049 if(!PtInRect(&screenRect, screenMousePos))
2051 LISTBOX_SetCaretIndex( pWnd, pDescr, pDescr->lphc->droppedIndex, FALSE );
2052 LISTBOX_SetSelection( pWnd, pDescr, pDescr->lphc->droppedIndex, FALSE, FALSE );
2053 COMBO_FlipListbox( pDescr->lphc, FALSE, FALSE );
2058 /* Check to see the NC is a scrollbar */
2060 /* Check Vertical scroll bar */
2061 if (pWnd->dwStyle & WS_VSCROLL)
2063 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2064 if (PtInRect( &clientRect, mousePos ))
2066 nHitTestType = HTVSCROLL;
2069 /* Check horizontal scroll bar */
2070 if (pWnd->dwStyle & WS_HSCROLL)
2072 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2073 if (PtInRect( &clientRect, mousePos ))
2075 nHitTestType = HTHSCROLL;
2078 /* Windows sends this message when a scrollbar is clicked
2081 if(nHitTestType != 0)
2083 SendMessageW(pWnd->hwndSelf, WM_NCLBUTTONDOWN, nHitTestType,
2084 MAKELONG(screenMousePos.x, screenMousePos.y));
2086 /* Resume the Capture after scrolling is complete
2088 if(hWndOldCapture != 0)
2090 SetCapture(hWndOldCapture);
2097 /***********************************************************************
2098 * LISTBOX_HandleLButtonUp
2100 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
2102 if (LISTBOX_Timer != LB_TIMER_NONE)
2103 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2104 LISTBOX_Timer = LB_TIMER_NONE;
2105 if (descr->captured)
2107 descr->captured = FALSE;
2108 if (GetCapture() == wnd->hwndSelf) ReleaseCapture();
2109 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2110 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2116 /***********************************************************************
2117 * LISTBOX_HandleTimer
2119 * Handle scrolling upon a timer event.
2120 * Return TRUE if scrolling should continue.
2122 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
2123 INT index, TIMER_DIRECTION dir )
2128 if (descr->top_item) index = descr->top_item - 1;
2132 if (descr->top_item) index -= descr->page_size;
2135 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2136 if (index == descr->focus_item) index++;
2137 if (index >= descr->nb_items) index = descr->nb_items - 1;
2139 case LB_TIMER_RIGHT:
2140 if (index + descr->page_size < descr->nb_items)
2141 index += descr->page_size;
2146 if (index == descr->focus_item) return FALSE;
2147 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
2152 /***********************************************************************
2153 * LISTBOX_HandleSystemTimer
2155 * WM_SYSTIMER handler.
2157 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
2159 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
2161 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2162 LISTBOX_Timer = LB_TIMER_NONE;
2168 /***********************************************************************
2169 * LISTBOX_HandleMouseMove
2171 * WM_MOUSEMOVE handler.
2173 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
2177 TIMER_DIRECTION dir = LB_TIMER_NONE;
2179 if (!descr->captured) return;
2181 if (descr->style & LBS_MULTICOLUMN)
2184 else if (y >= descr->item_height * descr->page_size)
2185 y = descr->item_height * descr->page_size - 1;
2189 dir = LB_TIMER_LEFT;
2192 else if (x >= descr->width)
2194 dir = LB_TIMER_RIGHT;
2195 x = descr->width - 1;
2200 if (y < 0) dir = LB_TIMER_UP; /* above */
2201 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2204 index = LISTBOX_GetItemFromPoint( descr, x, y );
2205 if (index == -1) index = descr->focus_item;
2206 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
2208 /* Start/stop the system timer */
2210 if (dir != LB_TIMER_NONE)
2211 SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2212 else if (LISTBOX_Timer != LB_TIMER_NONE)
2213 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2214 LISTBOX_Timer = dir;
2218 /***********************************************************************
2219 * LISTBOX_HandleKeyDown
2221 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
2224 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2225 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2226 bForceSelection = FALSE; /* only for single select list */
2228 if (descr->style & LBS_WANTKEYBOARDINPUT)
2230 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2231 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2233 if (caret == -2) return 0;
2235 if (caret == -1) switch(wParam)
2238 if (descr->style & LBS_MULTICOLUMN)
2240 bForceSelection = FALSE;
2241 if (descr->focus_item >= descr->page_size)
2242 caret = descr->focus_item - descr->page_size;
2247 caret = descr->focus_item - 1;
2248 if (caret < 0) caret = 0;
2251 if (descr->style & LBS_MULTICOLUMN)
2253 bForceSelection = FALSE;
2254 if (descr->focus_item + descr->page_size < descr->nb_items)
2255 caret = descr->focus_item + descr->page_size;
2260 caret = descr->focus_item + 1;
2261 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2265 if (descr->style & LBS_MULTICOLUMN)
2267 INT page = descr->width / descr->column_width;
2268 if (page < 1) page = 1;
2269 caret = descr->focus_item - (page * descr->page_size) + 1;
2271 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2272 if (caret < 0) caret = 0;
2275 if (descr->style & LBS_MULTICOLUMN)
2277 INT page = descr->width / descr->column_width;
2278 if (page < 1) page = 1;
2279 caret = descr->focus_item + (page * descr->page_size) - 1;
2281 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2282 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2288 caret = descr->nb_items - 1;
2291 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2292 else if (descr->style & LBS_MULTIPLESEL)
2294 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
2295 !descr->items[descr->focus_item].selected,
2296 (descr->style & LBS_NOTIFY) != 0 );
2300 bForceSelection = FALSE;
2302 if (bForceSelection) /* focused item is used instead of key */
2303 caret = descr->focus_item;
2306 if ((descr->style & LBS_EXTENDEDSEL) &&
2307 !(GetKeyState( VK_SHIFT ) & 0x8000))
2308 descr->anchor_item = caret;
2309 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2310 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2311 if (descr->style & LBS_NOTIFY)
2315 /* make sure that combo parent doesn't hide us */
2316 descr->lphc->wState |= CBF_NOROLLUP;
2318 if (descr->nb_items) SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2325 /***********************************************************************
2326 * LISTBOX_HandleChar
2328 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr, WCHAR charW )
2336 if (descr->style & LBS_WANTKEYBOARDINPUT)
2338 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2339 MAKEWPARAM(charW, descr->focus_item),
2341 if (caret == -2) return 0;
2344 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2347 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2348 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2349 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2350 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2351 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2357 /***********************************************************************
2360 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2363 MEASUREITEMSTRUCT mis;
2366 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2369 GetClientRect( wnd->hwndSelf, &rect );
2370 descr->owner = GetParent( wnd->hwndSelf );
2371 descr->style = wnd->dwStyle;
2372 descr->width = rect.right - rect.left;
2373 descr->height = rect.bottom - rect.top;
2374 descr->items = NULL;
2375 descr->nb_items = 0;
2376 descr->top_item = 0;
2377 descr->selected_item = -1;
2378 descr->focus_item = 0;
2379 descr->anchor_item = -1;
2380 descr->item_height = 1;
2381 descr->page_size = 1;
2382 descr->column_width = 150;
2383 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2384 descr->horz_pos = 0;
2387 descr->caret_on = lphc ? FALSE : TRUE;
2388 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2389 descr->in_focus = FALSE;
2390 descr->captured = FALSE;
2392 descr->locale = 0; /* FIXME */
2395 if( ( GetExpWinVer16( wnd->hInstance ) & 0xFF00 ) == 0x0300
2396 && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2398 /* Win95 document "List Box Differences" from MSDN:
2399 If a list box in a version 3.x application has either the
2400 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2401 horizontal and vertical scroll bars.
2403 descr->style |= WS_VSCROLL | WS_HSCROLL;
2408 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2409 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2410 descr->owner = lphc->self->hwndSelf;
2413 *(LB_DESCR **)wnd->wExtra = descr;
2415 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2417 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2418 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2419 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2420 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2422 if (descr->style & LBS_OWNERDRAWFIXED)
2424 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2426 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2427 descr->item_height = lphc->fixedOwnerDrawHeight;
2431 mis.CtlType = ODT_LISTBOX;
2432 mis.CtlID = wnd->wIDmenu;
2436 mis.itemHeight = descr->item_height;
2437 SendMessageW( descr->owner, WM_MEASUREITEM, wnd->wIDmenu, (LPARAM)&mis );
2438 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2442 TRACE("owner: %04x, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2447 /***********************************************************************
2450 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2452 LISTBOX_ResetContent( wnd, descr );
2453 HeapFree( GetProcessHeap(), 0, descr );
2459 /***********************************************************************
2462 static LRESULT WINAPI ListBoxWndProc_locked( WND* wnd, UINT msg,
2463 WPARAM wParam, LPARAM lParam, BOOL unicode )
2467 HWND hwnd = wnd->hwndSelf;
2469 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2471 if (msg == WM_CREATE)
2473 if (!LISTBOX_Create( wnd, NULL ))
2475 TRACE("creating wnd=%04x descr=%p\n", hwnd, *(LB_DESCR **)wnd->wExtra );
2478 /* Ignore all other messages before we get a WM_CREATE */
2479 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2480 DefWindowProcA( hwnd, msg, wParam, lParam );
2483 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2484 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2487 case LB_RESETCONTENT16:
2488 case LB_RESETCONTENT:
2489 LISTBOX_ResetContent( wnd, descr );
2490 LISTBOX_UpdateScroll( wnd, descr );
2491 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
2494 case LB_ADDSTRING16:
2495 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2501 if(unicode || !HAS_STRINGS(descr))
2502 textW = (LPWSTR)lParam;
2505 LPSTR textA = (LPSTR)lParam;
2506 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2507 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2508 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2510 wParam = LISTBOX_FindStringPos( wnd, descr, textW, FALSE );
2511 ret = LISTBOX_InsertString( wnd, descr, wParam, textW );
2512 if (!unicode && HAS_STRINGS(descr))
2513 HeapFree(GetProcessHeap(), 0, textW);
2517 case LB_INSERTSTRING16:
2518 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2519 wParam = (INT)(INT16)wParam;
2521 case LB_INSERTSTRING:
2525 if(unicode || !HAS_STRINGS(descr))
2526 textW = (LPWSTR)lParam;
2529 LPSTR textA = (LPSTR)lParam;
2530 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2531 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2532 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2534 ret = LISTBOX_InsertString( wnd, descr, wParam, textW );
2535 if(!unicode && HAS_STRINGS(descr))
2536 HeapFree(GetProcessHeap(), 0, textW);
2541 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2547 if(unicode || !HAS_STRINGS(descr))
2548 textW = (LPWSTR)lParam;
2551 LPSTR textA = (LPSTR)lParam;
2552 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2553 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2554 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2556 wParam = LISTBOX_FindFileStrPos( wnd, descr, textW );
2557 ret = LISTBOX_InsertString( wnd, descr, wParam, textW );
2558 if(!unicode && HAS_STRINGS(descr))
2559 HeapFree(GetProcessHeap(), 0, textW);
2563 case LB_DELETESTRING16:
2564 case LB_DELETESTRING:
2565 if (LISTBOX_RemoveItem( wnd, descr, wParam) != LB_ERR)
2566 return descr->nb_items;
2570 case LB_GETITEMDATA16:
2571 case LB_GETITEMDATA:
2572 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2574 return descr->items[wParam].data;
2576 case LB_SETITEMDATA16:
2577 case LB_SETITEMDATA:
2578 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2580 descr->items[wParam].data = (DWORD)lParam;
2585 return descr->nb_items;
2588 lParam = (LPARAM)MapSL(lParam);
2591 return LISTBOX_GetText( descr, wParam, lParam, unicode );
2593 case LB_GETTEXTLEN16:
2596 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2598 return (HAS_STRINGS(descr) ? strlenW(descr->items[wParam].str)
2601 case LB_GETCURSEL16:
2603 if (descr->nb_items==0)
2605 if (!IS_MULTISELECT(descr))
2606 return descr->selected_item;
2608 if (descr->selected_item!=-1)
2609 return descr->selected_item;
2611 return descr->focus_item;
2612 /* otherwise, if the user tries to move the selection with the */
2613 /* arrow keys, we will give the application something to choke on */
2614 case LB_GETTOPINDEX16:
2615 case LB_GETTOPINDEX:
2616 return descr->top_item;
2618 case LB_GETITEMHEIGHT16:
2619 case LB_GETITEMHEIGHT:
2620 return LISTBOX_GetItemHeight( descr, wParam );
2622 case LB_SETITEMHEIGHT16:
2623 lParam = LOWORD(lParam);
2625 case LB_SETITEMHEIGHT:
2626 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2628 case LB_ITEMFROMPOINT:
2633 pt.x = LOWORD(lParam);
2634 pt.y = HIWORD(lParam);
2637 rect.right = descr->width;
2638 rect.bottom = descr->height;
2640 return MAKELONG( LISTBOX_GetItemFromPoint(descr, pt.x, pt.y),
2641 !PtInRect( &rect, pt ) );
2644 case LB_SETCARETINDEX16:
2645 case LB_SETCARETINDEX:
2646 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2647 if (LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam ) == LB_ERR)
2654 case LB_GETCARETINDEX16:
2655 case LB_GETCARETINDEX:
2656 return descr->focus_item;
2658 case LB_SETTOPINDEX16:
2659 case LB_SETTOPINDEX:
2660 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2662 case LB_SETCOLUMNWIDTH16:
2663 case LB_SETCOLUMNWIDTH:
2664 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2666 case LB_GETITEMRECT16:
2669 ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2670 CONV_RECT32TO16( &rect, MapSL(lParam) );
2674 case LB_GETITEMRECT:
2675 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2677 case LB_FINDSTRING16:
2678 wParam = (INT)(INT16)wParam;
2679 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2685 if(unicode || !HAS_STRINGS(descr))
2686 textW = (LPWSTR)lParam;
2689 LPSTR textA = (LPSTR)lParam;
2690 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2691 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2692 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2694 ret = LISTBOX_FindString( wnd, descr, wParam, textW, FALSE );
2695 if(!unicode && HAS_STRINGS(descr))
2696 HeapFree(GetProcessHeap(), 0, textW);
2700 case LB_FINDSTRINGEXACT16:
2701 wParam = (INT)(INT16)wParam;
2702 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2704 case LB_FINDSTRINGEXACT:
2708 if(unicode || !HAS_STRINGS(descr))
2709 textW = (LPWSTR)lParam;
2712 LPSTR textA = (LPSTR)lParam;
2713 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2714 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2715 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2717 ret = LISTBOX_FindString( wnd, descr, wParam, textW, TRUE );
2718 if(!unicode && HAS_STRINGS(descr))
2719 HeapFree(GetProcessHeap(), 0, textW);
2723 case LB_SELECTSTRING16:
2724 wParam = (INT)(INT16)wParam;
2725 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2727 case LB_SELECTSTRING:
2732 if(HAS_STRINGS(descr))
2733 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2734 debugstr_a((LPSTR)lParam));
2735 if(unicode || !HAS_STRINGS(descr))
2736 textW = (LPWSTR)lParam;
2739 LPSTR textA = (LPSTR)lParam;
2740 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2741 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2742 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2744 index = LISTBOX_FindString( wnd, descr, wParam, textW, FALSE );
2745 if(!unicode && HAS_STRINGS(descr))
2746 HeapFree(GetProcessHeap(), 0, textW);
2747 if (index != LB_ERR)
2749 LISTBOX_SetCaretIndex( wnd, descr, index, TRUE );
2750 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2756 wParam = (INT)(INT16)wParam;
2759 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2761 return descr->items[wParam].selected;
2764 lParam = (INT)(INT16)lParam;
2767 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2769 case LB_SETCURSEL16:
2770 wParam = (INT)(INT16)wParam;
2773 if (IS_MULTISELECT(descr)) return LB_ERR;
2774 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2775 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2777 case LB_GETSELCOUNT16:
2778 case LB_GETSELCOUNT:
2779 return LISTBOX_GetSelCount( descr );
2781 case LB_GETSELITEMS16:
2782 return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2784 case LB_GETSELITEMS:
2785 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2787 case LB_SELITEMRANGE16:
2788 case LB_SELITEMRANGE:
2789 if (LOWORD(lParam) <= HIWORD(lParam))
2790 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2791 HIWORD(lParam), wParam );
2793 return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2794 LOWORD(lParam), wParam );
2796 case LB_SELITEMRANGEEX16:
2797 case LB_SELITEMRANGEEX:
2798 if ((INT)lParam >= (INT)wParam)
2799 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2801 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2803 case LB_GETHORIZONTALEXTENT16:
2804 case LB_GETHORIZONTALEXTENT:
2805 return descr->horz_extent;
2807 case LB_SETHORIZONTALEXTENT16:
2808 case LB_SETHORIZONTALEXTENT:
2809 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2811 case LB_GETANCHORINDEX16:
2812 case LB_GETANCHORINDEX:
2813 return descr->anchor_item;
2815 case LB_SETANCHORINDEX16:
2816 wParam = (INT)(INT16)wParam;
2818 case LB_SETANCHORINDEX:
2819 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2821 descr->anchor_item = (INT)wParam;
2825 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2826 * be set automatically (this is different in Win32) */
2827 if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2828 lParam = (LPARAM)MapSL(lParam);
2835 textW = (LPWSTR)lParam;
2838 LPSTR textA = (LPSTR)lParam;
2839 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2840 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2841 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2843 ret = LISTBOX_Directory( wnd, descr, wParam, textW, msg == LB_DIR );
2845 HeapFree(GetProcessHeap(), 0, textW);
2850 return descr->locale;
2853 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2856 case LB_INITSTORAGE:
2857 return LISTBOX_InitStorage( wnd, descr, wParam );
2860 return LISTBOX_SetCount( wnd, descr, (INT)wParam );
2862 case LB_SETTABSTOPS16:
2863 return LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2865 case LB_SETTABSTOPS:
2866 return LISTBOX_SetTabStops( wnd, descr, wParam, (LPINT)lParam, FALSE );
2870 if (descr->caret_on)
2872 descr->caret_on = TRUE;
2873 if ((descr->focus_item != -1) && (descr->in_focus))
2874 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2879 if (!descr->caret_on)
2881 descr->caret_on = FALSE;
2882 if ((descr->focus_item != -1) && (descr->in_focus))
2883 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2887 return LISTBOX_Destroy( wnd, descr );
2890 InvalidateRect( hwnd, NULL, TRUE );
2894 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2898 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2903 HDC hdc = ( wParam ) ? ((HDC)wParam)
2904 : BeginPaint( hwnd, &ps );
2905 ret = LISTBOX_Paint( wnd, descr, hdc );
2906 if( !wParam ) EndPaint( hwnd, &ps );
2910 LISTBOX_UpdateSize( wnd, descr );
2915 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2916 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2919 descr->in_focus = TRUE;
2920 descr->caret_on = TRUE;
2921 if (descr->focus_item != -1)
2922 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2923 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2926 descr->in_focus = FALSE;
2927 if ((descr->focus_item != -1) && descr->caret_on)
2928 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2929 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2932 return LISTBOX_HandleHScroll( wnd, descr, wParam );
2934 return LISTBOX_HandleVScroll( wnd, descr, wParam );
2936 if (wParam & (MK_SHIFT | MK_CONTROL))
2937 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2938 DefWindowProcA( hwnd, msg, wParam, lParam );
2939 return LISTBOX_HandleMouseWheel( wnd, descr, wParam );
2940 case WM_LBUTTONDOWN:
2941 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2942 (INT16)LOWORD(lParam),
2943 (INT16)HIWORD(lParam) );
2944 case WM_LBUTTONDBLCLK:
2945 if (descr->style & LBS_NOTIFY)
2946 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2949 if (GetCapture() == hwnd)
2950 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2951 (INT16)HIWORD(lParam) );
2954 return LISTBOX_HandleLButtonUp( wnd, descr );
2956 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2961 charW = (WCHAR)wParam;
2964 CHAR charA = (CHAR)wParam;
2965 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
2967 return LISTBOX_HandleChar( wnd, descr, charW );
2970 return LISTBOX_HandleSystemTimer( wnd, descr );
2972 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
2975 HBRUSH hbrush = SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
2976 wParam, (LPARAM)wnd->hwndSelf );
2977 TRACE("hbrush = %04x\n", hbrush);
2979 hbrush = GetSysColorBrush(COLOR_WINDOW);
2982 GetClientRect(hwnd, &rect);
2983 FillRect((HDC)wParam, &rect, hbrush);
2989 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
2990 SendMessageA( descr->owner, msg, wParam, lParam );
2994 case WM_QUERYDROPOBJECT:
2999 LPDRAGINFO16 dragInfo = MapSL( lParam );
3000 dragInfo->l = LISTBOX_GetItemFromPoint( descr, dragInfo->pt.x,
3002 return SendMessage16( descr->owner, msg, wParam, lParam );
3007 if ((msg >= WM_USER) && (msg < 0xc000))
3008 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
3009 hwnd, msg, wParam, lParam );
3010 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3011 DefWindowProcA( hwnd, msg, wParam, lParam );
3016 /***********************************************************************
3019 * This is just a wrapper for the real wndproc, it only does window locking
3022 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3025 WND* wndPtr = WIN_FindWndPtr( hwnd );
3029 res = ListBoxWndProc_locked(wndPtr, msg, wParam, lParam, FALSE);
3030 WIN_ReleaseWndPtr(wndPtr);
3035 /***********************************************************************
3038 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3041 WND* wndPtr = WIN_FindWndPtr( hwnd );
3045 res = ListBoxWndProc_locked(wndPtr, msg, wParam, lParam, TRUE);
3046 WIN_ReleaseWndPtr(wndPtr);
3051 /***********************************************************************
3052 * ComboLBWndProc_locked
3054 * The real combo listbox wndproc, but called with locked WND struct.
3056 static LRESULT WINAPI ComboLBWndProc_locked( WND* wnd, UINT msg,
3057 WPARAM wParam, LPARAM lParam, BOOL unicode )
3061 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
3063 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
3064 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
3066 hwnd = wnd->hwndSelf;
3068 if( descr || msg == WM_CREATE )
3070 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
3076 CREATESTRUCTA *lpcs = (CREATESTRUCTA *)lParam;
3077 TRACE_(combo)("\tpassed parent handle = %p\n",lpcs->lpCreateParams);
3078 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
3079 return LISTBOX_Create( wnd, lphc );
3082 if ( (TWEAK_WineLook > WIN31_LOOK) &&
3083 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
3089 mousePos.x = (INT16)LOWORD(lParam);
3090 mousePos.y = (INT16)HIWORD(lParam);
3093 * If we are in a dropdown combobox, we simulate that
3094 * the mouse is captured to show the tracking of the item.
3096 GetClientRect(hwnd, &clientRect);
3098 if (PtInRect( &clientRect, mousePos ))
3100 captured = descr->captured;
3101 descr->captured = TRUE;
3103 LISTBOX_HandleMouseMove( wnd, descr,
3104 mousePos.x, mousePos.y);
3106 descr->captured = captured;
3111 LISTBOX_HandleMouseMove( wnd, descr,
3112 mousePos.x, mousePos.y);
3121 * If we are in Win3.1 look, go with the default behavior.
3123 return unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3124 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3127 if (TWEAK_WineLook > WIN31_LOOK)
3133 * If the mouse button "up" is not in the listbox,
3134 * we make sure there is no selection by re-selecting the
3135 * item that was selected when the listbox was made visible.
3137 mousePos.x = (INT16)LOWORD(lParam);
3138 mousePos.y = (INT16)HIWORD(lParam);
3140 GetClientRect(hwnd, &clientRect);
3143 * When the user clicks outside the combobox and the focus
3144 * is lost, the owning combobox will send a fake buttonup with
3145 * 0xFFFFFFF as the mouse location, we must also revert the
3146 * selection to the original selection.
3148 if ( (lParam == (LPARAM)-1) ||
3149 (!PtInRect( &clientRect, mousePos )) )
3151 LISTBOX_MoveCaret( wnd, descr, lphc->droppedIndex, FALSE );
3154 return LISTBOX_HandleLButtonUp( wnd, descr );
3155 case WM_LBUTTONDBLCLK:
3156 case WM_LBUTTONDOWN:
3157 return LISTBOX_HandleLButtonDownCombo(wnd, descr, msg, wParam,
3158 (INT16)LOWORD(lParam),
3159 (INT16)HIWORD(lParam) );
3163 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3165 /* for some reason(?) Windows makes it possible to
3166 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3168 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3169 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3170 && (wParam == VK_DOWN || wParam == VK_UP)) )
3172 COMBO_FlipListbox( lphc, FALSE, FALSE );
3176 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
3178 case LB_SETCURSEL16:
3180 lRet = unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3181 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3182 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
3185 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3190 return unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3191 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3194 lRet = unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3195 DefWindowProcA( hwnd, msg, wParam, lParam );
3197 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
3202 /***********************************************************************
3205 * NOTE: in Windows, winproc address of the ComboLBox is the same
3206 * as that of the Listbox.
3208 * This is just a wrapper for the real wndproc, it only does window locking
3211 LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg,
3212 WPARAM wParam, LPARAM lParam )
3215 WND *wnd = WIN_FindWndPtr( hwnd );
3219 res = ComboLBWndProc_locked(wnd, msg, wParam, lParam, FALSE);
3220 WIN_ReleaseWndPtr(wnd);
3225 /***********************************************************************
3228 LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3231 WND *wnd = WIN_FindWndPtr( hwnd );
3235 res = ComboLBWndProc_locked(wnd, msg, wParam, lParam, TRUE);
3236 WIN_ReleaseWndPtr(wnd);