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
26 #include "wine/winuser16.h"
27 #include "wine/winbase16.h"
29 #include "wine/unicode.h"
35 #include "wine/debug.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(listbox);
39 WINE_DECLARE_DEBUG_CHANNEL(combo);
45 * Probably needs improvement:
49 /* Items array granularity */
50 #define LB_ARRAY_GRANULARITY 16
52 /* Scrolling timeout in ms */
53 #define LB_SCROLL_TIMEOUT 50
55 /* Listbox system timer id */
58 /* flag listbox changed while setredraw false - internal style */
59 #define LBS_DISPLAYCHANGED 0x80000000
64 LPWSTR str; /* Item text */
65 BOOL selected; /* Is item selected? */
66 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
67 DWORD data; /* User data */
70 /* Listbox structure */
73 HWND owner; /* Owner window to send notifications to */
74 UINT style; /* Window style */
75 INT width; /* Window width */
76 INT height; /* Window height */
77 LB_ITEMDATA *items; /* Array of items */
78 INT nb_items; /* Number of items */
79 INT top_item; /* Top visible item */
80 INT selected_item; /* Selected item */
81 INT focus_item; /* Item that has the focus */
82 INT anchor_item; /* Anchor item for extended selection */
83 INT item_height; /* Default item height */
84 INT page_size; /* Items per listbox page */
85 INT column_width; /* Column width for multi-column listboxes */
86 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
87 INT horz_pos; /* Horizontal position */
88 INT nb_tabs; /* Number of tabs in array */
89 INT *tabs; /* Array of tabs */
90 BOOL caret_on; /* Is caret on? */
91 BOOL captured; /* Is mouse captured? */
93 HFONT font; /* Current font */
94 LCID locale; /* Current locale for string comparisons */
95 LPHEADCOMBO lphc; /* ComboLBox */
99 #define IS_OWNERDRAW(descr) \
100 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
102 #define HAS_STRINGS(descr) \
103 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
106 #define IS_MULTISELECT(descr) \
107 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
109 #define SEND_NOTIFICATION(hwnd,descr,code) \
110 (SendMessageW( (descr)->owner, WM_COMMAND, \
111 MAKEWPARAM( GetWindowLongA((hwnd),GWL_ID), (code)), (LPARAM)(hwnd) ))
113 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
115 /* Current timer status */
125 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
127 static LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
128 static LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
129 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
130 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
132 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect );
134 /*********************************************************************
135 * listbox class descriptor
137 const struct builtin_class_descr LISTBOX_builtin_class =
139 "ListBox", /* name */
140 CS_GLOBALCLASS | CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
141 ListBoxWndProcA, /* procA */
142 ListBoxWndProcW, /* procW */
143 sizeof(LB_DESCR *), /* extra */
144 IDC_ARROWA, /* cursor */
149 /*********************************************************************
150 * combolbox class descriptor
152 const struct builtin_class_descr COMBOLBOX_builtin_class =
154 "ComboLBox", /* name */
155 CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS, /* style */
156 ComboLBWndProcA, /* procA */
157 ComboLBWndProcW, /* procW */
158 sizeof(LB_DESCR *), /* extra */
159 IDC_ARROWA, /* cursor */
164 /* check whether app is a Win 3.1 app */
165 inline static BOOL is_old_app( HWND hwnd )
167 return (GetExpWinVer16( GetWindowLongA(hwnd,GWL_HINSTANCE) ) & 0xFF00 ) == 0x0300;
171 /***********************************************************************
174 void LISTBOX_Dump( HWND hwnd )
178 LB_DESCR *descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 );
180 TRACE( "Listbox:\n" );
181 TRACE( "hwnd=%04x descr=%08x items=%d top=%d\n",
182 hwnd, (UINT)descr, descr->nb_items,
184 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
186 TRACE( "%4d: %-40s %d %08lx %3d\n",
187 i, debugstr_w(item->str), item->selected, item->data, item->height );
192 /***********************************************************************
193 * LISTBOX_GetCurrentPageSize
195 * Return the current page size
197 static INT LISTBOX_GetCurrentPageSize( LB_DESCR *descr )
200 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
201 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
203 if ((height += descr->items[i].height) > descr->height) break;
205 if (i == descr->top_item) return 1;
206 else return i - descr->top_item;
210 /***********************************************************************
211 * LISTBOX_GetMaxTopIndex
213 * Return the maximum possible index for the top of the listbox.
215 static INT LISTBOX_GetMaxTopIndex( LB_DESCR *descr )
219 if (descr->style & LBS_OWNERDRAWVARIABLE)
221 page = descr->height;
222 for (max = descr->nb_items - 1; max >= 0; max--)
223 if ((page -= descr->items[max].height) < 0) break;
224 if (max < descr->nb_items - 1) max++;
226 else if (descr->style & LBS_MULTICOLUMN)
228 if ((page = descr->width / descr->column_width) < 1) page = 1;
229 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
230 max = (max - page) * descr->page_size;
234 max = descr->nb_items - descr->page_size;
236 if (max < 0) max = 0;
241 /***********************************************************************
242 * LISTBOX_UpdateScroll
244 * Update the scrollbars. Should be called whenever the content
245 * of the listbox changes.
247 static void LISTBOX_UpdateScroll( HWND hwnd, LB_DESCR *descr )
251 /* Check the listbox scroll bar flags individually before we call
252 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
253 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
254 scroll bar when we do not need one.
255 if (!(descr->style & WS_VSCROLL)) return;
258 /* It is important that we check descr->style, and not wnd->dwStyle,
259 for WS_VSCROLL, as the former is exactly the one passed in
260 argument to CreateWindow.
261 In Windows (and from now on in Wine :) a listbox created
262 with such a style (no WS_SCROLL) does not update
263 the scrollbar with listbox-related data, thus letting
264 the programmer use it for his/her own purposes. */
266 if (descr->style & LBS_NOREDRAW) return;
267 info.cbSize = sizeof(info);
269 if (descr->style & LBS_MULTICOLUMN)
272 info.nMax = (descr->nb_items - 1) / descr->page_size;
273 info.nPos = descr->top_item / descr->page_size;
274 info.nPage = descr->width / descr->column_width;
275 if (info.nPage < 1) info.nPage = 1;
276 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
277 if (descr->style & LBS_DISABLENOSCROLL)
278 info.fMask |= SIF_DISABLENOSCROLL;
279 if (descr->style & WS_HSCROLL)
280 SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
282 info.fMask = SIF_RANGE;
283 if (descr->style & WS_VSCROLL)
284 SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
289 info.nMax = descr->nb_items - 1;
290 info.nPos = descr->top_item;
291 info.nPage = LISTBOX_GetCurrentPageSize( descr );
292 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
293 if (descr->style & LBS_DISABLENOSCROLL)
294 info.fMask |= SIF_DISABLENOSCROLL;
295 if (descr->style & WS_VSCROLL)
296 SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
298 if (descr->horz_extent)
301 info.nMax = descr->horz_extent - 1;
302 info.nPos = descr->horz_pos;
303 info.nPage = descr->width;
304 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
305 if (descr->style & LBS_DISABLENOSCROLL)
306 info.fMask |= SIF_DISABLENOSCROLL;
307 if (descr->style & WS_HSCROLL)
308 SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
314 /***********************************************************************
317 * Set the top item of the listbox, scrolling up or down if necessary.
319 static LRESULT LISTBOX_SetTopItem( HWND hwnd, LB_DESCR *descr, INT index,
322 INT max = LISTBOX_GetMaxTopIndex( descr );
323 if (index > max) index = max;
324 if (index < 0) index = 0;
325 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
326 if (descr->top_item == index) return LB_OKAY;
327 if (descr->style & LBS_MULTICOLUMN)
329 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
330 if (scroll && (abs(diff) < descr->width))
331 ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
332 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
340 if (descr->style & LBS_OWNERDRAWVARIABLE)
344 if (index > descr->top_item)
346 for (i = index - 1; i >= descr->top_item; i--)
347 diff -= descr->items[i].height;
351 for (i = index; i < descr->top_item; i++)
352 diff += descr->items[i].height;
356 diff = (descr->top_item - index) * descr->item_height;
358 if (abs(diff) < descr->height)
359 ScrollWindowEx( hwnd, 0, diff, NULL, NULL, 0, NULL,
360 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
364 if (!scroll) InvalidateRect( hwnd, NULL, TRUE );
365 descr->top_item = index;
366 LISTBOX_UpdateScroll( hwnd, descr );
371 /***********************************************************************
374 * Update the page size. Should be called when the size of
375 * the client area or the item height changes.
377 static void LISTBOX_UpdatePage( HWND hwnd, LB_DESCR *descr )
381 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
383 if (page_size == descr->page_size) return;
384 descr->page_size = page_size;
385 if (descr->style & LBS_MULTICOLUMN)
386 InvalidateRect( hwnd, NULL, TRUE );
387 LISTBOX_SetTopItem( hwnd, descr, descr->top_item, FALSE );
391 /***********************************************************************
394 * Update the size of the listbox. Should be called when the size of
395 * the client area changes.
397 static void LISTBOX_UpdateSize( HWND hwnd, LB_DESCR *descr )
401 GetClientRect( hwnd, &rect );
402 descr->width = rect.right - rect.left;
403 descr->height = rect.bottom - rect.top;
404 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
409 GetWindowRect( hwnd, &rect );
410 if(descr->item_height != 0)
411 remaining = descr->height % descr->item_height;
414 if ((descr->height > descr->item_height) && remaining)
416 if (is_old_app(hwnd))
417 { /* give a margin for error to 16 bits programs - if we need
418 less than the height of the nonclient area, round to the
419 *next* number of items */
420 int ncheight = rect.bottom - rect.top - descr->height;
421 if ((descr->item_height - remaining) <= ncheight)
422 remaining = remaining - descr->item_height;
424 TRACE("[%04x]: changing height %d -> %d\n",
425 hwnd, descr->height, descr->height - remaining );
426 SetWindowPos( hwnd, 0, 0, 0, rect.right - rect.left,
427 rect.bottom - rect.top - remaining,
428 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
432 TRACE("[%04x]: new size = %d,%d\n", hwnd, descr->width, descr->height );
433 LISTBOX_UpdatePage( hwnd, descr );
434 LISTBOX_UpdateScroll( hwnd, descr );
436 /* Invalidate the focused item so it will be repainted correctly */
437 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
439 InvalidateRect( hwnd, &rect, FALSE );
444 /***********************************************************************
445 * LISTBOX_GetItemRect
447 * Get the rectangle enclosing an item, in listbox client coordinates.
448 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
450 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect )
452 /* Index <= 0 is legal even on empty listboxes */
453 if (index && (index >= descr->nb_items)) return -1;
454 SetRect( rect, 0, 0, descr->width, descr->height );
455 if (descr->style & LBS_MULTICOLUMN)
457 INT col = (index / descr->page_size) -
458 (descr->top_item / descr->page_size);
459 rect->left += col * descr->column_width;
460 rect->right = rect->left + descr->column_width;
461 rect->top += (index % descr->page_size) * descr->item_height;
462 rect->bottom = rect->top + descr->item_height;
464 else if (descr->style & LBS_OWNERDRAWVARIABLE)
467 rect->right += descr->horz_pos;
468 if ((index >= 0) && (index < descr->nb_items))
470 if (index < descr->top_item)
472 for (i = descr->top_item-1; i >= index; i--)
473 rect->top -= descr->items[i].height;
477 for (i = descr->top_item; i < index; i++)
478 rect->top += descr->items[i].height;
480 rect->bottom = rect->top + descr->items[index].height;
486 rect->top += (index - descr->top_item) * descr->item_height;
487 rect->bottom = rect->top + descr->item_height;
488 rect->right += descr->horz_pos;
491 return ((rect->left < descr->width) && (rect->right > 0) &&
492 (rect->top < descr->height) && (rect->bottom > 0));
496 /***********************************************************************
497 * LISTBOX_GetItemFromPoint
499 * Return the item nearest from point (x,y) (in client coordinates).
501 static INT LISTBOX_GetItemFromPoint( LB_DESCR *descr, INT x, INT y )
503 INT index = descr->top_item;
505 if (!descr->nb_items) return -1; /* No items */
506 if (descr->style & LBS_OWNERDRAWVARIABLE)
511 while (index < descr->nb_items)
513 if ((pos += descr->items[index].height) > y) break;
522 if ((pos -= descr->items[index].height) <= y) break;
526 else if (descr->style & LBS_MULTICOLUMN)
528 if (y >= descr->item_height * descr->page_size) return -1;
529 if (y >= 0) index += y / descr->item_height;
530 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
531 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
535 index += (y / descr->item_height);
537 if (index < 0) return 0;
538 if (index >= descr->nb_items) return -1;
543 /***********************************************************************
548 static void LISTBOX_PaintItem( HWND hwnd, LB_DESCR *descr, HDC hdc,
549 const RECT *rect, INT index, UINT action, BOOL ignoreFocus )
551 LB_ITEMDATA *item = NULL;
552 if (index < descr->nb_items) item = &descr->items[index];
554 if (IS_OWNERDRAW(descr))
559 UINT id = GetWindowLongA( hwnd, GWL_ID );
563 if (action == ODA_FOCUS)
564 DrawFocusRect( hdc, rect );
566 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
570 /* some programs mess with the clipping region when
571 drawing the item, *and* restore the previous region
572 after they are done, so a region has better to exist
573 else everything ends clipped */
574 GetClientRect(hwnd, &r);
575 hrgn = CreateRectRgnIndirect(&r);
576 SelectClipRgn( hdc, hrgn);
577 DeleteObject( hrgn );
579 dis.CtlType = ODT_LISTBOX;
582 dis.itemAction = action;
586 if (item && item->selected) dis.itemState |= ODS_SELECTED;
587 if (!ignoreFocus && (descr->focus_item == index) &&
589 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
590 if (!IsWindowEnabled(hwnd)) dis.itemState |= ODS_DISABLED;
591 dis.itemData = item ? item->data : 0;
593 TRACE("[%04x]: drawitem %d (%s) action=%02x state=%02x rect=%d,%d-%d,%d\n",
594 hwnd, index, item ? debugstr_w(item->str) : "", action,
595 dis.itemState, rect->left, rect->top, rect->right, rect->bottom );
596 SendMessageW(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
600 COLORREF oldText = 0, oldBk = 0;
602 if (action == ODA_FOCUS)
604 DrawFocusRect( hdc, rect );
607 if (item && item->selected)
609 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
610 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
613 TRACE("[%04x]: painting %d (%s) action=%02x rect=%d,%d-%d,%d\n",
614 hwnd, index, item ? debugstr_w(item->str) : "", action,
615 rect->left, rect->top, rect->right, rect->bottom );
617 ExtTextOutW( hdc, rect->left + 1, rect->top,
618 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
619 else if (!(descr->style & LBS_USETABSTOPS))
620 ExtTextOutW( hdc, rect->left + 1, rect->top,
621 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
622 strlenW(item->str), NULL );
625 /* Output empty string to paint background in the full width. */
626 ExtTextOutW( hdc, rect->left + 1, rect->top,
627 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
628 TabbedTextOutW( hdc, rect->left + 1 , rect->top,
629 item->str, strlenW(item->str),
630 descr->nb_tabs, descr->tabs, 0);
632 if (item && item->selected)
634 SetBkColor( hdc, oldBk );
635 SetTextColor( hdc, oldText );
637 if (!ignoreFocus && (descr->focus_item == index) &&
639 (descr->in_focus)) DrawFocusRect( hdc, rect );
644 /***********************************************************************
647 * Change the redraw flag.
649 static void LISTBOX_SetRedraw( HWND hwnd, LB_DESCR *descr, BOOL on )
653 if (!(descr->style & LBS_NOREDRAW)) return;
654 descr->style &= ~LBS_NOREDRAW;
655 if (descr->style & LBS_DISPLAYCHANGED)
656 { /* page was changed while setredraw false, refresh automatically */
657 InvalidateRect(hwnd, NULL, TRUE);
658 if ((descr->top_item + descr->page_size) > descr->nb_items)
659 { /* reset top of page if less than number of items/page */
660 descr->top_item = descr->nb_items - descr->page_size;
661 if (descr->top_item < 0) descr->top_item = 0;
663 descr->style &= ~LBS_DISPLAYCHANGED;
665 LISTBOX_UpdateScroll( hwnd, descr );
667 else descr->style |= LBS_NOREDRAW;
671 /***********************************************************************
672 * LISTBOX_RepaintItem
674 * Repaint a single item synchronously.
676 static void LISTBOX_RepaintItem( HWND hwnd, LB_DESCR *descr, INT index,
682 HBRUSH hbrush, oldBrush = 0;
684 /* Do not repaint the item if the item is not visible */
685 if (!IsWindowVisible(hwnd)) return;
686 if (descr->style & LBS_NOREDRAW)
688 descr->style |= LBS_DISPLAYCHANGED;
691 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
692 if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE ))) return;
693 if (descr->font) oldFont = SelectObject( hdc, descr->font );
694 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
695 (WPARAM)hdc, (LPARAM)hwnd );
696 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
697 if (!IsWindowEnabled(hwnd))
698 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
699 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
700 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, index, action, FALSE );
701 if (oldFont) SelectObject( hdc, oldFont );
702 if (oldBrush) SelectObject( hdc, oldBrush );
703 ReleaseDC( hwnd, hdc );
707 /***********************************************************************
708 * LISTBOX_InitStorage
710 static LRESULT LISTBOX_InitStorage( HWND hwnd, LB_DESCR *descr, INT nb_items )
714 nb_items += LB_ARRAY_GRANULARITY - 1;
715 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
717 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
718 if (!(item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
719 nb_items * sizeof(LB_ITEMDATA) )))
721 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
729 /***********************************************************************
730 * LISTBOX_SetTabStops
732 static BOOL LISTBOX_SetTabStops( HWND hwnd, LB_DESCR *descr, INT count,
733 LPINT tabs, BOOL short_ints )
735 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
736 if (descr->tabs) HeapFree( GetProcessHeap(), 0, descr->tabs );
737 if (!(descr->nb_tabs = count))
742 /* FIXME: count = 1 */
743 if (!(descr->tabs = (INT *)HeapAlloc( GetProcessHeap(), 0,
744 descr->nb_tabs * sizeof(INT) )))
749 LPINT16 p = (LPINT16)tabs;
751 TRACE("[%04x]: settabstops ", hwnd );
752 for (i = 0; i < descr->nb_tabs; i++) {
753 descr->tabs[i] = *p++<<1; /* FIXME */
754 if (TRACE_ON(listbox)) DPRINTF("%hd ", descr->tabs[i]);
756 if (TRACE_ON(listbox)) DPRINTF("\n");
758 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
759 /* FIXME: repaint the window? */
764 /***********************************************************************
767 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPARAM lParam, BOOL unicode )
769 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
770 if (HAS_STRINGS(descr))
773 return strlenW(descr->items[index].str);
775 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
779 LPWSTR buffer = (LPWSTR)lParam;
780 strcpyW( buffer, descr->items[index].str );
781 return strlenW(buffer);
785 LPSTR buffer = (LPSTR)lParam;
786 return WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, buffer, 0x7FFFFFFF, NULL, NULL) - 1;
790 *((LPDWORD)lParam)=*(LPDWORD)(&descr->items[index].data);
791 return sizeof(DWORD);
796 /***********************************************************************
797 * LISTBOX_FindStringPos
799 * Find the nearest string located before a given string in sort order.
800 * If 'exact' is TRUE, return an error if we don't get an exact match.
802 static INT LISTBOX_FindStringPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str,
805 INT index, min, max, res = -1;
807 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
809 max = descr->nb_items;
812 index = (min + max) / 2;
813 if (HAS_STRINGS(descr))
814 res = lstrcmpiW( descr->items[index].str, str );
817 COMPAREITEMSTRUCT cis;
818 UINT id = GetWindowLongA( hwnd, GWL_ID );
820 cis.CtlType = ODT_LISTBOX;
824 cis.itemData1 = descr->items[index].data;
826 cis.itemData2 = (DWORD)str;
827 cis.dwLocaleId = descr->locale;
828 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
830 if (!res) return index;
831 if (res > 0) max = index;
832 else min = index + 1;
834 return exact ? -1 : max;
838 /***********************************************************************
839 * LISTBOX_FindFileStrPos
841 * Find the nearest string located before a given string in directory
842 * sort order (i.e. first files, then directories, then drives).
844 static INT LISTBOX_FindFileStrPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str )
846 INT min, max, res = -1;
848 if (!HAS_STRINGS(descr))
849 return LISTBOX_FindStringPos( hwnd, descr, str, FALSE );
851 max = descr->nb_items;
854 INT index = (min + max) / 2;
855 LPCWSTR p = descr->items[index].str;
856 if (*p == '[') /* drive or directory */
858 if (*str != '[') res = -1;
859 else if (p[1] == '-') /* drive */
861 if (str[1] == '-') res = str[2] - p[2];
866 if (str[1] == '-') res = 1;
867 else res = lstrcmpiW( str, p );
872 if (*str == '[') res = 1;
873 else res = lstrcmpiW( str, p );
875 if (!res) return index;
876 if (res < 0) max = index;
877 else min = index + 1;
883 /***********************************************************************
886 * Find the item beginning with a given string.
888 static INT LISTBOX_FindString( HWND hwnd, LB_DESCR *descr, INT start,
889 LPCWSTR str, BOOL exact )
894 if (start >= descr->nb_items) start = -1;
895 item = descr->items + start + 1;
896 if (HAS_STRINGS(descr))
898 if (!str || ! str[0] ) return LB_ERR;
901 for (i = start + 1; i < descr->nb_items; i++, item++)
902 if (!lstrcmpiW( str, item->str )) return i;
903 for (i = 0, item = descr->items; i <= start; i++, item++)
904 if (!lstrcmpiW( str, item->str )) return i;
908 /* Special case for drives and directories: ignore prefix */
909 #define CHECK_DRIVE(item) \
910 if ((item)->str[0] == '[') \
912 if (!strncmpiW( str, (item)->str+1, len )) return i; \
913 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
917 INT len = strlenW(str);
918 for (i = start + 1; i < descr->nb_items; i++, item++)
920 if (!strncmpiW( str, item->str, len )) return i;
923 for (i = 0, item = descr->items; i <= start; i++, item++)
925 if (!strncmpiW( str, item->str, len )) return i;
933 if (exact && (descr->style & LBS_SORT))
934 /* If sorted, use a WM_COMPAREITEM binary search */
935 return LISTBOX_FindStringPos( hwnd, descr, str, TRUE );
937 /* Otherwise use a linear search */
938 for (i = start + 1; i < descr->nb_items; i++, item++)
939 if (item->data == (DWORD)str) return i;
940 for (i = 0, item = descr->items; i <= start; i++, item++)
941 if (item->data == (DWORD)str) return i;
947 /***********************************************************************
948 * LISTBOX_GetSelCount
950 static LRESULT LISTBOX_GetSelCount( LB_DESCR *descr )
953 LB_ITEMDATA *item = descr->items;
955 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
956 for (i = count = 0; i < descr->nb_items; i++, item++)
957 if (item->selected) count++;
962 /***********************************************************************
963 * LISTBOX_GetSelItems16
965 static LRESULT LISTBOX_GetSelItems16( LB_DESCR *descr, INT16 max, LPINT16 array )
968 LB_ITEMDATA *item = descr->items;
970 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
971 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
972 if (item->selected) array[count++] = (INT16)i;
977 /***********************************************************************
978 * LISTBOX_GetSelItems
980 static LRESULT LISTBOX_GetSelItems( LB_DESCR *descr, INT max, LPINT array )
983 LB_ITEMDATA *item = descr->items;
985 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
986 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
987 if (item->selected) array[count++] = i;
992 /***********************************************************************
995 static LRESULT LISTBOX_Paint( HWND hwnd, LB_DESCR *descr, HDC hdc )
997 INT i, col_pos = descr->page_size - 1;
999 RECT focusRect = {-1, -1, -1, -1};
1001 HBRUSH hbrush, oldBrush = 0;
1003 if (descr->style & LBS_NOREDRAW) return 0;
1005 SetRect( &rect, 0, 0, descr->width, descr->height );
1006 if (descr->style & LBS_MULTICOLUMN)
1007 rect.right = rect.left + descr->column_width;
1008 else if (descr->horz_pos)
1010 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
1011 rect.right += descr->horz_pos;
1014 if (descr->font) oldFont = SelectObject( hdc, descr->font );
1015 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1016 (WPARAM)hdc, (LPARAM)hwnd );
1017 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1018 if (!IsWindowEnabled(hwnd)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1020 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1023 /* Special case for empty listbox: paint focus rect */
1024 rect.bottom = rect.top + descr->item_height;
1025 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, descr->focus_item,
1027 rect.top = rect.bottom;
1030 /* Paint all the item, regarding the selection
1031 Focus state will be painted after */
1033 for (i = descr->top_item; i < descr->nb_items; i++)
1035 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1036 rect.bottom = rect.top + descr->item_height;
1038 rect.bottom = rect.top + descr->items[i].height;
1040 if (i == descr->focus_item)
1042 /* keep the focus rect, to paint the focus item after */
1043 focusRect.left = rect.left;
1044 focusRect.right = rect.right;
1045 focusRect.top = rect.top;
1046 focusRect.bottom = rect.bottom;
1048 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1049 rect.top = rect.bottom;
1051 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1053 if (!IS_OWNERDRAW(descr))
1055 /* Clear the bottom of the column */
1056 if (rect.top < descr->height)
1058 rect.bottom = descr->height;
1059 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1060 &rect, NULL, 0, NULL );
1064 /* Go to the next column */
1065 rect.left += descr->column_width;
1066 rect.right += descr->column_width;
1068 col_pos = descr->page_size - 1;
1073 if (rect.top >= descr->height) break;
1077 /* Paint the focus item now */
1078 if (focusRect.top != focusRect.bottom && descr->caret_on)
1079 LISTBOX_PaintItem( hwnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1081 if (!IS_OWNERDRAW(descr))
1083 /* Clear the remainder of the client area */
1084 if (rect.top < descr->height)
1086 rect.bottom = descr->height;
1087 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1088 &rect, NULL, 0, NULL );
1090 if (rect.right < descr->width)
1092 rect.left = rect.right;
1093 rect.right = descr->width;
1095 rect.bottom = descr->height;
1096 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1097 &rect, NULL, 0, NULL );
1100 if (oldFont) SelectObject( hdc, oldFont );
1101 if (oldBrush) SelectObject( hdc, oldBrush );
1106 /***********************************************************************
1107 * LISTBOX_InvalidateItems
1109 * Invalidate all items from a given item. If the specified item is not
1110 * visible, nothing happens.
1112 static void LISTBOX_InvalidateItems( HWND hwnd, LB_DESCR *descr, INT index )
1116 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1118 if (descr->style & LBS_NOREDRAW)
1120 descr->style |= LBS_DISPLAYCHANGED;
1123 rect.bottom = descr->height;
1124 InvalidateRect( hwnd, &rect, TRUE );
1125 if (descr->style & LBS_MULTICOLUMN)
1127 /* Repaint the other columns */
1128 rect.left = rect.right;
1129 rect.right = descr->width;
1131 InvalidateRect( hwnd, &rect, TRUE );
1137 /***********************************************************************
1138 * LISTBOX_GetItemHeight
1140 static LRESULT LISTBOX_GetItemHeight( LB_DESCR *descr, INT index )
1142 if (descr->style & LBS_OWNERDRAWVARIABLE)
1144 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1145 return descr->items[index].height;
1147 else return descr->item_height;
1151 /***********************************************************************
1152 * LISTBOX_SetItemHeight
1154 static LRESULT LISTBOX_SetItemHeight( HWND hwnd, LB_DESCR *descr, INT index,
1155 INT height, BOOL repaint )
1157 if (!height) height = 1;
1159 if (descr->style & LBS_OWNERDRAWVARIABLE)
1161 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1162 TRACE("[%04x]: item %d height = %d\n", hwnd, index, height );
1163 descr->items[index].height = height;
1164 LISTBOX_UpdateScroll( hwnd, descr );
1166 LISTBOX_InvalidateItems( hwnd, descr, index );
1168 else if (height != descr->item_height)
1170 TRACE("[%04x]: new height = %d\n", hwnd, height );
1171 descr->item_height = height;
1172 LISTBOX_UpdatePage( hwnd, descr );
1173 LISTBOX_UpdateScroll( hwnd, descr );
1175 InvalidateRect( hwnd, 0, TRUE );
1181 /***********************************************************************
1182 * LISTBOX_SetHorizontalPos
1184 static void LISTBOX_SetHorizontalPos( HWND hwnd, LB_DESCR *descr, INT pos )
1188 if (pos > descr->horz_extent - descr->width)
1189 pos = descr->horz_extent - descr->width;
1190 if (pos < 0) pos = 0;
1191 if (!(diff = descr->horz_pos - pos)) return;
1192 TRACE("[%04x]: new horz pos = %d\n", hwnd, pos );
1193 descr->horz_pos = pos;
1194 LISTBOX_UpdateScroll( hwnd, descr );
1195 if (abs(diff) < descr->width)
1196 ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
1197 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1199 InvalidateRect( hwnd, NULL, TRUE );
1203 /***********************************************************************
1204 * LISTBOX_SetHorizontalExtent
1206 static LRESULT LISTBOX_SetHorizontalExtent( HWND hwnd, LB_DESCR *descr,
1209 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1211 if (extent <= 0) extent = 1;
1212 if (extent == descr->horz_extent) return LB_OKAY;
1213 TRACE("[%04x]: new horz extent = %d\n", hwnd, extent );
1214 descr->horz_extent = extent;
1215 if (descr->horz_pos > extent - descr->width)
1216 LISTBOX_SetHorizontalPos( hwnd, descr, extent - descr->width );
1218 LISTBOX_UpdateScroll( hwnd, descr );
1223 /***********************************************************************
1224 * LISTBOX_SetColumnWidth
1226 static LRESULT LISTBOX_SetColumnWidth( HWND hwnd, LB_DESCR *descr, INT width)
1228 if (width == descr->column_width) return LB_OKAY;
1229 TRACE("[%04x]: new column width = %d\n", hwnd, width );
1230 descr->column_width = width;
1231 LISTBOX_UpdatePage( hwnd, descr );
1236 /***********************************************************************
1239 * Returns the item height.
1241 static INT LISTBOX_SetFont( HWND hwnd, LB_DESCR *descr, HFONT font )
1249 if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE )))
1251 ERR("unable to get DC.\n" );
1254 if (font) oldFont = SelectObject( hdc, font );
1255 GetTextMetricsW( hdc, &tm );
1256 if (oldFont) SelectObject( hdc, oldFont );
1257 ReleaseDC( hwnd, hdc );
1258 if (!IS_OWNERDRAW(descr))
1259 LISTBOX_SetItemHeight( hwnd, descr, 0, tm.tmHeight, FALSE );
1260 return tm.tmHeight ;
1264 /***********************************************************************
1265 * LISTBOX_MakeItemVisible
1267 * Make sure that a given item is partially or fully visible.
1269 static void LISTBOX_MakeItemVisible( HWND hwnd, LB_DESCR *descr, INT index,
1274 if (index <= descr->top_item) top = index;
1275 else if (descr->style & LBS_MULTICOLUMN)
1277 INT cols = descr->width;
1278 if (!fully) cols += descr->column_width - 1;
1279 if (cols >= descr->column_width) cols /= descr->column_width;
1281 if (index < descr->top_item + (descr->page_size * cols)) return;
1282 top = index - descr->page_size * (cols - 1);
1284 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1286 INT height = fully ? descr->items[index].height : 1;
1287 for (top = index; top > descr->top_item; top--)
1288 if ((height += descr->items[top-1].height) > descr->height) break;
1292 if (index < descr->top_item + descr->page_size) return;
1293 if (!fully && (index == descr->top_item + descr->page_size) &&
1294 (descr->height > (descr->page_size * descr->item_height))) return;
1295 top = index - descr->page_size + 1;
1297 LISTBOX_SetTopItem( hwnd, descr, top, TRUE );
1300 /***********************************************************************
1301 * LISTBOX_SetCaretIndex
1304 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1307 static LRESULT LISTBOX_SetCaretIndex( HWND hwnd, LB_DESCR *descr, INT index,
1308 BOOL fully_visible )
1310 INT oldfocus = descr->focus_item;
1312 if (descr->style & LBS_NOSEL) return LB_ERR;
1313 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1314 if (index == oldfocus) return LB_OKAY;
1315 descr->focus_item = index;
1316 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1317 LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
1319 LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
1320 if (descr->caret_on && (descr->in_focus))
1321 LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
1327 /***********************************************************************
1328 * LISTBOX_SelectItemRange
1330 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1332 static LRESULT LISTBOX_SelectItemRange( HWND hwnd, LB_DESCR *descr, INT first,
1337 /* A few sanity checks */
1339 if (descr->style & LBS_NOSEL) return LB_ERR;
1340 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1341 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1342 if (last == -1) last = descr->nb_items - 1;
1343 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1344 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1345 /* selected_item reflects last selected/unselected item on multiple sel */
1346 descr->selected_item = last;
1348 if (on) /* Turn selection on */
1350 for (i = first; i <= last; i++)
1352 if (descr->items[i].selected) continue;
1353 descr->items[i].selected = TRUE;
1354 LISTBOX_RepaintItem( hwnd, descr, i, ODA_SELECT );
1356 LISTBOX_SetCaretIndex( hwnd, descr, last, TRUE );
1358 else /* Turn selection off */
1360 for (i = first; i <= last; i++)
1362 if (!descr->items[i].selected) continue;
1363 descr->items[i].selected = FALSE;
1364 LISTBOX_RepaintItem( hwnd, descr, i, ODA_SELECT );
1370 /***********************************************************************
1371 * LISTBOX_SetSelection
1373 static LRESULT LISTBOX_SetSelection( HWND hwnd, LB_DESCR *descr, INT index,
1374 BOOL on, BOOL send_notify )
1376 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1378 if (descr->style & LBS_NOSEL) return LB_ERR;
1379 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1380 if (descr->style & LBS_MULTIPLESEL)
1382 if (index == -1) /* Select all items */
1383 return LISTBOX_SelectItemRange( hwnd, descr, 0, -1, on );
1384 else /* Only one item */
1385 return LISTBOX_SelectItemRange( hwnd, descr, index, index, on );
1389 INT oldsel = descr->selected_item;
1390 if (index == oldsel) return LB_OKAY;
1391 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1392 if (index != -1) descr->items[index].selected = TRUE;
1393 descr->selected_item = index;
1394 if (oldsel != -1) LISTBOX_RepaintItem( hwnd, descr, oldsel, ODA_SELECT );
1395 if (index != -1) LISTBOX_RepaintItem( hwnd, descr, index, ODA_SELECT );
1396 if (send_notify && descr->nb_items) SEND_NOTIFICATION( hwnd, descr,
1397 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1399 if( descr->lphc ) /* set selection change flag for parent combo */
1400 descr->lphc->wState |= CBF_SELCHANGE;
1406 /***********************************************************************
1409 * Change the caret position and extend the selection to the new caret.
1411 static void LISTBOX_MoveCaret( HWND hwnd, LB_DESCR *descr, INT index,
1412 BOOL fully_visible )
1414 INT oldfocus = descr->focus_item;
1416 if ((index < 0) || (index >= descr->nb_items))
1419 /* Important, repaint needs to be done in this order if
1420 you want to mimic Windows behavior:
1421 1. Remove the focus and paint the item
1422 2. Remove the selection and paint the item(s)
1423 3. Set the selection and repaint the item(s)
1424 4. Set the focus to 'index' and repaint the item */
1426 /* 1. remove the focus and repaint the item */
1427 descr->focus_item = -1;
1428 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1429 LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
1431 /* 2. then turn off the previous selection */
1432 /* 3. repaint the new selected item */
1433 if (descr->style & LBS_EXTENDEDSEL)
1435 if (descr->anchor_item != -1)
1437 INT first = min( index, descr->anchor_item );
1438 INT last = max( index, descr->anchor_item );
1440 LISTBOX_SelectItemRange( hwnd, descr, 0, first - 1, FALSE );
1441 LISTBOX_SelectItemRange( hwnd, descr, last + 1, -1, FALSE );
1442 LISTBOX_SelectItemRange( hwnd, descr, first, last, TRUE );
1445 else if (!(descr->style & LBS_MULTIPLESEL))
1447 /* Set selection to new caret item */
1448 LISTBOX_SetSelection( hwnd, descr, index, TRUE, FALSE );
1451 /* 4. repaint the new item with the focus */
1452 descr->focus_item = index;
1453 LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
1454 if (descr->caret_on && (descr->in_focus))
1455 LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
1459 /***********************************************************************
1460 * LISTBOX_InsertItem
1462 static LRESULT LISTBOX_InsertItem( HWND hwnd, LB_DESCR *descr, INT index,
1463 LPWSTR str, DWORD data )
1467 INT oldfocus = descr->focus_item;
1469 if (index == -1) index = descr->nb_items;
1470 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1471 if (!descr->items) max_items = 0;
1472 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1473 if (descr->nb_items == max_items)
1475 /* We need to grow the array */
1476 max_items += LB_ARRAY_GRANULARITY;
1477 if (!(item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1478 max_items * sizeof(LB_ITEMDATA) )))
1480 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
1483 descr->items = item;
1486 /* Insert the item structure */
1488 item = &descr->items[index];
1489 if (index < descr->nb_items)
1490 RtlMoveMemory( item + 1, item,
1491 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1495 item->selected = FALSE;
1498 /* Get item height */
1500 if (descr->style & LBS_OWNERDRAWVARIABLE)
1502 MEASUREITEMSTRUCT mis;
1503 UINT id = GetWindowLongA( hwnd, GWL_ID );
1505 mis.CtlType = ODT_LISTBOX;
1508 mis.itemData = descr->items[index].data;
1509 mis.itemHeight = descr->item_height;
1510 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1511 item->height = mis.itemHeight ? mis.itemHeight : 1;
1512 TRACE("[%04x]: measure item %d (%s) = %d\n",
1513 hwnd, index, str ? debugstr_w(str) : "", item->height );
1516 /* Repaint the items */
1518 LISTBOX_UpdateScroll( hwnd, descr );
1519 LISTBOX_InvalidateItems( hwnd, descr, index );
1521 /* Move selection and focused item */
1522 /* If listbox was empty, set focus to the first item */
1523 if (descr->nb_items == 1)
1524 LISTBOX_SetCaretIndex( hwnd, descr, 0, FALSE );
1525 /* single select don't change selection index in win31 */
1526 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1528 descr->selected_item++;
1529 LISTBOX_SetSelection( hwnd, descr, descr->selected_item-1, TRUE, FALSE );
1533 if (index <= descr->selected_item)
1535 descr->selected_item++;
1536 descr->focus_item = oldfocus; /* focus not changed */
1543 /***********************************************************************
1544 * LISTBOX_InsertString
1546 static LRESULT LISTBOX_InsertString( HWND hwnd, LB_DESCR *descr, INT index,
1549 LPWSTR new_str = NULL;
1553 if (HAS_STRINGS(descr))
1555 static const WCHAR empty_stringW[] = { 0 };
1556 if (!str) str = empty_stringW;
1557 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1559 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
1562 strcpyW(new_str, str);
1564 else data = (DWORD)str;
1566 if (index == -1) index = descr->nb_items;
1567 if ((ret = LISTBOX_InsertItem( hwnd, descr, index, new_str, data )) != 0)
1569 if (new_str) HeapFree( GetProcessHeap(), 0, new_str );
1573 TRACE("[%04x]: added item %d %s\n",
1574 hwnd, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1579 /***********************************************************************
1580 * LISTBOX_DeleteItem
1582 * Delete the content of an item. 'index' must be a valid index.
1584 static void LISTBOX_DeleteItem( HWND hwnd, LB_DESCR *descr, INT index )
1586 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1587 * while Win95 sends it for all items with user data.
1588 * It's probably better to send it too often than not
1589 * often enough, so this is what we do here.
1591 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1593 DELETEITEMSTRUCT dis;
1594 UINT id = GetWindowLongA( hwnd, GWL_ID );
1596 dis.CtlType = ODT_LISTBOX;
1599 dis.hwndItem = hwnd;
1600 dis.itemData = descr->items[index].data;
1601 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1603 if (HAS_STRINGS(descr) && descr->items[index].str)
1604 HeapFree( GetProcessHeap(), 0, descr->items[index].str );
1608 /***********************************************************************
1609 * LISTBOX_RemoveItem
1611 * Remove an item from the listbox and delete its content.
1613 static LRESULT LISTBOX_RemoveItem( HWND hwnd, LB_DESCR *descr, INT index )
1618 if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1619 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1621 /* We need to invalidate the original rect instead of the updated one. */
1622 LISTBOX_InvalidateItems( hwnd, descr, index );
1624 LISTBOX_DeleteItem( hwnd, descr, index );
1626 /* Remove the item */
1628 item = &descr->items[index];
1629 if (index < descr->nb_items-1)
1630 RtlMoveMemory( item, item + 1,
1631 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1633 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1635 /* Shrink the item array if possible */
1637 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1638 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1640 max_items -= LB_ARRAY_GRANULARITY;
1641 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1642 max_items * sizeof(LB_ITEMDATA) );
1643 if (item) descr->items = item;
1645 /* Repaint the items */
1647 LISTBOX_UpdateScroll( hwnd, descr );
1648 /* if we removed the scrollbar, reset the top of the list
1649 (correct for owner-drawn ???) */
1650 if (descr->nb_items == descr->page_size)
1651 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1653 /* Move selection and focused item */
1654 if (!IS_MULTISELECT(descr))
1656 if (index == descr->selected_item)
1657 descr->selected_item = -1;
1658 else if (index < descr->selected_item)
1660 descr->selected_item--;
1661 if (ISWIN31) /* win 31 do not change the selected item number */
1662 LISTBOX_SetSelection( hwnd, descr, descr->selected_item + 1, TRUE, FALSE);
1666 if (descr->focus_item >= descr->nb_items)
1668 descr->focus_item = descr->nb_items - 1;
1669 if (descr->focus_item < 0) descr->focus_item = 0;
1675 /***********************************************************************
1676 * LISTBOX_ResetContent
1678 static void LISTBOX_ResetContent( HWND hwnd, LB_DESCR *descr )
1682 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( hwnd, descr, i );
1683 if (descr->items) HeapFree( GetProcessHeap(), 0, descr->items );
1684 descr->nb_items = 0;
1685 descr->top_item = 0;
1686 descr->selected_item = -1;
1687 descr->focus_item = 0;
1688 descr->anchor_item = -1;
1689 descr->items = NULL;
1693 /***********************************************************************
1696 static LRESULT LISTBOX_SetCount( HWND hwnd, LB_DESCR *descr, INT count )
1700 if (HAS_STRINGS(descr)) return LB_ERR;
1701 /* FIXME: this is far from optimal... */
1702 if (count > descr->nb_items)
1704 while (count > descr->nb_items)
1705 if ((ret = LISTBOX_InsertString( hwnd, descr, -1, 0 )) < 0)
1708 else if (count < descr->nb_items)
1710 while (count < descr->nb_items)
1711 if ((ret = LISTBOX_RemoveItem( hwnd, descr, -1 )) < 0)
1718 /***********************************************************************
1721 static LRESULT LISTBOX_Directory( HWND hwnd, LB_DESCR *descr, UINT attrib,
1722 LPCWSTR filespec, BOOL long_names )
1725 LRESULT ret = LB_OKAY;
1726 WIN32_FIND_DATAW entry;
1729 /* don't scan directory if we just want drives exclusively */
1730 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1731 /* scan directory */
1732 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1734 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1741 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1743 static const WCHAR bracketW[] = { ']',0 };
1744 static const WCHAR dotW[] = { '.',0 };
1745 if (!(attrib & DDL_DIRECTORY) ||
1746 !strcmpW( entry.cAlternateFileName, dotW )) continue;
1748 if (long_names) strcpyW( buffer + 1, entry.cFileName );
1749 else strcpyW( buffer + 1, entry.cAlternateFileName );
1750 strcatW(buffer, bracketW);
1752 else /* not a directory */
1754 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1755 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1757 if ((attrib & DDL_EXCLUSIVE) &&
1758 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1761 if (long_names) strcpyW( buffer, entry.cFileName );
1762 else strcpyW( buffer, entry.cAlternateFileName );
1764 if (!long_names) CharLowerW( buffer );
1765 pos = LISTBOX_FindFileStrPos( hwnd, descr, buffer );
1766 if ((ret = LISTBOX_InsertString( hwnd, descr, pos, buffer )) < 0)
1768 } while (FindNextFileW( handle, &entry ));
1769 FindClose( handle );
1774 if ((ret >= 0) && (attrib & DDL_DRIVES))
1776 WCHAR buffer[] = {'[','-','a','-',']',0};
1777 WCHAR root[] = {'A',':','\\',0};
1779 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1781 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1782 if ((ret = LISTBOX_InsertString( hwnd, descr, -1, buffer )) < 0)
1790 /***********************************************************************
1791 * LISTBOX_HandleVScroll
1793 static LRESULT LISTBOX_HandleVScroll( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1797 if (descr->style & LBS_MULTICOLUMN) return 0;
1798 switch(LOWORD(wParam))
1801 LISTBOX_SetTopItem( hwnd, descr, descr->top_item - 1, TRUE );
1804 LISTBOX_SetTopItem( hwnd, descr, descr->top_item + 1, TRUE );
1807 LISTBOX_SetTopItem( hwnd, descr, descr->top_item -
1808 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1811 LISTBOX_SetTopItem( hwnd, descr, descr->top_item +
1812 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1814 case SB_THUMBPOSITION:
1815 LISTBOX_SetTopItem( hwnd, descr, HIWORD(wParam), TRUE );
1818 info.cbSize = sizeof(info);
1819 info.fMask = SIF_TRACKPOS;
1820 GetScrollInfo( hwnd, SB_VERT, &info );
1821 LISTBOX_SetTopItem( hwnd, descr, info.nTrackPos, TRUE );
1824 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1827 LISTBOX_SetTopItem( hwnd, descr, descr->nb_items, TRUE );
1834 /***********************************************************************
1835 * LISTBOX_HandleHScroll
1837 static LRESULT LISTBOX_HandleHScroll( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1842 if (descr->style & LBS_MULTICOLUMN)
1844 switch(LOWORD(wParam))
1847 LISTBOX_SetTopItem( hwnd, descr, descr->top_item-descr->page_size,
1851 LISTBOX_SetTopItem( hwnd, descr, descr->top_item+descr->page_size,
1855 page = descr->width / descr->column_width;
1856 if (page < 1) page = 1;
1857 LISTBOX_SetTopItem( hwnd, descr,
1858 descr->top_item - page * descr->page_size, TRUE );
1861 page = descr->width / descr->column_width;
1862 if (page < 1) page = 1;
1863 LISTBOX_SetTopItem( hwnd, descr,
1864 descr->top_item + page * descr->page_size, TRUE );
1866 case SB_THUMBPOSITION:
1867 LISTBOX_SetTopItem( hwnd, descr, HIWORD(wParam)*descr->page_size,
1871 info.cbSize = sizeof(info);
1872 info.fMask = SIF_TRACKPOS;
1873 GetScrollInfo( hwnd, SB_VERT, &info );
1874 LISTBOX_SetTopItem( hwnd, descr, info.nTrackPos*descr->page_size,
1878 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1881 LISTBOX_SetTopItem( hwnd, descr, descr->nb_items, TRUE );
1885 else if (descr->horz_extent)
1887 switch(LOWORD(wParam))
1890 LISTBOX_SetHorizontalPos( hwnd, descr, descr->horz_pos - 1 );
1893 LISTBOX_SetHorizontalPos( hwnd, descr, descr->horz_pos + 1 );
1896 LISTBOX_SetHorizontalPos( hwnd, descr,
1897 descr->horz_pos - descr->width );
1900 LISTBOX_SetHorizontalPos( hwnd, descr,
1901 descr->horz_pos + descr->width );
1903 case SB_THUMBPOSITION:
1904 LISTBOX_SetHorizontalPos( hwnd, descr, HIWORD(wParam) );
1907 info.cbSize = sizeof(info);
1908 info.fMask = SIF_TRACKPOS;
1909 GetScrollInfo( hwnd, SB_HORZ, &info );
1910 LISTBOX_SetHorizontalPos( hwnd, descr, info.nTrackPos );
1913 LISTBOX_SetHorizontalPos( hwnd, descr, 0 );
1916 LISTBOX_SetHorizontalPos( hwnd, descr,
1917 descr->horz_extent - descr->width );
1924 static LRESULT LISTBOX_HandleMouseWheel(HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1926 short gcWheelDelta = 0;
1927 UINT pulScrollLines = 3;
1929 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1931 gcWheelDelta -= (short) HIWORD(wParam);
1933 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1935 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1936 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1937 LISTBOX_SetTopItem( hwnd, descr, descr->top_item + cLineScroll, TRUE );
1942 /***********************************************************************
1943 * LISTBOX_HandleLButtonDown
1945 static LRESULT LISTBOX_HandleLButtonDown( HWND hwnd, LB_DESCR *descr,
1946 WPARAM wParam, INT x, INT y )
1948 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
1949 TRACE("[%04x]: lbuttondown %d,%d item %d\n", hwnd, x, y, index );
1950 if (!descr->caret_on && (descr->in_focus)) return 0;
1952 if (!descr->in_focus)
1954 if( !descr->lphc ) SetFocus( hwnd );
1955 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
1958 if (index == -1) return 0;
1960 if (descr->style & LBS_EXTENDEDSEL)
1962 /* we should perhaps make sure that all items are deselected
1963 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1964 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1965 LISTBOX_SetSelection( hwnd, descr, -1, FALSE, FALSE);
1968 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1969 if (wParam & MK_CONTROL)
1971 LISTBOX_SetCaretIndex( hwnd, descr, index, FALSE );
1972 LISTBOX_SetSelection( hwnd, descr, index,
1973 !descr->items[index].selected,
1974 (descr->style & LBS_NOTIFY) != 0);
1976 else LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
1980 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
1981 LISTBOX_SetSelection( hwnd, descr, index,
1982 (!(descr->style & LBS_MULTIPLESEL) ||
1983 !descr->items[index].selected),
1984 (descr->style & LBS_NOTIFY) != 0 );
1987 descr->captured = TRUE;
1992 if (descr->style & LBS_NOTIFY )
1993 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
1994 MAKELPARAM( x, y ) );
1995 if (GetWindowLongA( hwnd, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
2002 if (DragDetect( hwnd, pt ))
2003 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
2010 /*************************************************************************
2011 * LISTBOX_HandleLButtonDownCombo [Internal]
2013 * Process LButtonDown message for the ComboListBox
2016 * pWnd [I] The windows internal structure
2017 * pDescr [I] The ListBox internal structure
2018 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2019 * x [I] X Mouse Coordinate
2020 * y [I] Y Mouse Coordinate
2023 * 0 since we are processing the WM_LBUTTONDOWN Message
2026 * This function is only to be used when a ListBox is a ComboListBox
2029 static LRESULT LISTBOX_HandleLButtonDownCombo( HWND hwnd, LB_DESCR *pDescr,
2030 UINT msg, WPARAM wParam, INT x, INT y)
2032 RECT clientRect, screenRect;
2038 GetClientRect(hwnd, &clientRect);
2040 if(PtInRect(&clientRect, mousePos))
2042 /* MousePos is in client, resume normal processing */
2043 if (msg == WM_LBUTTONDOWN)
2045 pDescr->lphc->droppedIndex = pDescr->nb_items ? pDescr->selected_item : -1;
2046 return LISTBOX_HandleLButtonDown( hwnd, pDescr, wParam, x, y);
2048 else if (pDescr->style & LBS_NOTIFY)
2049 SEND_NOTIFICATION( hwnd, pDescr, LBN_DBLCLK );
2054 POINT screenMousePos;
2055 HWND hWndOldCapture;
2057 /* Check the Non-Client Area */
2058 screenMousePos = mousePos;
2059 hWndOldCapture = GetCapture();
2061 GetWindowRect(hwnd, &screenRect);
2062 ClientToScreen(hwnd, &screenMousePos);
2064 if(!PtInRect(&screenRect, screenMousePos))
2066 LISTBOX_SetCaretIndex( hwnd, pDescr, pDescr->lphc->droppedIndex, FALSE );
2067 LISTBOX_SetSelection( hwnd, pDescr, pDescr->lphc->droppedIndex, FALSE, FALSE );
2068 COMBO_FlipListbox( pDescr->lphc, FALSE, FALSE );
2073 /* Check to see the NC is a scrollbar */
2075 LONG style = GetWindowLongA( hwnd, GWL_STYLE );
2076 /* Check Vertical scroll bar */
2077 if (style & WS_VSCROLL)
2079 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2080 if (PtInRect( &clientRect, mousePos ))
2082 nHitTestType = HTVSCROLL;
2085 /* Check horizontal scroll bar */
2086 if (style & WS_HSCROLL)
2088 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2089 if (PtInRect( &clientRect, mousePos ))
2091 nHitTestType = HTHSCROLL;
2094 /* Windows sends this message when a scrollbar is clicked
2097 if(nHitTestType != 0)
2099 SendMessageW(hwnd, WM_NCLBUTTONDOWN, nHitTestType,
2100 MAKELONG(screenMousePos.x, screenMousePos.y));
2102 /* Resume the Capture after scrolling is complete
2104 if(hWndOldCapture != 0)
2106 SetCapture(hWndOldCapture);
2113 /***********************************************************************
2114 * LISTBOX_HandleLButtonUp
2116 static LRESULT LISTBOX_HandleLButtonUp( HWND hwnd, LB_DESCR *descr )
2118 if (LISTBOX_Timer != LB_TIMER_NONE)
2119 KillSystemTimer( hwnd, LB_TIMER_ID );
2120 LISTBOX_Timer = LB_TIMER_NONE;
2121 if (descr->captured)
2123 descr->captured = FALSE;
2124 if (GetCapture() == hwnd) ReleaseCapture();
2125 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2126 SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2132 /***********************************************************************
2133 * LISTBOX_HandleTimer
2135 * Handle scrolling upon a timer event.
2136 * Return TRUE if scrolling should continue.
2138 static LRESULT LISTBOX_HandleTimer( HWND hwnd, LB_DESCR *descr,
2139 INT index, TIMER_DIRECTION dir )
2144 if (descr->top_item) index = descr->top_item - 1;
2148 if (descr->top_item) index -= descr->page_size;
2151 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2152 if (index == descr->focus_item) index++;
2153 if (index >= descr->nb_items) index = descr->nb_items - 1;
2155 case LB_TIMER_RIGHT:
2156 if (index + descr->page_size < descr->nb_items)
2157 index += descr->page_size;
2162 if (index == descr->focus_item) return FALSE;
2163 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
2168 /***********************************************************************
2169 * LISTBOX_HandleSystemTimer
2171 * WM_SYSTIMER handler.
2173 static LRESULT LISTBOX_HandleSystemTimer( HWND hwnd, LB_DESCR *descr )
2175 if (!LISTBOX_HandleTimer( hwnd, descr, descr->focus_item, LISTBOX_Timer ))
2177 KillSystemTimer( hwnd, LB_TIMER_ID );
2178 LISTBOX_Timer = LB_TIMER_NONE;
2184 /***********************************************************************
2185 * LISTBOX_HandleMouseMove
2187 * WM_MOUSEMOVE handler.
2189 static void LISTBOX_HandleMouseMove( HWND hwnd, LB_DESCR *descr,
2193 TIMER_DIRECTION dir = LB_TIMER_NONE;
2195 if (!descr->captured) return;
2197 if (descr->style & LBS_MULTICOLUMN)
2200 else if (y >= descr->item_height * descr->page_size)
2201 y = descr->item_height * descr->page_size - 1;
2205 dir = LB_TIMER_LEFT;
2208 else if (x >= descr->width)
2210 dir = LB_TIMER_RIGHT;
2211 x = descr->width - 1;
2216 if (y < 0) dir = LB_TIMER_UP; /* above */
2217 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2220 index = LISTBOX_GetItemFromPoint( descr, x, y );
2221 if (index == -1) index = descr->focus_item;
2222 if (!LISTBOX_HandleTimer( hwnd, descr, index, dir )) dir = LB_TIMER_NONE;
2224 /* Start/stop the system timer */
2226 if (dir != LB_TIMER_NONE)
2227 SetSystemTimer( hwnd, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2228 else if (LISTBOX_Timer != LB_TIMER_NONE)
2229 KillSystemTimer( hwnd, LB_TIMER_ID );
2230 LISTBOX_Timer = dir;
2234 /***********************************************************************
2235 * LISTBOX_HandleKeyDown
2237 static LRESULT LISTBOX_HandleKeyDown( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
2240 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2241 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2242 bForceSelection = FALSE; /* only for single select list */
2244 if (descr->style & LBS_WANTKEYBOARDINPUT)
2246 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2247 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2249 if (caret == -2) return 0;
2251 if (caret == -1) switch(wParam)
2254 if (descr->style & LBS_MULTICOLUMN)
2256 bForceSelection = FALSE;
2257 if (descr->focus_item >= descr->page_size)
2258 caret = descr->focus_item - descr->page_size;
2263 caret = descr->focus_item - 1;
2264 if (caret < 0) caret = 0;
2267 if (descr->style & LBS_MULTICOLUMN)
2269 bForceSelection = FALSE;
2270 if (descr->focus_item + descr->page_size < descr->nb_items)
2271 caret = descr->focus_item + descr->page_size;
2276 caret = descr->focus_item + 1;
2277 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2281 if (descr->style & LBS_MULTICOLUMN)
2283 INT page = descr->width / descr->column_width;
2284 if (page < 1) page = 1;
2285 caret = descr->focus_item - (page * descr->page_size) + 1;
2287 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2288 if (caret < 0) caret = 0;
2291 if (descr->style & LBS_MULTICOLUMN)
2293 INT page = descr->width / descr->column_width;
2294 if (page < 1) page = 1;
2295 caret = descr->focus_item + (page * descr->page_size) - 1;
2297 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2298 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2304 caret = descr->nb_items - 1;
2307 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2308 else if (descr->style & LBS_MULTIPLESEL)
2310 LISTBOX_SetSelection( hwnd, descr, descr->focus_item,
2311 !descr->items[descr->focus_item].selected,
2312 (descr->style & LBS_NOTIFY) != 0 );
2316 bForceSelection = FALSE;
2318 if (bForceSelection) /* focused item is used instead of key */
2319 caret = descr->focus_item;
2322 if ((descr->style & LBS_EXTENDEDSEL) &&
2323 !(GetKeyState( VK_SHIFT ) & 0x8000))
2324 descr->anchor_item = caret;
2325 LISTBOX_MoveCaret( hwnd, descr, caret, TRUE );
2326 LISTBOX_SetSelection( hwnd, descr, caret, TRUE, FALSE);
2327 if (descr->style & LBS_NOTIFY)
2331 /* make sure that combo parent doesn't hide us */
2332 descr->lphc->wState |= CBF_NOROLLUP;
2334 if (descr->nb_items) SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2341 /***********************************************************************
2342 * LISTBOX_HandleChar
2344 static LRESULT LISTBOX_HandleChar( HWND hwnd, LB_DESCR *descr, WCHAR charW )
2352 if (descr->style & LBS_WANTKEYBOARDINPUT)
2354 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2355 MAKEWPARAM(charW, descr->focus_item),
2357 if (caret == -2) return 0;
2360 caret = LISTBOX_FindString( hwnd, descr, descr->focus_item, str, FALSE);
2363 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2364 LISTBOX_SetSelection( hwnd, descr, caret, TRUE, FALSE);
2365 LISTBOX_MoveCaret( hwnd, descr, caret, TRUE );
2366 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2367 SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2373 /***********************************************************************
2376 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2379 MEASUREITEMSTRUCT mis;
2382 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2385 GetClientRect( hwnd, &rect );
2386 descr->owner = GetParent( hwnd );
2387 descr->style = GetWindowLongA( hwnd, GWL_STYLE );
2388 descr->width = rect.right - rect.left;
2389 descr->height = rect.bottom - rect.top;
2390 descr->items = NULL;
2391 descr->nb_items = 0;
2392 descr->top_item = 0;
2393 descr->selected_item = -1;
2394 descr->focus_item = 0;
2395 descr->anchor_item = -1;
2396 descr->item_height = 1;
2397 descr->page_size = 1;
2398 descr->column_width = 150;
2399 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0;
2400 descr->horz_pos = 0;
2403 descr->caret_on = lphc ? FALSE : TRUE;
2404 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2405 descr->in_focus = FALSE;
2406 descr->captured = FALSE;
2408 descr->locale = 0; /* FIXME */
2411 if (is_old_app(hwnd) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2413 /* Win95 document "List Box Differences" from MSDN:
2414 If a list box in a version 3.x application has either the
2415 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2416 horizontal and vertical scroll bars.
2418 descr->style |= WS_VSCROLL | WS_HSCROLL;
2423 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2424 hwnd, descr->owner, lphc->self );
2425 descr->owner = lphc->self;
2428 SetWindowLongA( hwnd, 0, (LONG)descr );
2430 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2432 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2433 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2434 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2435 descr->item_height = LISTBOX_SetFont( hwnd, descr, 0 );
2437 if (descr->style & LBS_OWNERDRAWFIXED)
2439 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2441 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2442 descr->item_height = lphc->fixedOwnerDrawHeight;
2446 UINT id = GetWindowLongA( hwnd, GWL_ID );
2447 mis.CtlType = ODT_LISTBOX;
2452 mis.itemHeight = descr->item_height;
2453 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2454 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2458 TRACE("owner: %04x, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2463 /***********************************************************************
2466 static BOOL LISTBOX_Destroy( HWND hwnd, LB_DESCR *descr )
2468 LISTBOX_ResetContent( hwnd, descr );
2469 SetWindowLongA( hwnd, 0, 0 );
2470 HeapFree( GetProcessHeap(), 0, descr );
2475 /***********************************************************************
2476 * ListBoxWndProc_common
2478 static LRESULT WINAPI ListBoxWndProc_common( HWND hwnd, UINT msg,
2479 WPARAM wParam, LPARAM lParam, BOOL unicode )
2484 if (!(descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 )))
2486 if (msg == WM_CREATE)
2488 if (!LISTBOX_Create( hwnd, NULL ))
2490 TRACE("creating wnd=%04x descr=%lx\n", hwnd, GetWindowLongA( hwnd, 0 ) );
2493 /* Ignore all other messages before we get a WM_CREATE */
2494 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2495 DefWindowProcA( hwnd, msg, wParam, lParam );
2498 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2499 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
2502 case LB_RESETCONTENT16:
2503 case LB_RESETCONTENT:
2504 LISTBOX_ResetContent( hwnd, descr );
2505 LISTBOX_UpdateScroll( hwnd, descr );
2506 InvalidateRect( hwnd, NULL, TRUE );
2509 case LB_ADDSTRING16:
2510 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2516 if(unicode || !HAS_STRINGS(descr))
2517 textW = (LPWSTR)lParam;
2520 LPSTR textA = (LPSTR)lParam;
2521 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2522 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2523 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2525 wParam = LISTBOX_FindStringPos( hwnd, descr, textW, FALSE );
2526 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2527 if (!unicode && HAS_STRINGS(descr))
2528 HeapFree(GetProcessHeap(), 0, textW);
2532 case LB_INSERTSTRING16:
2533 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2534 wParam = (INT)(INT16)wParam;
2536 case LB_INSERTSTRING:
2540 if(unicode || !HAS_STRINGS(descr))
2541 textW = (LPWSTR)lParam;
2544 LPSTR textA = (LPSTR)lParam;
2545 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2546 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2547 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2549 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2550 if(!unicode && HAS_STRINGS(descr))
2551 HeapFree(GetProcessHeap(), 0, textW);
2556 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2562 if(unicode || !HAS_STRINGS(descr))
2563 textW = (LPWSTR)lParam;
2566 LPSTR textA = (LPSTR)lParam;
2567 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2568 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2569 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2571 wParam = LISTBOX_FindFileStrPos( hwnd, descr, textW );
2572 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2573 if(!unicode && HAS_STRINGS(descr))
2574 HeapFree(GetProcessHeap(), 0, textW);
2578 case LB_DELETESTRING16:
2579 case LB_DELETESTRING:
2580 if (LISTBOX_RemoveItem( hwnd, descr, wParam) != LB_ERR)
2581 return descr->nb_items;
2585 case LB_GETITEMDATA16:
2586 case LB_GETITEMDATA:
2587 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2589 return descr->items[wParam].data;
2591 case LB_SETITEMDATA16:
2592 case LB_SETITEMDATA:
2593 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2595 descr->items[wParam].data = (DWORD)lParam;
2600 return descr->nb_items;
2603 lParam = (LPARAM)MapSL(lParam);
2606 return LISTBOX_GetText( descr, wParam, lParam, unicode );
2608 case LB_GETTEXTLEN16:
2611 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2613 if (!HAS_STRINGS(descr)) return sizeof(DWORD);
2614 if (unicode) return strlenW( descr->items[wParam].str );
2615 return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
2616 strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
2618 case LB_GETCURSEL16:
2620 if (descr->nb_items==0)
2622 if (!IS_MULTISELECT(descr))
2623 return descr->selected_item;
2625 if (descr->selected_item!=-1)
2626 return descr->selected_item;
2628 return descr->focus_item;
2629 /* otherwise, if the user tries to move the selection with the */
2630 /* arrow keys, we will give the application something to choke on */
2631 case LB_GETTOPINDEX16:
2632 case LB_GETTOPINDEX:
2633 return descr->top_item;
2635 case LB_GETITEMHEIGHT16:
2636 case LB_GETITEMHEIGHT:
2637 return LISTBOX_GetItemHeight( descr, wParam );
2639 case LB_SETITEMHEIGHT16:
2640 lParam = LOWORD(lParam);
2642 case LB_SETITEMHEIGHT:
2643 return LISTBOX_SetItemHeight( hwnd, descr, wParam, lParam, TRUE );
2645 case LB_ITEMFROMPOINT:
2650 pt.x = LOWORD(lParam);
2651 pt.y = HIWORD(lParam);
2654 rect.right = descr->width;
2655 rect.bottom = descr->height;
2657 return MAKELONG( LISTBOX_GetItemFromPoint(descr, pt.x, pt.y),
2658 !PtInRect( &rect, pt ) );
2661 case LB_SETCARETINDEX16:
2662 case LB_SETCARETINDEX:
2663 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2664 if (LISTBOX_SetCaretIndex( hwnd, descr, wParam, !lParam ) == LB_ERR)
2671 case LB_GETCARETINDEX16:
2672 case LB_GETCARETINDEX:
2673 return descr->focus_item;
2675 case LB_SETTOPINDEX16:
2676 case LB_SETTOPINDEX:
2677 return LISTBOX_SetTopItem( hwnd, descr, wParam, TRUE );
2679 case LB_SETCOLUMNWIDTH16:
2680 case LB_SETCOLUMNWIDTH:
2681 return LISTBOX_SetColumnWidth( hwnd, descr, wParam );
2683 case LB_GETITEMRECT16:
2686 ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2687 CONV_RECT32TO16( &rect, MapSL(lParam) );
2691 case LB_GETITEMRECT:
2692 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2694 case LB_FINDSTRING16:
2695 wParam = (INT)(INT16)wParam;
2696 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2702 if(unicode || !HAS_STRINGS(descr))
2703 textW = (LPWSTR)lParam;
2706 LPSTR textA = (LPSTR)lParam;
2707 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2708 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2709 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2711 ret = LISTBOX_FindString( hwnd, descr, wParam, textW, FALSE );
2712 if(!unicode && HAS_STRINGS(descr))
2713 HeapFree(GetProcessHeap(), 0, textW);
2717 case LB_FINDSTRINGEXACT16:
2718 wParam = (INT)(INT16)wParam;
2719 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2721 case LB_FINDSTRINGEXACT:
2725 if(unicode || !HAS_STRINGS(descr))
2726 textW = (LPWSTR)lParam;
2729 LPSTR textA = (LPSTR)lParam;
2730 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2731 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2732 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2734 ret = LISTBOX_FindString( hwnd, descr, wParam, textW, TRUE );
2735 if(!unicode && HAS_STRINGS(descr))
2736 HeapFree(GetProcessHeap(), 0, textW);
2740 case LB_SELECTSTRING16:
2741 wParam = (INT)(INT16)wParam;
2742 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2744 case LB_SELECTSTRING:
2749 if(HAS_STRINGS(descr))
2750 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2751 debugstr_a((LPSTR)lParam));
2752 if(unicode || !HAS_STRINGS(descr))
2753 textW = (LPWSTR)lParam;
2756 LPSTR textA = (LPSTR)lParam;
2757 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2758 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2759 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2761 index = LISTBOX_FindString( hwnd, descr, wParam, textW, FALSE );
2762 if(!unicode && HAS_STRINGS(descr))
2763 HeapFree(GetProcessHeap(), 0, textW);
2764 if (index != LB_ERR)
2766 LISTBOX_SetCaretIndex( hwnd, descr, index, TRUE );
2767 LISTBOX_SetSelection( hwnd, descr, index, TRUE, FALSE );
2773 wParam = (INT)(INT16)wParam;
2776 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2778 return descr->items[wParam].selected;
2781 lParam = (INT)(INT16)lParam;
2784 return LISTBOX_SetSelection( hwnd, descr, lParam, wParam, FALSE );
2786 case LB_SETCURSEL16:
2787 wParam = (INT)(INT16)wParam;
2790 if (IS_MULTISELECT(descr)) return LB_ERR;
2791 LISTBOX_SetCaretIndex( hwnd, descr, wParam, TRUE );
2792 return LISTBOX_SetSelection( hwnd, descr, wParam, TRUE, FALSE );
2794 case LB_GETSELCOUNT16:
2795 case LB_GETSELCOUNT:
2796 return LISTBOX_GetSelCount( descr );
2798 case LB_GETSELITEMS16:
2799 return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2801 case LB_GETSELITEMS:
2802 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2804 case LB_SELITEMRANGE16:
2805 case LB_SELITEMRANGE:
2806 if (LOWORD(lParam) <= HIWORD(lParam))
2807 return LISTBOX_SelectItemRange( hwnd, descr, LOWORD(lParam),
2808 HIWORD(lParam), wParam );
2810 return LISTBOX_SelectItemRange( hwnd, descr, HIWORD(lParam),
2811 LOWORD(lParam), wParam );
2813 case LB_SELITEMRANGEEX16:
2814 case LB_SELITEMRANGEEX:
2815 if ((INT)lParam >= (INT)wParam)
2816 return LISTBOX_SelectItemRange( hwnd, descr, wParam, lParam, TRUE );
2818 return LISTBOX_SelectItemRange( hwnd, descr, lParam, wParam, FALSE);
2820 case LB_GETHORIZONTALEXTENT16:
2821 case LB_GETHORIZONTALEXTENT:
2822 return descr->horz_extent;
2824 case LB_SETHORIZONTALEXTENT16:
2825 case LB_SETHORIZONTALEXTENT:
2826 return LISTBOX_SetHorizontalExtent( hwnd, descr, wParam );
2828 case LB_GETANCHORINDEX16:
2829 case LB_GETANCHORINDEX:
2830 return descr->anchor_item;
2832 case LB_SETANCHORINDEX16:
2833 wParam = (INT)(INT16)wParam;
2835 case LB_SETANCHORINDEX:
2836 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2838 descr->anchor_item = (INT)wParam;
2842 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2843 * be set automatically (this is different in Win32) */
2844 if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2845 lParam = (LPARAM)MapSL(lParam);
2852 textW = (LPWSTR)lParam;
2855 LPSTR textA = (LPSTR)lParam;
2856 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2857 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2858 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2860 ret = LISTBOX_Directory( hwnd, descr, wParam, textW, msg == LB_DIR );
2862 HeapFree(GetProcessHeap(), 0, textW);
2867 return descr->locale;
2870 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2873 case LB_INITSTORAGE:
2874 return LISTBOX_InitStorage( hwnd, descr, wParam );
2877 return LISTBOX_SetCount( hwnd, descr, (INT)wParam );
2879 case LB_SETTABSTOPS16:
2880 return LISTBOX_SetTabStops( hwnd, descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2882 case LB_SETTABSTOPS:
2883 return LISTBOX_SetTabStops( hwnd, descr, wParam, (LPINT)lParam, FALSE );
2887 if (descr->caret_on)
2889 descr->caret_on = TRUE;
2890 if ((descr->focus_item != -1) && (descr->in_focus))
2891 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2896 if (!descr->caret_on)
2898 descr->caret_on = FALSE;
2899 if ((descr->focus_item != -1) && (descr->in_focus))
2900 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2904 return LISTBOX_Destroy( hwnd, descr );
2907 InvalidateRect( hwnd, NULL, TRUE );
2911 LISTBOX_SetRedraw( hwnd, descr, wParam != 0 );
2915 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2920 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( hwnd, &ps );
2921 ret = LISTBOX_Paint( hwnd, descr, hdc );
2922 if( !wParam ) EndPaint( hwnd, &ps );
2926 LISTBOX_UpdateSize( hwnd, descr );
2929 return (LRESULT)descr->font;
2931 LISTBOX_SetFont( hwnd, descr, (HFONT)wParam );
2932 if (lParam) InvalidateRect( hwnd, 0, TRUE );
2935 descr->in_focus = TRUE;
2936 descr->caret_on = TRUE;
2937 if (descr->focus_item != -1)
2938 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2939 SEND_NOTIFICATION( hwnd, descr, LBN_SETFOCUS );
2942 descr->in_focus = FALSE;
2943 if ((descr->focus_item != -1) && descr->caret_on)
2944 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2945 SEND_NOTIFICATION( hwnd, descr, LBN_KILLFOCUS );
2948 return LISTBOX_HandleHScroll( hwnd, descr, wParam );
2950 return LISTBOX_HandleVScroll( hwnd, descr, wParam );
2952 if (wParam & (MK_SHIFT | MK_CONTROL))
2953 return DefWindowProcW( hwnd, msg, wParam, lParam );
2954 return LISTBOX_HandleMouseWheel( hwnd, descr, wParam );
2955 case WM_LBUTTONDOWN:
2956 return LISTBOX_HandleLButtonDown( hwnd, descr, wParam,
2957 (INT16)LOWORD(lParam),
2958 (INT16)HIWORD(lParam) );
2959 case WM_LBUTTONDBLCLK:
2960 if (descr->style & LBS_NOTIFY)
2961 SEND_NOTIFICATION( hwnd, descr, LBN_DBLCLK );
2964 if (GetCapture() == hwnd)
2965 LISTBOX_HandleMouseMove( hwnd, descr, (INT16)LOWORD(lParam),
2966 (INT16)HIWORD(lParam) );
2969 return LISTBOX_HandleLButtonUp( hwnd, descr );
2971 return LISTBOX_HandleKeyDown( hwnd, descr, wParam );
2976 charW = (WCHAR)wParam;
2979 CHAR charA = (CHAR)wParam;
2980 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
2982 return LISTBOX_HandleChar( hwnd, descr, charW );
2985 return LISTBOX_HandleSystemTimer( hwnd, descr );
2987 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
2990 HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
2991 wParam, (LPARAM)hwnd );
2992 TRACE("hbrush = %04x\n", hbrush);
2994 hbrush = GetSysColorBrush(COLOR_WINDOW);
2997 GetClientRect(hwnd, &rect);
2998 FillRect((HDC)wParam, &rect, hbrush);
3004 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3005 SendMessageA( descr->owner, msg, wParam, lParam );
3009 case WM_QUERYDROPOBJECT:
3014 LPDRAGINFO16 dragInfo = MapSL( lParam );
3015 dragInfo->l = LISTBOX_GetItemFromPoint( descr, dragInfo->pt.x,
3017 return SendMessage16( HWND_16(descr->owner), msg, wParam, lParam );
3022 if ((msg >= WM_USER) && (msg < 0xc000))
3023 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
3024 hwnd, msg, wParam, lParam );
3025 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3026 DefWindowProcA( hwnd, msg, wParam, lParam );
3031 /***********************************************************************
3034 * This is just a wrapper for the real wndproc, it only does window locking
3037 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3039 if (!IsWindow(hwnd)) return 0;
3040 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3043 /***********************************************************************
3046 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3048 if (!IsWindow(hwnd)) return 0;
3049 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE );
3052 /***********************************************************************
3053 * ComboLBWndProc_common
3055 * The real combo listbox wndproc
3057 static LRESULT WINAPI ComboLBWndProc_common( HWND hwnd, UINT msg,
3058 WPARAM wParam, LPARAM lParam, BOOL unicode )
3064 if (!(descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 )))
3066 if (msg == WM_CREATE)
3068 CREATESTRUCTA *lpcs = (CREATESTRUCTA *)lParam;
3069 TRACE_(combo)("\tpassed parent handle = %p\n",lpcs->lpCreateParams);
3070 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
3071 return LISTBOX_Create( hwnd, lphc );
3073 /* Ignore all other messages before we get a WM_CREATE */
3074 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3075 DefWindowProcA( hwnd, msg, wParam, lParam );
3078 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
3079 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
3081 if ((lphc = descr->lphc) != NULL)
3086 if ( (TWEAK_WineLook > WIN31_LOOK) &&
3087 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
3093 mousePos.x = (INT16)LOWORD(lParam);
3094 mousePos.y = (INT16)HIWORD(lParam);
3097 * If we are in a dropdown combobox, we simulate that
3098 * the mouse is captured to show the tracking of the item.
3100 GetClientRect(hwnd, &clientRect);
3102 if (PtInRect( &clientRect, mousePos ))
3104 captured = descr->captured;
3105 descr->captured = TRUE;
3107 LISTBOX_HandleMouseMove( hwnd, descr,
3108 mousePos.x, mousePos.y);
3110 descr->captured = captured;
3115 LISTBOX_HandleMouseMove( hwnd, descr,
3116 mousePos.x, mousePos.y);
3122 /* else we are in Win3.1 look, go with the default behavior. */
3126 if (TWEAK_WineLook > WIN31_LOOK)
3132 * If the mouse button "up" is not in the listbox,
3133 * we make sure there is no selection by re-selecting the
3134 * item that was selected when the listbox was made visible.
3136 mousePos.x = (INT16)LOWORD(lParam);
3137 mousePos.y = (INT16)HIWORD(lParam);
3139 GetClientRect(hwnd, &clientRect);
3142 * When the user clicks outside the combobox and the focus
3143 * is lost, the owning combobox will send a fake buttonup with
3144 * 0xFFFFFFF as the mouse location, we must also revert the
3145 * selection to the original selection.
3147 if ( (lParam == (LPARAM)-1) ||
3148 (!PtInRect( &clientRect, mousePos )) )
3150 LISTBOX_MoveCaret( hwnd, descr, lphc->droppedIndex, FALSE );
3153 return LISTBOX_HandleLButtonUp( hwnd, descr );
3154 case WM_LBUTTONDBLCLK:
3155 case WM_LBUTTONDOWN:
3156 return LISTBOX_HandleLButtonDownCombo(hwnd, descr, msg, wParam,
3157 (INT16)LOWORD(lParam),
3158 (INT16)HIWORD(lParam) );
3162 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3164 /* for some reason(?) Windows makes it possible to
3165 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3167 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3168 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3169 && (wParam == VK_DOWN || wParam == VK_UP)) )
3171 COMBO_FlipListbox( lphc, FALSE, FALSE );
3175 return LISTBOX_HandleKeyDown( hwnd, descr, wParam );
3177 case LB_SETCURSEL16:
3179 lRet = unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3180 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3181 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
3184 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3190 /* default handling: call listbox wnd proc */
3191 lRet = unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3192 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3194 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
3199 /***********************************************************************
3202 * NOTE: in Windows, winproc address of the ComboLBox is the same
3203 * as that of the Listbox.
3205 LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3207 if (!IsWindow(hwnd)) return 0;
3208 return ComboLBWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3211 /***********************************************************************
3214 LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3216 if (!IsWindow(hwnd)) return 0;
3217 return ComboLBWndProc_common( hwnd, msg, wParam, lParam, TRUE );