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
28 #include "wine/winuser16.h"
29 #include "wine/winbase16.h"
31 #include "wine/unicode.h"
37 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(listbox);
41 WINE_DECLARE_DEBUG_CHANNEL(combo);
47 * Probably needs improvement:
51 /* Items array granularity */
52 #define LB_ARRAY_GRANULARITY 16
54 /* Scrolling timeout in ms */
55 #define LB_SCROLL_TIMEOUT 50
57 /* Listbox system timer id */
60 /* flag listbox changed while setredraw false - internal style */
61 #define LBS_DISPLAYCHANGED 0x80000000
66 LPWSTR str; /* Item text */
67 BOOL selected; /* Is item selected? */
68 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
69 DWORD data; /* User data */
72 /* Listbox structure */
75 HWND owner; /* Owner window to send notifications to */
76 UINT style; /* Window style */
77 INT width; /* Window width */
78 INT height; /* Window height */
79 LB_ITEMDATA *items; /* Array of items */
80 INT nb_items; /* Number of items */
81 INT top_item; /* Top visible item */
82 INT selected_item; /* Selected item */
83 INT focus_item; /* Item that has the focus */
84 INT anchor_item; /* Anchor item for extended selection */
85 INT item_height; /* Default item height */
86 INT page_size; /* Items per listbox page */
87 INT column_width; /* Column width for multi-column listboxes */
88 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
89 INT horz_pos; /* Horizontal position */
90 INT nb_tabs; /* Number of tabs in array */
91 INT *tabs; /* Array of tabs */
92 BOOL caret_on; /* Is caret on? */
93 BOOL captured; /* Is mouse captured? */
95 HFONT font; /* Current font */
96 LCID locale; /* Current locale for string comparisons */
97 LPHEADCOMBO lphc; /* ComboLBox */
101 #define IS_OWNERDRAW(descr) \
102 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
104 #define HAS_STRINGS(descr) \
105 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
108 #define IS_MULTISELECT(descr) \
109 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
111 #define SEND_NOTIFICATION(hwnd,descr,code) \
112 (SendMessageW( (descr)->owner, WM_COMMAND, \
113 MAKEWPARAM( GetWindowLongPtrW((hwnd),GWLP_ID), (code)), (LPARAM)(hwnd) ))
115 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
117 /* Current timer status */
127 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
129 static LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
130 static LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
131 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
132 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
134 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect );
136 /*********************************************************************
137 * listbox class descriptor
139 const struct builtin_class_descr LISTBOX_builtin_class =
141 "ListBox", /* name */
142 CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
143 ListBoxWndProcA, /* procA */
144 ListBoxWndProcW, /* procW */
145 sizeof(LB_DESCR *), /* extra */
146 IDC_ARROW, /* cursor */
151 /*********************************************************************
152 * combolbox class descriptor
154 const struct builtin_class_descr COMBOLBOX_builtin_class =
156 "ComboLBox", /* name */
157 CS_DBLCLKS | CS_SAVEBITS, /* style */
158 ComboLBWndProcA, /* procA */
159 ComboLBWndProcW, /* procW */
160 sizeof(LB_DESCR *), /* extra */
161 IDC_ARROW, /* cursor */
166 /* check whether app is a Win 3.1 app */
167 inline static BOOL is_old_app( HWND hwnd )
169 return (GetExpWinVer16( GetWindowLongPtrW(hwnd,GWLP_HINSTANCE) ) & 0xFF00 ) == 0x0300;
173 /***********************************************************************
176 void LISTBOX_Dump( HWND hwnd )
180 LB_DESCR *descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 );
182 TRACE( "Listbox:\n" );
183 TRACE( "hwnd=%p descr=%08x items=%d top=%d\n",
184 hwnd, (UINT)descr, descr->nb_items, descr->top_item );
185 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
187 TRACE( "%4d: %-40s %d %08lx %3d\n",
188 i, debugstr_w(item->str), item->selected, item->data, item->height );
193 /***********************************************************************
194 * LISTBOX_GetCurrentPageSize
196 * Return the current page size
198 static INT LISTBOX_GetCurrentPageSize( LB_DESCR *descr )
201 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
202 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
204 if ((height += descr->items[i].height) > descr->height) break;
206 if (i == descr->top_item) return 1;
207 else return i - descr->top_item;
211 /***********************************************************************
212 * LISTBOX_GetMaxTopIndex
214 * Return the maximum possible index for the top of the listbox.
216 static INT LISTBOX_GetMaxTopIndex( LB_DESCR *descr )
220 if (descr->style & LBS_OWNERDRAWVARIABLE)
222 page = descr->height;
223 for (max = descr->nb_items - 1; max >= 0; max--)
224 if ((page -= descr->items[max].height) < 0) break;
225 if (max < descr->nb_items - 1) max++;
227 else if (descr->style & LBS_MULTICOLUMN)
229 if ((page = descr->width / descr->column_width) < 1) page = 1;
230 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
231 max = (max - page) * descr->page_size;
235 max = descr->nb_items - descr->page_size;
237 if (max < 0) max = 0;
242 /***********************************************************************
243 * LISTBOX_UpdateScroll
245 * Update the scrollbars. Should be called whenever the content
246 * of the listbox changes.
248 static void LISTBOX_UpdateScroll( HWND hwnd, LB_DESCR *descr )
252 /* Check the listbox scroll bar flags individually before we call
253 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
254 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
255 scroll bar when we do not need one.
256 if (!(descr->style & WS_VSCROLL)) return;
259 /* It is important that we check descr->style, and not wnd->dwStyle,
260 for WS_VSCROLL, as the former is exactly the one passed in
261 argument to CreateWindow.
262 In Windows (and from now on in Wine :) a listbox created
263 with such a style (no WS_SCROLL) does not update
264 the scrollbar with listbox-related data, thus letting
265 the programmer use it for his/her own purposes. */
267 if (descr->style & LBS_NOREDRAW) return;
268 info.cbSize = sizeof(info);
270 if (descr->style & LBS_MULTICOLUMN)
273 info.nMax = (descr->nb_items - 1) / descr->page_size;
274 info.nPos = descr->top_item / descr->page_size;
275 info.nPage = descr->width / descr->column_width;
276 if (info.nPage < 1) info.nPage = 1;
277 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
278 if (descr->style & LBS_DISABLENOSCROLL)
279 info.fMask |= SIF_DISABLENOSCROLL;
280 if (descr->style & WS_HSCROLL)
281 SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
283 info.fMask = SIF_RANGE;
284 if (descr->style & WS_VSCROLL)
285 SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
290 info.nMax = descr->nb_items - 1;
291 info.nPos = descr->top_item;
292 info.nPage = LISTBOX_GetCurrentPageSize( descr );
293 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
294 if (descr->style & LBS_DISABLENOSCROLL)
295 info.fMask |= SIF_DISABLENOSCROLL;
296 if (descr->style & WS_VSCROLL)
297 SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
299 if (descr->horz_extent)
302 info.nMax = descr->horz_extent - 1;
303 info.nPos = descr->horz_pos;
304 info.nPage = descr->width;
305 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
306 if (descr->style & LBS_DISABLENOSCROLL)
307 info.fMask |= SIF_DISABLENOSCROLL;
308 if (descr->style & WS_HSCROLL)
309 SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
315 /***********************************************************************
318 * Set the top item of the listbox, scrolling up or down if necessary.
320 static LRESULT LISTBOX_SetTopItem( HWND hwnd, LB_DESCR *descr, INT index,
323 INT max = LISTBOX_GetMaxTopIndex( descr );
324 if (index > max) index = max;
325 if (index < 0) index = 0;
326 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
327 if (descr->top_item == index) return LB_OKAY;
328 if (descr->style & LBS_MULTICOLUMN)
330 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
331 if (scroll && (abs(diff) < descr->width))
332 ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
333 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
341 if (descr->style & LBS_OWNERDRAWVARIABLE)
345 if (index > descr->top_item)
347 for (i = index - 1; i >= descr->top_item; i--)
348 diff -= descr->items[i].height;
352 for (i = index; i < descr->top_item; i++)
353 diff += descr->items[i].height;
357 diff = (descr->top_item - index) * descr->item_height;
359 if (abs(diff) < descr->height)
360 ScrollWindowEx( hwnd, 0, diff, NULL, NULL, 0, NULL,
361 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
365 if (!scroll) InvalidateRect( hwnd, NULL, TRUE );
366 descr->top_item = index;
367 LISTBOX_UpdateScroll( hwnd, descr );
372 /***********************************************************************
375 * Update the page size. Should be called when the size of
376 * the client area or the item height changes.
378 static void LISTBOX_UpdatePage( HWND hwnd, LB_DESCR *descr )
382 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
384 if (page_size == descr->page_size) return;
385 descr->page_size = page_size;
386 if (descr->style & LBS_MULTICOLUMN)
387 InvalidateRect( hwnd, NULL, TRUE );
388 LISTBOX_SetTopItem( hwnd, descr, descr->top_item, FALSE );
392 /***********************************************************************
395 * Update the size of the listbox. Should be called when the size of
396 * the client area changes.
398 static void LISTBOX_UpdateSize( HWND hwnd, LB_DESCR *descr )
402 GetClientRect( hwnd, &rect );
403 descr->width = rect.right - rect.left;
404 descr->height = rect.bottom - rect.top;
405 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
410 GetWindowRect( hwnd, &rect );
411 if(descr->item_height != 0)
412 remaining = descr->height % descr->item_height;
415 if ((descr->height > descr->item_height) && remaining)
417 if (is_old_app(hwnd))
418 { /* give a margin for error to 16 bits programs - if we need
419 less than the height of the nonclient area, round to the
420 *next* number of items */
421 int ncheight = rect.bottom - rect.top - descr->height;
422 if ((descr->item_height - remaining) <= ncheight)
423 remaining = remaining - descr->item_height;
425 TRACE("[%p]: changing height %d -> %d\n",
426 hwnd, descr->height, descr->height - remaining );
427 SetWindowPos( hwnd, 0, 0, 0, rect.right - rect.left,
428 rect.bottom - rect.top - remaining,
429 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
433 TRACE("[%p]: new size = %d,%d\n", hwnd, descr->width, descr->height );
434 LISTBOX_UpdatePage( hwnd, descr );
435 LISTBOX_UpdateScroll( hwnd, descr );
437 /* Invalidate the focused item so it will be repainted correctly */
438 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
440 InvalidateRect( hwnd, &rect, FALSE );
445 /***********************************************************************
446 * LISTBOX_GetItemRect
448 * Get the rectangle enclosing an item, in listbox client coordinates.
449 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
451 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect )
453 /* Index <= 0 is legal even on empty listboxes */
454 if (index && (index >= descr->nb_items)) return -1;
455 SetRect( rect, 0, 0, descr->width, descr->height );
456 if (descr->style & LBS_MULTICOLUMN)
458 INT col = (index / descr->page_size) -
459 (descr->top_item / descr->page_size);
460 rect->left += col * descr->column_width;
461 rect->right = rect->left + descr->column_width;
462 rect->top += (index % descr->page_size) * descr->item_height;
463 rect->bottom = rect->top + descr->item_height;
465 else if (descr->style & LBS_OWNERDRAWVARIABLE)
468 rect->right += descr->horz_pos;
469 if ((index >= 0) && (index < descr->nb_items))
471 if (index < descr->top_item)
473 for (i = descr->top_item-1; i >= index; i--)
474 rect->top -= descr->items[i].height;
478 for (i = descr->top_item; i < index; i++)
479 rect->top += descr->items[i].height;
481 rect->bottom = rect->top + descr->items[index].height;
487 rect->top += (index - descr->top_item) * descr->item_height;
488 rect->bottom = rect->top + descr->item_height;
489 rect->right += descr->horz_pos;
492 return ((rect->left < descr->width) && (rect->right > 0) &&
493 (rect->top < descr->height) && (rect->bottom > 0));
497 /***********************************************************************
498 * LISTBOX_GetItemFromPoint
500 * Return the item nearest from point (x,y) (in client coordinates).
502 static INT LISTBOX_GetItemFromPoint( LB_DESCR *descr, INT x, INT y )
504 INT index = descr->top_item;
506 if (!descr->nb_items) return -1; /* No items */
507 if (descr->style & LBS_OWNERDRAWVARIABLE)
512 while (index < descr->nb_items)
514 if ((pos += descr->items[index].height) > y) break;
523 if ((pos -= descr->items[index].height) <= y) break;
527 else if (descr->style & LBS_MULTICOLUMN)
529 if (y >= descr->item_height * descr->page_size) return -1;
530 if (y >= 0) index += y / descr->item_height;
531 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
532 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
536 index += (y / descr->item_height);
538 if (index < 0) return 0;
539 if (index >= descr->nb_items) return -1;
544 /***********************************************************************
549 static void LISTBOX_PaintItem( HWND hwnd, LB_DESCR *descr, HDC hdc,
550 const RECT *rect, INT index, UINT action, BOOL ignoreFocus )
552 LB_ITEMDATA *item = NULL;
553 if (index < descr->nb_items) item = &descr->items[index];
555 if (IS_OWNERDRAW(descr))
560 UINT id = (UINT)GetWindowLongPtrW( hwnd, GWLP_ID );
564 if (action == ODA_FOCUS)
565 DrawFocusRect( hdc, rect );
567 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
571 /* some programs mess with the clipping region when
572 drawing the item, *and* restore the previous region
573 after they are done, so a region has better to exist
574 else everything ends clipped */
575 GetClientRect(hwnd, &r);
576 hrgn = CreateRectRgnIndirect(&r);
577 SelectClipRgn( hdc, hrgn);
578 DeleteObject( hrgn );
580 dis.CtlType = ODT_LISTBOX;
583 dis.itemAction = action;
587 if (item && item->selected) dis.itemState |= ODS_SELECTED;
588 if (!ignoreFocus && (descr->focus_item == index) &&
590 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
591 if (!IsWindowEnabled(hwnd)) dis.itemState |= ODS_DISABLED;
592 dis.itemData = item ? item->data : 0;
594 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%ld,%ld-%ld,%ld\n",
595 hwnd, index, item ? debugstr_w(item->str) : "", action,
596 dis.itemState, rect->left, rect->top, rect->right, rect->bottom );
597 SendMessageW(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
601 COLORREF oldText = 0, oldBk = 0;
603 if (action == ODA_FOCUS)
605 DrawFocusRect( hdc, rect );
608 if (item && item->selected)
610 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
611 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
614 TRACE("[%p]: painting %d (%s) action=%02x rect=%ld,%ld-%ld,%ld\n",
615 hwnd, index, item ? debugstr_w(item->str) : "", action,
616 rect->left, rect->top, rect->right, rect->bottom );
618 ExtTextOutW( hdc, rect->left + 1, rect->top,
619 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
620 else if (!(descr->style & LBS_USETABSTOPS))
621 ExtTextOutW( hdc, rect->left + 1, rect->top,
622 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
623 strlenW(item->str), NULL );
626 /* Output empty string to paint background in the full width. */
627 ExtTextOutW( hdc, rect->left + 1, rect->top,
628 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
629 TabbedTextOutW( hdc, rect->left + 1 , rect->top,
630 item->str, strlenW(item->str),
631 descr->nb_tabs, descr->tabs, 0);
633 if (item && item->selected)
635 SetBkColor( hdc, oldBk );
636 SetTextColor( hdc, oldText );
638 if (!ignoreFocus && (descr->focus_item == index) &&
640 (descr->in_focus)) DrawFocusRect( hdc, rect );
645 /***********************************************************************
648 * Change the redraw flag.
650 static void LISTBOX_SetRedraw( HWND hwnd, LB_DESCR *descr, BOOL on )
654 if (!(descr->style & LBS_NOREDRAW)) return;
655 descr->style &= ~LBS_NOREDRAW;
656 if (descr->style & LBS_DISPLAYCHANGED)
657 { /* page was changed while setredraw false, refresh automatically */
658 InvalidateRect(hwnd, NULL, TRUE);
659 if ((descr->top_item + descr->page_size) > descr->nb_items)
660 { /* reset top of page if less than number of items/page */
661 descr->top_item = descr->nb_items - descr->page_size;
662 if (descr->top_item < 0) descr->top_item = 0;
664 descr->style &= ~LBS_DISPLAYCHANGED;
666 LISTBOX_UpdateScroll( hwnd, descr );
668 else descr->style |= LBS_NOREDRAW;
672 /***********************************************************************
673 * LISTBOX_RepaintItem
675 * Repaint a single item synchronously.
677 static void LISTBOX_RepaintItem( HWND hwnd, LB_DESCR *descr, INT index,
683 HBRUSH hbrush, oldBrush = 0;
685 /* Do not repaint the item if the item is not visible */
686 if (!IsWindowVisible(hwnd)) return;
687 if (descr->style & LBS_NOREDRAW)
689 descr->style |= LBS_DISPLAYCHANGED;
692 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
693 if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE ))) return;
694 if (descr->font) oldFont = SelectObject( hdc, descr->font );
695 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
696 (WPARAM)hdc, (LPARAM)hwnd );
697 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
698 if (!IsWindowEnabled(hwnd))
699 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
700 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
701 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, index, action, FALSE );
702 if (oldFont) SelectObject( hdc, oldFont );
703 if (oldBrush) SelectObject( hdc, oldBrush );
704 ReleaseDC( hwnd, hdc );
708 /***********************************************************************
709 * LISTBOX_InitStorage
711 static LRESULT LISTBOX_InitStorage( HWND hwnd, LB_DESCR *descr, INT nb_items )
715 nb_items += LB_ARRAY_GRANULARITY - 1;
716 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
718 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
719 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
720 nb_items * sizeof(LB_ITEMDATA));
723 item = HeapAlloc( GetProcessHeap(), 0,
724 nb_items * sizeof(LB_ITEMDATA));
729 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
737 /***********************************************************************
738 * LISTBOX_SetTabStops
740 static BOOL LISTBOX_SetTabStops( HWND hwnd, LB_DESCR *descr, INT count,
741 LPINT tabs, BOOL short_ints )
743 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
744 if (descr->tabs) HeapFree( GetProcessHeap(), 0, descr->tabs );
745 if (!(descr->nb_tabs = count))
750 /* FIXME: count = 1 */
751 if (!(descr->tabs = (INT *)HeapAlloc( GetProcessHeap(), 0,
752 descr->nb_tabs * sizeof(INT) )))
757 LPINT16 p = (LPINT16)tabs;
759 TRACE("[%p]: settabstops ", hwnd );
760 for (i = 0; i < descr->nb_tabs; i++) {
761 descr->tabs[i] = *p++<<1; /* FIXME */
762 if (TRACE_ON(listbox)) TRACE("%hd ", descr->tabs[i]);
764 if (TRACE_ON(listbox)) TRACE("\n");
766 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
767 /* FIXME: repaint the window? */
772 /***********************************************************************
775 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPARAM lParam, BOOL unicode )
777 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
778 if (HAS_STRINGS(descr))
782 DWORD len = strlenW(descr->items[index].str);
785 return WideCharToMultiByte( CP_ACP, 0, descr->items[index].str, len,
786 NULL, 0, NULL, NULL );
789 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
793 LPWSTR buffer = (LPWSTR)lParam;
794 strcpyW( buffer, descr->items[index].str );
795 return strlenW(buffer);
799 LPSTR buffer = (LPSTR)lParam;
800 return WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, buffer, 0x7FFFFFFF, NULL, NULL) - 1;
804 *((LPDWORD)lParam)=*(LPDWORD)(&descr->items[index].data);
805 return sizeof(DWORD);
810 /***********************************************************************
811 * LISTBOX_FindStringPos
813 * Find the nearest string located before a given string in sort order.
814 * If 'exact' is TRUE, return an error if we don't get an exact match.
816 static INT LISTBOX_FindStringPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str,
819 INT index, min, max, res = -1;
821 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
823 max = descr->nb_items;
826 index = (min + max) / 2;
827 if (HAS_STRINGS(descr))
828 res = lstrcmpiW( str, descr->items[index].str);
831 COMPAREITEMSTRUCT cis;
832 UINT id = (UINT)GetWindowLongPtrW( hwnd, GWLP_ID );
834 cis.CtlType = ODT_LISTBOX;
837 /* note that some application (MetaStock) expects the second item
838 * to be in the listbox */
840 cis.itemData1 = (DWORD)str;
842 cis.itemData2 = descr->items[index].data;
843 cis.dwLocaleId = descr->locale;
844 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
846 if (!res) return index;
847 if (res < 0) max = index;
848 else min = index + 1;
850 return exact ? -1 : max;
854 /***********************************************************************
855 * LISTBOX_FindFileStrPos
857 * Find the nearest string located before a given string in directory
858 * sort order (i.e. first files, then directories, then drives).
860 static INT LISTBOX_FindFileStrPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str )
862 INT min, max, res = -1;
864 if (!HAS_STRINGS(descr))
865 return LISTBOX_FindStringPos( hwnd, descr, str, FALSE );
867 max = descr->nb_items;
870 INT index = (min + max) / 2;
871 LPCWSTR p = descr->items[index].str;
872 if (*p == '[') /* drive or directory */
874 if (*str != '[') res = -1;
875 else if (p[1] == '-') /* drive */
877 if (str[1] == '-') res = str[2] - p[2];
882 if (str[1] == '-') res = 1;
883 else res = lstrcmpiW( str, p );
888 if (*str == '[') res = 1;
889 else res = lstrcmpiW( str, p );
891 if (!res) return index;
892 if (res < 0) max = index;
893 else min = index + 1;
899 /***********************************************************************
902 * Find the item beginning with a given string.
904 static INT LISTBOX_FindString( HWND hwnd, LB_DESCR *descr, INT start,
905 LPCWSTR str, BOOL exact )
910 if (start >= descr->nb_items) start = -1;
911 item = descr->items + start + 1;
912 if (HAS_STRINGS(descr))
914 if (!str || ! str[0] ) return LB_ERR;
917 for (i = start + 1; i < descr->nb_items; i++, item++)
918 if (!lstrcmpiW( str, item->str )) return i;
919 for (i = 0, item = descr->items; i <= start; i++, item++)
920 if (!lstrcmpiW( str, item->str )) return i;
924 /* Special case for drives and directories: ignore prefix */
925 #define CHECK_DRIVE(item) \
926 if ((item)->str[0] == '[') \
928 if (!strncmpiW( str, (item)->str+1, len )) return i; \
929 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
933 INT len = strlenW(str);
934 for (i = start + 1; i < descr->nb_items; i++, item++)
936 if (!strncmpiW( str, item->str, len )) return i;
939 for (i = 0, item = descr->items; i <= start; i++, item++)
941 if (!strncmpiW( str, item->str, len )) return i;
949 if (exact && (descr->style & LBS_SORT))
950 /* If sorted, use a WM_COMPAREITEM binary search */
951 return LISTBOX_FindStringPos( hwnd, descr, str, TRUE );
953 /* Otherwise use a linear search */
954 for (i = start + 1; i < descr->nb_items; i++, item++)
955 if (item->data == (DWORD)str) return i;
956 for (i = 0, item = descr->items; i <= start; i++, item++)
957 if (item->data == (DWORD)str) return i;
963 /***********************************************************************
964 * LISTBOX_GetSelCount
966 static LRESULT LISTBOX_GetSelCount( LB_DESCR *descr )
969 LB_ITEMDATA *item = descr->items;
971 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
972 for (i = count = 0; i < descr->nb_items; i++, item++)
973 if (item->selected) count++;
978 /***********************************************************************
979 * LISTBOX_GetSelItems16
981 static LRESULT LISTBOX_GetSelItems16( LB_DESCR *descr, INT16 max, LPINT16 array )
984 LB_ITEMDATA *item = descr->items;
986 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
987 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
988 if (item->selected) array[count++] = (INT16)i;
993 /***********************************************************************
994 * LISTBOX_GetSelItems
996 static LRESULT LISTBOX_GetSelItems( LB_DESCR *descr, INT max, LPINT array )
999 LB_ITEMDATA *item = descr->items;
1001 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1002 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
1003 if (item->selected) array[count++] = i;
1008 /***********************************************************************
1011 static LRESULT LISTBOX_Paint( HWND hwnd, LB_DESCR *descr, HDC hdc )
1013 INT i, col_pos = descr->page_size - 1;
1015 RECT focusRect = {-1, -1, -1, -1};
1017 HBRUSH hbrush, oldBrush = 0;
1019 if (descr->style & LBS_NOREDRAW) return 0;
1021 SetRect( &rect, 0, 0, descr->width, descr->height );
1022 if (descr->style & LBS_MULTICOLUMN)
1023 rect.right = rect.left + descr->column_width;
1024 else if (descr->horz_pos)
1026 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
1027 rect.right += descr->horz_pos;
1030 if (descr->font) oldFont = SelectObject( hdc, descr->font );
1031 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1032 (WPARAM)hdc, (LPARAM)hwnd );
1033 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1034 if (!IsWindowEnabled(hwnd)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1036 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1039 /* Special case for empty listbox: paint focus rect */
1040 rect.bottom = rect.top + descr->item_height;
1041 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, descr->focus_item,
1043 rect.top = rect.bottom;
1046 /* Paint all the item, regarding the selection
1047 Focus state will be painted after */
1049 for (i = descr->top_item; i < descr->nb_items; i++)
1051 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1052 rect.bottom = rect.top + descr->item_height;
1054 rect.bottom = rect.top + descr->items[i].height;
1056 if (i == descr->focus_item)
1058 /* keep the focus rect, to paint the focus item after */
1059 focusRect.left = rect.left;
1060 focusRect.right = rect.right;
1061 focusRect.top = rect.top;
1062 focusRect.bottom = rect.bottom;
1064 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1065 rect.top = rect.bottom;
1067 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1069 if (!IS_OWNERDRAW(descr))
1071 /* Clear the bottom of the column */
1072 if (rect.top < descr->height)
1074 rect.bottom = descr->height;
1075 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1076 &rect, NULL, 0, NULL );
1080 /* Go to the next column */
1081 rect.left += descr->column_width;
1082 rect.right += descr->column_width;
1084 col_pos = descr->page_size - 1;
1089 if (rect.top >= descr->height) break;
1093 /* Paint the focus item now */
1094 if (focusRect.top != focusRect.bottom && descr->caret_on)
1095 LISTBOX_PaintItem( hwnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1097 if (!IS_OWNERDRAW(descr))
1099 /* Clear the remainder of the client area */
1100 if (rect.top < descr->height)
1102 rect.bottom = descr->height;
1103 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1104 &rect, NULL, 0, NULL );
1106 if (rect.right < descr->width)
1108 rect.left = rect.right;
1109 rect.right = descr->width;
1111 rect.bottom = descr->height;
1112 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1113 &rect, NULL, 0, NULL );
1116 if (oldFont) SelectObject( hdc, oldFont );
1117 if (oldBrush) SelectObject( hdc, oldBrush );
1122 /***********************************************************************
1123 * LISTBOX_InvalidateItems
1125 * Invalidate all items from a given item. If the specified item is not
1126 * visible, nothing happens.
1128 static void LISTBOX_InvalidateItems( HWND hwnd, LB_DESCR *descr, INT index )
1132 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1134 if (descr->style & LBS_NOREDRAW)
1136 descr->style |= LBS_DISPLAYCHANGED;
1139 rect.bottom = descr->height;
1140 InvalidateRect( hwnd, &rect, TRUE );
1141 if (descr->style & LBS_MULTICOLUMN)
1143 /* Repaint the other columns */
1144 rect.left = rect.right;
1145 rect.right = descr->width;
1147 InvalidateRect( hwnd, &rect, TRUE );
1152 static void LISTBOX_InvalidateItemRect( HWND hwnd, LB_DESCR *descr, INT index )
1156 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1157 InvalidateRect( hwnd, &rect, TRUE );
1160 /***********************************************************************
1161 * LISTBOX_GetItemHeight
1163 static LRESULT LISTBOX_GetItemHeight( LB_DESCR *descr, INT index )
1165 if (descr->style & LBS_OWNERDRAWVARIABLE)
1167 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1168 return descr->items[index].height;
1170 else return descr->item_height;
1174 /***********************************************************************
1175 * LISTBOX_SetItemHeight
1177 static LRESULT LISTBOX_SetItemHeight( HWND hwnd, LB_DESCR *descr, INT index,
1178 INT height, BOOL repaint )
1180 if (!height) height = 1;
1182 if (descr->style & LBS_OWNERDRAWVARIABLE)
1184 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1185 TRACE("[%p]: item %d height = %d\n", hwnd, index, height );
1186 descr->items[index].height = height;
1187 LISTBOX_UpdateScroll( hwnd, descr );
1189 LISTBOX_InvalidateItems( hwnd, descr, index );
1191 else if (height != descr->item_height)
1193 TRACE("[%p]: new height = %d\n", hwnd, height );
1194 descr->item_height = height;
1195 LISTBOX_UpdatePage( hwnd, descr );
1196 LISTBOX_UpdateScroll( hwnd, descr );
1198 InvalidateRect( hwnd, 0, TRUE );
1204 /***********************************************************************
1205 * LISTBOX_SetHorizontalPos
1207 static void LISTBOX_SetHorizontalPos( HWND hwnd, LB_DESCR *descr, INT pos )
1211 if (pos > descr->horz_extent - descr->width)
1212 pos = descr->horz_extent - descr->width;
1213 if (pos < 0) pos = 0;
1214 if (!(diff = descr->horz_pos - pos)) return;
1215 TRACE("[%p]: new horz pos = %d\n", hwnd, pos );
1216 descr->horz_pos = pos;
1217 LISTBOX_UpdateScroll( hwnd, descr );
1218 if (abs(diff) < descr->width)
1221 /* Invalidate the focused item so it will be repainted correctly */
1222 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
1223 InvalidateRect( hwnd, &rect, TRUE );
1224 ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
1225 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1228 InvalidateRect( hwnd, NULL, TRUE );
1232 /***********************************************************************
1233 * LISTBOX_SetHorizontalExtent
1235 static LRESULT LISTBOX_SetHorizontalExtent( HWND hwnd, LB_DESCR *descr,
1238 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1240 if (extent <= 0) extent = 1;
1241 if (extent == descr->horz_extent) return LB_OKAY;
1242 TRACE("[%p]: new horz extent = %d\n", hwnd, extent );
1243 descr->horz_extent = extent;
1244 if (descr->horz_pos > extent - descr->width)
1245 LISTBOX_SetHorizontalPos( hwnd, descr, extent - descr->width );
1247 LISTBOX_UpdateScroll( hwnd, descr );
1252 /***********************************************************************
1253 * LISTBOX_SetColumnWidth
1255 static LRESULT LISTBOX_SetColumnWidth( HWND hwnd, LB_DESCR *descr, INT width)
1257 if (width == descr->column_width) return LB_OKAY;
1258 TRACE("[%p]: new column width = %d\n", hwnd, width );
1259 descr->column_width = width;
1260 LISTBOX_UpdatePage( hwnd, descr );
1265 /***********************************************************************
1268 * Returns the item height.
1270 static INT LISTBOX_SetFont( HWND hwnd, LB_DESCR *descr, HFONT font )
1278 if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE )))
1280 ERR("unable to get DC.\n" );
1283 if (font) oldFont = SelectObject( hdc, font );
1284 GetTextMetricsW( hdc, &tm );
1285 if (oldFont) SelectObject( hdc, oldFont );
1286 ReleaseDC( hwnd, hdc );
1287 if (!IS_OWNERDRAW(descr))
1288 LISTBOX_SetItemHeight( hwnd, descr, 0, tm.tmHeight, FALSE );
1289 return tm.tmHeight ;
1293 /***********************************************************************
1294 * LISTBOX_MakeItemVisible
1296 * Make sure that a given item is partially or fully visible.
1298 static void LISTBOX_MakeItemVisible( HWND hwnd, LB_DESCR *descr, INT index,
1303 if (index <= descr->top_item) top = index;
1304 else if (descr->style & LBS_MULTICOLUMN)
1306 INT cols = descr->width;
1307 if (!fully) cols += descr->column_width - 1;
1308 if (cols >= descr->column_width) cols /= descr->column_width;
1310 if (index < descr->top_item + (descr->page_size * cols)) return;
1311 top = index - descr->page_size * (cols - 1);
1313 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1315 INT height = fully ? descr->items[index].height : 1;
1316 for (top = index; top > descr->top_item; top--)
1317 if ((height += descr->items[top-1].height) > descr->height) break;
1321 if (index < descr->top_item + descr->page_size) return;
1322 if (!fully && (index == descr->top_item + descr->page_size) &&
1323 (descr->height > (descr->page_size * descr->item_height))) return;
1324 top = index - descr->page_size + 1;
1326 LISTBOX_SetTopItem( hwnd, descr, top, TRUE );
1329 /***********************************************************************
1330 * LISTBOX_SetCaretIndex
1333 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1336 static LRESULT LISTBOX_SetCaretIndex( HWND hwnd, LB_DESCR *descr, INT index,
1337 BOOL fully_visible )
1339 INT oldfocus = descr->focus_item;
1341 if (descr->style & LBS_NOSEL) return LB_ERR;
1342 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1343 if (index == oldfocus) return LB_OKAY;
1344 descr->focus_item = index;
1345 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1346 LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
1348 LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
1349 if (descr->caret_on && (descr->in_focus))
1350 LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
1356 /***********************************************************************
1357 * LISTBOX_SelectItemRange
1359 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1361 static LRESULT LISTBOX_SelectItemRange( HWND hwnd, LB_DESCR *descr, INT first,
1366 /* A few sanity checks */
1368 if (descr->style & LBS_NOSEL) return LB_ERR;
1369 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1370 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1371 if (last == -1) last = descr->nb_items - 1;
1372 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1373 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1374 /* selected_item reflects last selected/unselected item on multiple sel */
1375 descr->selected_item = last;
1377 if (on) /* Turn selection on */
1379 for (i = first; i <= last; i++)
1381 if (descr->items[i].selected) continue;
1382 descr->items[i].selected = TRUE;
1383 LISTBOX_InvalidateItemRect(hwnd, descr, i);
1385 LISTBOX_SetCaretIndex( hwnd, descr, last, TRUE );
1387 else /* Turn selection off */
1389 for (i = first; i <= last; i++)
1391 if (!descr->items[i].selected) continue;
1392 descr->items[i].selected = FALSE;
1393 LISTBOX_InvalidateItemRect(hwnd, descr, i);
1399 /***********************************************************************
1400 * LISTBOX_SetSelection
1402 static LRESULT LISTBOX_SetSelection( HWND hwnd, LB_DESCR *descr, INT index,
1403 BOOL on, BOOL send_notify )
1405 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1407 if (descr->style & LBS_NOSEL) return LB_ERR;
1408 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1409 if (descr->style & LBS_MULTIPLESEL)
1411 if (index == -1) /* Select all items */
1412 return LISTBOX_SelectItemRange( hwnd, descr, 0, -1, on );
1413 else /* Only one item */
1414 return LISTBOX_SelectItemRange( hwnd, descr, index, index, on );
1418 INT oldsel = descr->selected_item;
1419 if (index == oldsel) return LB_OKAY;
1420 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1421 if (index != -1) descr->items[index].selected = TRUE;
1422 descr->selected_item = index;
1423 if (oldsel != -1) LISTBOX_RepaintItem( hwnd, descr, oldsel, ODA_SELECT );
1424 if (index != -1) LISTBOX_RepaintItem( hwnd, descr, index, ODA_SELECT );
1425 if (send_notify && descr->nb_items) SEND_NOTIFICATION( hwnd, descr,
1426 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1428 if( descr->lphc ) /* set selection change flag for parent combo */
1429 descr->lphc->wState |= CBF_SELCHANGE;
1435 /***********************************************************************
1438 * Change the caret position and extend the selection to the new caret.
1440 static void LISTBOX_MoveCaret( HWND hwnd, LB_DESCR *descr, INT index,
1441 BOOL fully_visible )
1443 INT oldfocus = descr->focus_item;
1445 if ((index < 0) || (index >= descr->nb_items))
1448 /* Important, repaint needs to be done in this order if
1449 you want to mimic Windows behavior:
1450 1. Remove the focus and paint the item
1451 2. Remove the selection and paint the item(s)
1452 3. Set the selection and repaint the item(s)
1453 4. Set the focus to 'index' and repaint the item */
1455 /* 1. remove the focus and repaint the item */
1456 descr->focus_item = -1;
1457 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1458 LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
1460 /* 2. then turn off the previous selection */
1461 /* 3. repaint the new selected item */
1462 if (descr->style & LBS_EXTENDEDSEL)
1464 if (descr->anchor_item != -1)
1466 INT first = min( index, descr->anchor_item );
1467 INT last = max( index, descr->anchor_item );
1469 LISTBOX_SelectItemRange( hwnd, descr, 0, first - 1, FALSE );
1470 LISTBOX_SelectItemRange( hwnd, descr, last + 1, -1, FALSE );
1471 LISTBOX_SelectItemRange( hwnd, descr, first, last, TRUE );
1474 else if (!(descr->style & LBS_MULTIPLESEL))
1476 /* Set selection to new caret item */
1477 LISTBOX_SetSelection( hwnd, descr, index, TRUE, FALSE );
1480 /* 4. repaint the new item with the focus */
1481 descr->focus_item = index;
1482 LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
1483 if (descr->caret_on && (descr->in_focus))
1484 LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
1488 /***********************************************************************
1489 * LISTBOX_InsertItem
1491 static LRESULT LISTBOX_InsertItem( HWND hwnd, LB_DESCR *descr, INT index,
1492 LPWSTR str, DWORD data )
1496 INT oldfocus = descr->focus_item;
1498 if (index == -1) index = descr->nb_items;
1499 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1500 if (!descr->items) max_items = 0;
1501 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1502 if (descr->nb_items == max_items)
1504 /* We need to grow the array */
1505 max_items += LB_ARRAY_GRANULARITY;
1507 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1508 max_items * sizeof(LB_ITEMDATA) );
1510 item = HeapAlloc( GetProcessHeap(), 0,
1511 max_items * sizeof(LB_ITEMDATA) );
1514 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
1517 descr->items = item;
1520 /* Insert the item structure */
1522 item = &descr->items[index];
1523 if (index < descr->nb_items)
1524 RtlMoveMemory( item + 1, item,
1525 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1529 item->selected = FALSE;
1532 /* Get item height */
1534 if (descr->style & LBS_OWNERDRAWVARIABLE)
1536 MEASUREITEMSTRUCT mis;
1537 UINT id = (UINT)GetWindowLongPtrW( hwnd, GWLP_ID );
1539 mis.CtlType = ODT_LISTBOX;
1542 mis.itemData = descr->items[index].data;
1543 mis.itemHeight = descr->item_height;
1544 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1545 item->height = mis.itemHeight ? mis.itemHeight : 1;
1546 TRACE("[%p]: measure item %d (%s) = %d\n",
1547 hwnd, index, str ? debugstr_w(str) : "", item->height );
1550 /* Repaint the items */
1552 LISTBOX_UpdateScroll( hwnd, descr );
1553 LISTBOX_InvalidateItems( hwnd, descr, index );
1555 /* Move selection and focused item */
1556 /* If listbox was empty, set focus to the first item */
1557 if (descr->nb_items == 1)
1558 LISTBOX_SetCaretIndex( hwnd, descr, 0, FALSE );
1559 /* single select don't change selection index in win31 */
1560 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1562 descr->selected_item++;
1563 LISTBOX_SetSelection( hwnd, descr, descr->selected_item-1, TRUE, FALSE );
1567 if (index <= descr->selected_item)
1569 descr->selected_item++;
1570 descr->focus_item = oldfocus; /* focus not changed */
1577 /***********************************************************************
1578 * LISTBOX_InsertString
1580 static LRESULT LISTBOX_InsertString( HWND hwnd, LB_DESCR *descr, INT index,
1583 LPWSTR new_str = NULL;
1587 if (HAS_STRINGS(descr))
1589 static const WCHAR empty_stringW[] = { 0 };
1590 if (!str) str = empty_stringW;
1591 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1593 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
1596 strcpyW(new_str, str);
1598 else data = (DWORD)str;
1600 if (index == -1) index = descr->nb_items;
1601 if ((ret = LISTBOX_InsertItem( hwnd, descr, index, new_str, data )) != 0)
1603 if (new_str) HeapFree( GetProcessHeap(), 0, new_str );
1607 TRACE("[%p]: added item %d %s\n",
1608 hwnd, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1613 /***********************************************************************
1614 * LISTBOX_DeleteItem
1616 * Delete the content of an item. 'index' must be a valid index.
1618 static void LISTBOX_DeleteItem( HWND hwnd, LB_DESCR *descr, INT index )
1620 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1621 * while Win95 sends it for all items with user data.
1622 * It's probably better to send it too often than not
1623 * often enough, so this is what we do here.
1625 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1627 DELETEITEMSTRUCT dis;
1628 UINT id = (UINT)GetWindowLongPtrW( hwnd, GWLP_ID );
1630 dis.CtlType = ODT_LISTBOX;
1633 dis.hwndItem = hwnd;
1634 dis.itemData = descr->items[index].data;
1635 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1637 if (HAS_STRINGS(descr) && descr->items[index].str)
1638 HeapFree( GetProcessHeap(), 0, descr->items[index].str );
1642 /***********************************************************************
1643 * LISTBOX_RemoveItem
1645 * Remove an item from the listbox and delete its content.
1647 static LRESULT LISTBOX_RemoveItem( HWND hwnd, LB_DESCR *descr, INT index )
1652 if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1653 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1655 /* We need to invalidate the original rect instead of the updated one. */
1656 LISTBOX_InvalidateItems( hwnd, descr, index );
1658 LISTBOX_DeleteItem( hwnd, descr, index );
1660 /* Remove the item */
1662 item = &descr->items[index];
1663 if (index < descr->nb_items-1)
1664 RtlMoveMemory( item, item + 1,
1665 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1667 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1669 /* Shrink the item array if possible */
1671 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1672 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1674 max_items -= LB_ARRAY_GRANULARITY;
1675 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1676 max_items * sizeof(LB_ITEMDATA) );
1677 if (item) descr->items = item;
1679 /* Repaint the items */
1681 LISTBOX_UpdateScroll( hwnd, descr );
1682 /* if we removed the scrollbar, reset the top of the list
1683 (correct for owner-drawn ???) */
1684 if (descr->nb_items == descr->page_size)
1685 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1687 /* Move selection and focused item */
1688 if (!IS_MULTISELECT(descr))
1690 if (index == descr->selected_item)
1691 descr->selected_item = -1;
1692 else if (index < descr->selected_item)
1694 descr->selected_item--;
1695 if (ISWIN31) /* win 31 do not change the selected item number */
1696 LISTBOX_SetSelection( hwnd, descr, descr->selected_item + 1, TRUE, FALSE);
1700 if (descr->focus_item >= descr->nb_items)
1702 descr->focus_item = descr->nb_items - 1;
1703 if (descr->focus_item < 0) descr->focus_item = 0;
1709 /***********************************************************************
1710 * LISTBOX_ResetContent
1712 static void LISTBOX_ResetContent( HWND hwnd, LB_DESCR *descr )
1716 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( hwnd, descr, i );
1717 if (descr->items) HeapFree( GetProcessHeap(), 0, descr->items );
1718 descr->nb_items = 0;
1719 descr->top_item = 0;
1720 descr->selected_item = -1;
1721 descr->focus_item = 0;
1722 descr->anchor_item = -1;
1723 descr->items = NULL;
1727 /***********************************************************************
1730 static LRESULT LISTBOX_SetCount( HWND hwnd, LB_DESCR *descr, INT count )
1734 if (HAS_STRINGS(descr)) return LB_ERR;
1735 /* FIXME: this is far from optimal... */
1736 if (count > descr->nb_items)
1738 while (count > descr->nb_items)
1739 if ((ret = LISTBOX_InsertString( hwnd, descr, -1, 0 )) < 0)
1742 else if (count < descr->nb_items)
1744 while (count < descr->nb_items)
1745 if ((ret = LISTBOX_RemoveItem( hwnd, descr, -1 )) < 0)
1752 /***********************************************************************
1755 static LRESULT LISTBOX_Directory( HWND hwnd, LB_DESCR *descr, UINT attrib,
1756 LPCWSTR filespec, BOOL long_names )
1759 LRESULT ret = LB_OKAY;
1760 WIN32_FIND_DATAW entry;
1763 /* don't scan directory if we just want drives exclusively */
1764 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1765 /* scan directory */
1766 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1768 int le = GetLastError();
1769 if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR;
1776 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1778 static const WCHAR bracketW[] = { ']',0 };
1779 static const WCHAR dotW[] = { '.',0 };
1780 if (!(attrib & DDL_DIRECTORY) ||
1781 !strcmpW( entry.cFileName, dotW )) continue;
1783 if (!long_names && entry.cAlternateFileName[0])
1784 strcpyW( buffer + 1, entry.cAlternateFileName );
1786 strcpyW( buffer + 1, entry.cFileName );
1787 strcatW(buffer, bracketW);
1789 else /* not a directory */
1791 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1792 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1794 if ((attrib & DDL_EXCLUSIVE) &&
1795 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1798 if (!long_names && entry.cAlternateFileName[0])
1799 strcpyW( buffer, entry.cAlternateFileName );
1801 strcpyW( buffer, entry.cFileName );
1803 if (!long_names) CharLowerW( buffer );
1804 pos = LISTBOX_FindFileStrPos( hwnd, descr, buffer );
1805 if ((ret = LISTBOX_InsertString( hwnd, descr, pos, buffer )) < 0)
1807 } while (FindNextFileW( handle, &entry ));
1808 FindClose( handle );
1813 if ((ret >= 0) && (attrib & DDL_DRIVES))
1815 WCHAR buffer[] = {'[','-','a','-',']',0};
1816 WCHAR root[] = {'A',':','\\',0};
1818 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1820 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1821 if ((ret = LISTBOX_InsertString( hwnd, descr, -1, buffer )) < 0)
1829 /***********************************************************************
1830 * LISTBOX_HandleVScroll
1832 static LRESULT LISTBOX_HandleVScroll( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1836 if (descr->style & LBS_MULTICOLUMN) return 0;
1837 switch(LOWORD(wParam))
1840 LISTBOX_SetTopItem( hwnd, descr, descr->top_item - 1, TRUE );
1843 LISTBOX_SetTopItem( hwnd, descr, descr->top_item + 1, TRUE );
1846 LISTBOX_SetTopItem( hwnd, descr, descr->top_item -
1847 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1850 LISTBOX_SetTopItem( hwnd, descr, descr->top_item +
1851 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1853 case SB_THUMBPOSITION:
1854 LISTBOX_SetTopItem( hwnd, descr, HIWORD(wParam), TRUE );
1857 info.cbSize = sizeof(info);
1858 info.fMask = SIF_TRACKPOS;
1859 GetScrollInfo( hwnd, SB_VERT, &info );
1860 LISTBOX_SetTopItem( hwnd, descr, info.nTrackPos, TRUE );
1863 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1866 LISTBOX_SetTopItem( hwnd, descr, descr->nb_items, TRUE );
1873 /***********************************************************************
1874 * LISTBOX_HandleHScroll
1876 static LRESULT LISTBOX_HandleHScroll( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1881 if (descr->style & LBS_MULTICOLUMN)
1883 switch(LOWORD(wParam))
1886 LISTBOX_SetTopItem( hwnd, descr, descr->top_item-descr->page_size,
1890 LISTBOX_SetTopItem( hwnd, descr, descr->top_item+descr->page_size,
1894 page = descr->width / descr->column_width;
1895 if (page < 1) page = 1;
1896 LISTBOX_SetTopItem( hwnd, descr,
1897 descr->top_item - page * descr->page_size, TRUE );
1900 page = descr->width / descr->column_width;
1901 if (page < 1) page = 1;
1902 LISTBOX_SetTopItem( hwnd, descr,
1903 descr->top_item + page * descr->page_size, TRUE );
1905 case SB_THUMBPOSITION:
1906 LISTBOX_SetTopItem( hwnd, descr, HIWORD(wParam)*descr->page_size,
1910 info.cbSize = sizeof(info);
1911 info.fMask = SIF_TRACKPOS;
1912 GetScrollInfo( hwnd, SB_VERT, &info );
1913 LISTBOX_SetTopItem( hwnd, descr, info.nTrackPos*descr->page_size,
1917 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1920 LISTBOX_SetTopItem( hwnd, descr, descr->nb_items, TRUE );
1924 else if (descr->horz_extent)
1926 switch(LOWORD(wParam))
1929 LISTBOX_SetHorizontalPos( hwnd, descr, descr->horz_pos - 1 );
1932 LISTBOX_SetHorizontalPos( hwnd, descr, descr->horz_pos + 1 );
1935 LISTBOX_SetHorizontalPos( hwnd, descr,
1936 descr->horz_pos - descr->width );
1939 LISTBOX_SetHorizontalPos( hwnd, descr,
1940 descr->horz_pos + descr->width );
1942 case SB_THUMBPOSITION:
1943 LISTBOX_SetHorizontalPos( hwnd, descr, HIWORD(wParam) );
1946 info.cbSize = sizeof(info);
1947 info.fMask = SIF_TRACKPOS;
1948 GetScrollInfo( hwnd, SB_HORZ, &info );
1949 LISTBOX_SetHorizontalPos( hwnd, descr, info.nTrackPos );
1952 LISTBOX_SetHorizontalPos( hwnd, descr, 0 );
1955 LISTBOX_SetHorizontalPos( hwnd, descr,
1956 descr->horz_extent - descr->width );
1963 static LRESULT LISTBOX_HandleMouseWheel(HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1965 short gcWheelDelta = 0;
1966 UINT pulScrollLines = 3;
1968 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1970 gcWheelDelta -= (short) HIWORD(wParam);
1972 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1974 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1975 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1976 LISTBOX_SetTopItem( hwnd, descr, descr->top_item + cLineScroll, TRUE );
1981 /***********************************************************************
1982 * LISTBOX_HandleLButtonDown
1984 static LRESULT LISTBOX_HandleLButtonDown( HWND hwnd, LB_DESCR *descr,
1985 WPARAM wParam, INT x, INT y )
1987 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
1988 TRACE("[%p]: lbuttondown %d,%d item %d\n", hwnd, x, y, index );
1989 if (!descr->caret_on && (descr->in_focus)) return 0;
1991 if (!descr->in_focus)
1993 if( !descr->lphc ) SetFocus( hwnd );
1994 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
1997 if (index == -1) return 0;
1999 if (descr->style & LBS_EXTENDEDSEL)
2001 /* we should perhaps make sure that all items are deselected
2002 FIXME: needed for !LBS_EXTENDEDSEL, too ?
2003 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
2004 LISTBOX_SetSelection( hwnd, descr, -1, FALSE, FALSE);
2007 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
2008 if (wParam & MK_CONTROL)
2010 LISTBOX_SetCaretIndex( hwnd, descr, index, FALSE );
2011 LISTBOX_SetSelection( hwnd, descr, index,
2012 !descr->items[index].selected,
2013 (descr->style & LBS_NOTIFY) != 0);
2015 else LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
2019 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
2020 LISTBOX_SetSelection( hwnd, descr, index,
2021 (!(descr->style & LBS_MULTIPLESEL) ||
2022 !descr->items[index].selected),
2023 (descr->style & LBS_NOTIFY) != 0 );
2026 descr->captured = TRUE;
2031 if (descr->style & LBS_NOTIFY )
2032 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
2033 MAKELPARAM( x, y ) );
2034 if (GetWindowLongA( hwnd, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
2041 if (DragDetect( hwnd, pt ))
2042 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
2049 /*************************************************************************
2050 * LISTBOX_HandleLButtonDownCombo [Internal]
2052 * Process LButtonDown message for the ComboListBox
2055 * pWnd [I] The windows internal structure
2056 * pDescr [I] The ListBox internal structure
2057 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2058 * x [I] X Mouse Coordinate
2059 * y [I] Y Mouse Coordinate
2062 * 0 since we are processing the WM_LBUTTONDOWN Message
2065 * This function is only to be used when a ListBox is a ComboListBox
2068 static LRESULT LISTBOX_HandleLButtonDownCombo( HWND hwnd, LB_DESCR *pDescr,
2069 UINT msg, WPARAM wParam, INT x, INT y)
2071 RECT clientRect, screenRect;
2077 GetClientRect(hwnd, &clientRect);
2079 if(PtInRect(&clientRect, mousePos))
2081 /* MousePos is in client, resume normal processing */
2082 if (msg == WM_LBUTTONDOWN)
2084 pDescr->lphc->droppedIndex = pDescr->nb_items ? pDescr->selected_item : -1;
2085 return LISTBOX_HandleLButtonDown( hwnd, pDescr, wParam, x, y);
2087 else if (pDescr->style & LBS_NOTIFY)
2088 SEND_NOTIFICATION( hwnd, pDescr, LBN_DBLCLK );
2093 POINT screenMousePos;
2094 HWND hWndOldCapture;
2096 /* Check the Non-Client Area */
2097 screenMousePos = mousePos;
2098 hWndOldCapture = GetCapture();
2100 GetWindowRect(hwnd, &screenRect);
2101 ClientToScreen(hwnd, &screenMousePos);
2103 if(!PtInRect(&screenRect, screenMousePos))
2105 LISTBOX_SetCaretIndex( hwnd, pDescr, pDescr->lphc->droppedIndex, FALSE );
2106 LISTBOX_SetSelection( hwnd, pDescr, pDescr->lphc->droppedIndex, FALSE, FALSE );
2107 COMBO_FlipListbox( pDescr->lphc, FALSE, FALSE );
2112 /* Check to see the NC is a scrollbar */
2114 LONG style = GetWindowLongA( hwnd, GWL_STYLE );
2115 /* Check Vertical scroll bar */
2116 if (style & WS_VSCROLL)
2118 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2119 if (PtInRect( &clientRect, mousePos ))
2121 nHitTestType = HTVSCROLL;
2124 /* Check horizontal scroll bar */
2125 if (style & WS_HSCROLL)
2127 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2128 if (PtInRect( &clientRect, mousePos ))
2130 nHitTestType = HTHSCROLL;
2133 /* Windows sends this message when a scrollbar is clicked
2136 if(nHitTestType != 0)
2138 SendMessageW(hwnd, WM_NCLBUTTONDOWN, nHitTestType,
2139 MAKELONG(screenMousePos.x, screenMousePos.y));
2141 /* Resume the Capture after scrolling is complete
2143 if(hWndOldCapture != 0)
2145 SetCapture(hWndOldCapture);
2152 /***********************************************************************
2153 * LISTBOX_HandleLButtonUp
2155 static LRESULT LISTBOX_HandleLButtonUp( HWND hwnd, LB_DESCR *descr )
2157 if (LISTBOX_Timer != LB_TIMER_NONE)
2158 KillSystemTimer( hwnd, LB_TIMER_ID );
2159 LISTBOX_Timer = LB_TIMER_NONE;
2160 if (descr->captured)
2162 descr->captured = FALSE;
2163 if (GetCapture() == hwnd) ReleaseCapture();
2164 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2165 SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2171 /***********************************************************************
2172 * LISTBOX_HandleTimer
2174 * Handle scrolling upon a timer event.
2175 * Return TRUE if scrolling should continue.
2177 static LRESULT LISTBOX_HandleTimer( HWND hwnd, LB_DESCR *descr,
2178 INT index, TIMER_DIRECTION dir )
2183 if (descr->top_item) index = descr->top_item - 1;
2187 if (descr->top_item) index -= descr->page_size;
2190 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2191 if (index == descr->focus_item) index++;
2192 if (index >= descr->nb_items) index = descr->nb_items - 1;
2194 case LB_TIMER_RIGHT:
2195 if (index + descr->page_size < descr->nb_items)
2196 index += descr->page_size;
2201 if (index == descr->focus_item) return FALSE;
2202 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
2207 /***********************************************************************
2208 * LISTBOX_HandleSystemTimer
2210 * WM_SYSTIMER handler.
2212 static LRESULT LISTBOX_HandleSystemTimer( HWND hwnd, LB_DESCR *descr )
2214 if (!LISTBOX_HandleTimer( hwnd, descr, descr->focus_item, LISTBOX_Timer ))
2216 KillSystemTimer( hwnd, LB_TIMER_ID );
2217 LISTBOX_Timer = LB_TIMER_NONE;
2223 /***********************************************************************
2224 * LISTBOX_HandleMouseMove
2226 * WM_MOUSEMOVE handler.
2228 static void LISTBOX_HandleMouseMove( HWND hwnd, LB_DESCR *descr,
2232 TIMER_DIRECTION dir = LB_TIMER_NONE;
2234 if (!descr->captured) return;
2236 if (descr->style & LBS_MULTICOLUMN)
2239 else if (y >= descr->item_height * descr->page_size)
2240 y = descr->item_height * descr->page_size - 1;
2244 dir = LB_TIMER_LEFT;
2247 else if (x >= descr->width)
2249 dir = LB_TIMER_RIGHT;
2250 x = descr->width - 1;
2255 if (y < 0) dir = LB_TIMER_UP; /* above */
2256 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2259 index = LISTBOX_GetItemFromPoint( descr, x, y );
2260 if (index == -1) index = descr->focus_item;
2261 if (!LISTBOX_HandleTimer( hwnd, descr, index, dir )) dir = LB_TIMER_NONE;
2263 /* Start/stop the system timer */
2265 if (dir != LB_TIMER_NONE)
2266 SetSystemTimer( hwnd, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2267 else if (LISTBOX_Timer != LB_TIMER_NONE)
2268 KillSystemTimer( hwnd, LB_TIMER_ID );
2269 LISTBOX_Timer = dir;
2273 /***********************************************************************
2274 * LISTBOX_HandleKeyDown
2276 static LRESULT LISTBOX_HandleKeyDown( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
2279 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2280 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2281 bForceSelection = FALSE; /* only for single select list */
2283 if (descr->style & LBS_WANTKEYBOARDINPUT)
2285 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2286 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2288 if (caret == -2) return 0;
2290 if (caret == -1) switch(wParam)
2293 if (descr->style & LBS_MULTICOLUMN)
2295 bForceSelection = FALSE;
2296 if (descr->focus_item >= descr->page_size)
2297 caret = descr->focus_item - descr->page_size;
2302 caret = descr->focus_item - 1;
2303 if (caret < 0) caret = 0;
2306 if (descr->style & LBS_MULTICOLUMN)
2308 bForceSelection = FALSE;
2309 if (descr->focus_item + descr->page_size < descr->nb_items)
2310 caret = descr->focus_item + descr->page_size;
2315 caret = descr->focus_item + 1;
2316 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2320 if (descr->style & LBS_MULTICOLUMN)
2322 INT page = descr->width / descr->column_width;
2323 if (page < 1) page = 1;
2324 caret = descr->focus_item - (page * descr->page_size) + 1;
2326 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2327 if (caret < 0) caret = 0;
2330 if (descr->style & LBS_MULTICOLUMN)
2332 INT page = descr->width / descr->column_width;
2333 if (page < 1) page = 1;
2334 caret = descr->focus_item + (page * descr->page_size) - 1;
2336 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2337 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2343 caret = descr->nb_items - 1;
2346 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2347 else if (descr->style & LBS_MULTIPLESEL)
2349 LISTBOX_SetSelection( hwnd, descr, descr->focus_item,
2350 !descr->items[descr->focus_item].selected,
2351 (descr->style & LBS_NOTIFY) != 0 );
2355 bForceSelection = FALSE;
2357 if (bForceSelection) /* focused item is used instead of key */
2358 caret = descr->focus_item;
2361 if ((descr->style & LBS_EXTENDEDSEL) &&
2362 !(GetKeyState( VK_SHIFT ) & 0x8000))
2363 descr->anchor_item = caret;
2364 LISTBOX_MoveCaret( hwnd, descr, caret, TRUE );
2365 LISTBOX_SetSelection( hwnd, descr, caret, TRUE, FALSE);
2366 if (descr->style & LBS_NOTIFY)
2370 /* make sure that combo parent doesn't hide us */
2371 descr->lphc->wState |= CBF_NOROLLUP;
2373 if (descr->nb_items) SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2380 /***********************************************************************
2381 * LISTBOX_HandleChar
2383 static LRESULT LISTBOX_HandleChar( HWND hwnd, LB_DESCR *descr, WCHAR charW )
2391 if (descr->style & LBS_WANTKEYBOARDINPUT)
2393 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2394 MAKEWPARAM(charW, descr->focus_item),
2396 if (caret == -2) return 0;
2399 caret = LISTBOX_FindString( hwnd, descr, descr->focus_item, str, FALSE);
2402 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2403 LISTBOX_SetSelection( hwnd, descr, caret, TRUE, FALSE);
2404 LISTBOX_MoveCaret( hwnd, descr, caret, TRUE );
2405 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2406 SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2412 /***********************************************************************
2415 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2418 MEASUREITEMSTRUCT mis;
2421 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2424 GetClientRect( hwnd, &rect );
2425 descr->owner = GetParent( hwnd );
2426 descr->style = GetWindowLongA( hwnd, GWL_STYLE );
2427 descr->width = rect.right - rect.left;
2428 descr->height = rect.bottom - rect.top;
2429 descr->items = NULL;
2430 descr->nb_items = 0;
2431 descr->top_item = 0;
2432 descr->selected_item = -1;
2433 descr->focus_item = 0;
2434 descr->anchor_item = -1;
2435 descr->item_height = 1;
2436 descr->page_size = 1;
2437 descr->column_width = 150;
2438 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0;
2439 descr->horz_pos = 0;
2442 descr->caret_on = lphc ? FALSE : TRUE;
2443 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2444 descr->in_focus = FALSE;
2445 descr->captured = FALSE;
2447 descr->locale = 0; /* FIXME */
2450 if (is_old_app(hwnd) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2452 /* Win95 document "List Box Differences" from MSDN:
2453 If a list box in a version 3.x application has either the
2454 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2455 horizontal and vertical scroll bars.
2457 descr->style |= WS_VSCROLL | WS_HSCROLL;
2462 TRACE_(combo)("[%p]: resetting owner %p -> %p\n", hwnd, descr->owner, lphc->self );
2463 descr->owner = lphc->self;
2466 SetWindowLongA( hwnd, 0, (LONG)descr );
2468 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2470 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2471 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2472 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2473 descr->item_height = LISTBOX_SetFont( hwnd, descr, 0 );
2475 if (descr->style & LBS_OWNERDRAWFIXED)
2477 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2479 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2480 descr->item_height = lphc->fixedOwnerDrawHeight;
2484 UINT id = (UINT)GetWindowLongPtrW( hwnd, GWLP_ID );
2485 mis.CtlType = ODT_LISTBOX;
2490 mis.itemHeight = descr->item_height;
2491 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2492 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2496 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2501 /***********************************************************************
2504 static BOOL LISTBOX_Destroy( HWND hwnd, LB_DESCR *descr )
2506 LISTBOX_ResetContent( hwnd, descr );
2507 SetWindowLongA( hwnd, 0, 0 );
2508 HeapFree( GetProcessHeap(), 0, descr );
2513 /***********************************************************************
2514 * ListBoxWndProc_common
2516 static LRESULT WINAPI ListBoxWndProc_common( HWND hwnd, UINT msg,
2517 WPARAM wParam, LPARAM lParam, BOOL unicode )
2522 if (!(descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 )))
2524 if (msg == WM_CREATE)
2526 if (!LISTBOX_Create( hwnd, NULL ))
2528 TRACE("creating wnd=%p descr=%lx\n", hwnd, GetWindowLongA( hwnd, 0 ) );
2531 /* Ignore all other messages before we get a WM_CREATE */
2532 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2533 DefWindowProcA( hwnd, msg, wParam, lParam );
2536 TRACE("[%p]: msg %s wp %08x lp %08lx\n",
2537 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
2540 case LB_RESETCONTENT16:
2541 case LB_RESETCONTENT:
2542 LISTBOX_ResetContent( hwnd, descr );
2543 LISTBOX_UpdateScroll( hwnd, descr );
2544 InvalidateRect( hwnd, NULL, TRUE );
2547 case LB_ADDSTRING16:
2548 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2554 if(unicode || !HAS_STRINGS(descr))
2555 textW = (LPWSTR)lParam;
2558 LPSTR textA = (LPSTR)lParam;
2559 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2560 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2561 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2563 wParam = LISTBOX_FindStringPos( hwnd, descr, textW, FALSE );
2564 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2565 if (!unicode && HAS_STRINGS(descr))
2566 HeapFree(GetProcessHeap(), 0, textW);
2570 case LB_INSERTSTRING16:
2571 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2572 wParam = (INT)(INT16)wParam;
2574 case LB_INSERTSTRING:
2578 if(unicode || !HAS_STRINGS(descr))
2579 textW = (LPWSTR)lParam;
2582 LPSTR textA = (LPSTR)lParam;
2583 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2584 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2585 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2587 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2588 if(!unicode && HAS_STRINGS(descr))
2589 HeapFree(GetProcessHeap(), 0, textW);
2594 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2600 if(unicode || !HAS_STRINGS(descr))
2601 textW = (LPWSTR)lParam;
2604 LPSTR textA = (LPSTR)lParam;
2605 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2606 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2607 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2609 wParam = LISTBOX_FindFileStrPos( hwnd, descr, textW );
2610 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2611 if(!unicode && HAS_STRINGS(descr))
2612 HeapFree(GetProcessHeap(), 0, textW);
2616 case LB_DELETESTRING16:
2617 case LB_DELETESTRING:
2618 if (LISTBOX_RemoveItem( hwnd, descr, wParam) != LB_ERR)
2619 return descr->nb_items;
2623 case LB_GETITEMDATA16:
2624 case LB_GETITEMDATA:
2625 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2627 return descr->items[wParam].data;
2629 case LB_SETITEMDATA16:
2630 case LB_SETITEMDATA:
2631 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2633 descr->items[wParam].data = (DWORD)lParam;
2638 return descr->nb_items;
2641 lParam = (LPARAM)MapSL(lParam);
2644 return LISTBOX_GetText( descr, wParam, lParam, unicode );
2646 case LB_GETTEXTLEN16:
2649 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2651 if (!HAS_STRINGS(descr)) return sizeof(DWORD);
2652 if (unicode) return strlenW( descr->items[wParam].str );
2653 return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
2654 strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
2656 case LB_GETCURSEL16:
2658 if (descr->nb_items==0)
2660 if (!IS_MULTISELECT(descr))
2661 return descr->selected_item;
2663 if (descr->selected_item!=-1)
2664 return descr->selected_item;
2666 return descr->focus_item;
2667 /* otherwise, if the user tries to move the selection with the */
2668 /* arrow keys, we will give the application something to choke on */
2669 case LB_GETTOPINDEX16:
2670 case LB_GETTOPINDEX:
2671 return descr->top_item;
2673 case LB_GETITEMHEIGHT16:
2674 case LB_GETITEMHEIGHT:
2675 return LISTBOX_GetItemHeight( descr, wParam );
2677 case LB_SETITEMHEIGHT16:
2678 lParam = LOWORD(lParam);
2680 case LB_SETITEMHEIGHT:
2681 return LISTBOX_SetItemHeight( hwnd, descr, wParam, lParam, TRUE );
2683 case LB_ITEMFROMPOINT:
2688 pt.x = LOWORD(lParam);
2689 pt.y = HIWORD(lParam);
2692 rect.right = descr->width;
2693 rect.bottom = descr->height;
2695 return MAKELONG( LISTBOX_GetItemFromPoint(descr, pt.x, pt.y),
2696 !PtInRect( &rect, pt ) );
2699 case LB_SETCARETINDEX16:
2700 case LB_SETCARETINDEX:
2701 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2702 if (LISTBOX_SetCaretIndex( hwnd, descr, wParam, !lParam ) == LB_ERR)
2709 case LB_GETCARETINDEX16:
2710 case LB_GETCARETINDEX:
2711 return descr->focus_item;
2713 case LB_SETTOPINDEX16:
2714 case LB_SETTOPINDEX:
2715 return LISTBOX_SetTopItem( hwnd, descr, wParam, TRUE );
2717 case LB_SETCOLUMNWIDTH16:
2718 case LB_SETCOLUMNWIDTH:
2719 return LISTBOX_SetColumnWidth( hwnd, descr, wParam );
2721 case LB_GETITEMRECT16:
2724 RECT16 *r16 = MapSL(lParam);
2725 ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2726 r16->left = rect.left;
2727 r16->top = rect.top;
2728 r16->right = rect.right;
2729 r16->bottom = rect.bottom;
2733 case LB_GETITEMRECT:
2734 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2736 case LB_FINDSTRING16:
2737 wParam = (INT)(INT16)wParam;
2738 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2744 if(unicode || !HAS_STRINGS(descr))
2745 textW = (LPWSTR)lParam;
2748 LPSTR textA = (LPSTR)lParam;
2749 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2750 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2751 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2753 ret = LISTBOX_FindString( hwnd, descr, wParam, textW, FALSE );
2754 if(!unicode && HAS_STRINGS(descr))
2755 HeapFree(GetProcessHeap(), 0, textW);
2759 case LB_FINDSTRINGEXACT16:
2760 wParam = (INT)(INT16)wParam;
2761 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2763 case LB_FINDSTRINGEXACT:
2767 if(unicode || !HAS_STRINGS(descr))
2768 textW = (LPWSTR)lParam;
2771 LPSTR textA = (LPSTR)lParam;
2772 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2773 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2774 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2776 ret = LISTBOX_FindString( hwnd, descr, wParam, textW, TRUE );
2777 if(!unicode && HAS_STRINGS(descr))
2778 HeapFree(GetProcessHeap(), 0, textW);
2782 case LB_SELECTSTRING16:
2783 wParam = (INT)(INT16)wParam;
2784 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2786 case LB_SELECTSTRING:
2791 if(HAS_STRINGS(descr))
2792 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2793 debugstr_a((LPSTR)lParam));
2794 if(unicode || !HAS_STRINGS(descr))
2795 textW = (LPWSTR)lParam;
2798 LPSTR textA = (LPSTR)lParam;
2799 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2800 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2801 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2803 index = LISTBOX_FindString( hwnd, descr, wParam, textW, FALSE );
2804 if(!unicode && HAS_STRINGS(descr))
2805 HeapFree(GetProcessHeap(), 0, textW);
2806 if (index != LB_ERR)
2808 LISTBOX_MoveCaret( hwnd, descr, index, TRUE );
2809 LISTBOX_SetSelection( hwnd, descr, index, TRUE, FALSE );
2815 wParam = (INT)(INT16)wParam;
2818 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2820 return descr->items[wParam].selected;
2823 lParam = (INT)(INT16)lParam;
2826 return LISTBOX_SetSelection( hwnd, descr, lParam, wParam, FALSE );
2828 case LB_SETCURSEL16:
2829 wParam = (INT)(INT16)wParam;
2832 if (IS_MULTISELECT(descr)) return LB_ERR;
2833 LISTBOX_SetCaretIndex( hwnd, descr, wParam, TRUE );
2834 return LISTBOX_SetSelection( hwnd, descr, wParam, TRUE, FALSE );
2836 case LB_GETSELCOUNT16:
2837 case LB_GETSELCOUNT:
2838 return LISTBOX_GetSelCount( descr );
2840 case LB_GETSELITEMS16:
2841 return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2843 case LB_GETSELITEMS:
2844 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2846 case LB_SELITEMRANGE16:
2847 case LB_SELITEMRANGE:
2848 if (LOWORD(lParam) <= HIWORD(lParam))
2849 return LISTBOX_SelectItemRange( hwnd, descr, LOWORD(lParam),
2850 HIWORD(lParam), wParam );
2852 return LISTBOX_SelectItemRange( hwnd, descr, HIWORD(lParam),
2853 LOWORD(lParam), wParam );
2855 case LB_SELITEMRANGEEX16:
2856 case LB_SELITEMRANGEEX:
2857 if ((INT)lParam >= (INT)wParam)
2858 return LISTBOX_SelectItemRange( hwnd, descr, wParam, lParam, TRUE );
2860 return LISTBOX_SelectItemRange( hwnd, descr, lParam, wParam, FALSE);
2862 case LB_GETHORIZONTALEXTENT16:
2863 case LB_GETHORIZONTALEXTENT:
2864 return descr->horz_extent;
2866 case LB_SETHORIZONTALEXTENT16:
2867 case LB_SETHORIZONTALEXTENT:
2868 return LISTBOX_SetHorizontalExtent( hwnd, descr, wParam );
2870 case LB_GETANCHORINDEX16:
2871 case LB_GETANCHORINDEX:
2872 return descr->anchor_item;
2874 case LB_SETANCHORINDEX16:
2875 wParam = (INT)(INT16)wParam;
2877 case LB_SETANCHORINDEX:
2878 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2880 descr->anchor_item = (INT)wParam;
2884 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2885 * be set automatically (this is different in Win32) */
2886 if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2887 lParam = (LPARAM)MapSL(lParam);
2894 textW = (LPWSTR)lParam;
2897 LPSTR textA = (LPSTR)lParam;
2898 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2899 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2900 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2902 ret = LISTBOX_Directory( hwnd, descr, wParam, textW, msg == LB_DIR );
2904 HeapFree(GetProcessHeap(), 0, textW);
2909 return descr->locale;
2912 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2915 case LB_INITSTORAGE:
2916 return LISTBOX_InitStorage( hwnd, descr, wParam );
2919 return LISTBOX_SetCount( hwnd, descr, (INT)wParam );
2921 case LB_SETTABSTOPS16:
2922 return LISTBOX_SetTabStops( hwnd, descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2924 case LB_SETTABSTOPS:
2925 return LISTBOX_SetTabStops( hwnd, descr, wParam, (LPINT)lParam, FALSE );
2929 if (descr->caret_on)
2931 descr->caret_on = TRUE;
2932 if ((descr->focus_item != -1) && (descr->in_focus))
2933 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2938 if (!descr->caret_on)
2940 descr->caret_on = FALSE;
2941 if ((descr->focus_item != -1) && (descr->in_focus))
2942 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2946 return LISTBOX_Destroy( hwnd, descr );
2949 InvalidateRect( hwnd, NULL, TRUE );
2953 LISTBOX_SetRedraw( hwnd, descr, wParam != 0 );
2957 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2962 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( hwnd, &ps );
2963 ret = LISTBOX_Paint( hwnd, descr, hdc );
2964 if( !wParam ) EndPaint( hwnd, &ps );
2968 LISTBOX_UpdateSize( hwnd, descr );
2971 return (LRESULT)descr->font;
2973 LISTBOX_SetFont( hwnd, descr, (HFONT)wParam );
2974 if (lParam) InvalidateRect( hwnd, 0, TRUE );
2977 descr->in_focus = TRUE;
2978 descr->caret_on = TRUE;
2979 if (descr->focus_item != -1)
2980 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2981 SEND_NOTIFICATION( hwnd, descr, LBN_SETFOCUS );
2984 descr->in_focus = FALSE;
2985 if ((descr->focus_item != -1) && descr->caret_on)
2986 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2987 SEND_NOTIFICATION( hwnd, descr, LBN_KILLFOCUS );
2990 return LISTBOX_HandleHScroll( hwnd, descr, wParam );
2992 return LISTBOX_HandleVScroll( hwnd, descr, wParam );
2994 if (wParam & (MK_SHIFT | MK_CONTROL))
2995 return DefWindowProcW( hwnd, msg, wParam, lParam );
2996 return LISTBOX_HandleMouseWheel( hwnd, descr, wParam );
2997 case WM_LBUTTONDOWN:
2998 return LISTBOX_HandleLButtonDown( hwnd, descr, wParam,
2999 (INT16)LOWORD(lParam),
3000 (INT16)HIWORD(lParam) );
3001 case WM_LBUTTONDBLCLK:
3002 if (descr->style & LBS_NOTIFY)
3003 SEND_NOTIFICATION( hwnd, descr, LBN_DBLCLK );
3006 if (GetCapture() == hwnd)
3007 LISTBOX_HandleMouseMove( hwnd, descr, (INT16)LOWORD(lParam),
3008 (INT16)HIWORD(lParam) );
3011 return LISTBOX_HandleLButtonUp( hwnd, descr );
3013 return LISTBOX_HandleKeyDown( hwnd, descr, wParam );
3018 charW = (WCHAR)wParam;
3021 CHAR charA = (CHAR)wParam;
3022 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
3024 return LISTBOX_HandleChar( hwnd, descr, charW );
3027 return LISTBOX_HandleSystemTimer( hwnd, descr );
3029 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
3032 HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
3033 wParam, (LPARAM)hwnd );
3034 TRACE("hbrush = %p\n", hbrush);
3036 hbrush = GetSysColorBrush(COLOR_WINDOW);
3039 GetClientRect(hwnd, &rect);
3040 FillRect((HDC)wParam, &rect, hbrush);
3046 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3047 SendMessageA( descr->owner, msg, wParam, lParam );
3051 if ((msg >= WM_USER) && (msg < 0xc000))
3052 WARN("[%p]: unknown msg %04x wp %08x lp %08lx\n",
3053 hwnd, msg, wParam, lParam );
3054 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3055 DefWindowProcA( hwnd, msg, wParam, lParam );
3060 /***********************************************************************
3063 * This is just a wrapper for the real wndproc, it only does window locking
3066 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3068 if (!IsWindow(hwnd)) return 0;
3069 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3072 /***********************************************************************
3075 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3077 if (!IsWindow(hwnd)) return 0;
3078 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE );
3081 /***********************************************************************
3082 * ComboLBWndProc_common
3084 * The real combo listbox wndproc
3086 static LRESULT WINAPI ComboLBWndProc_common( HWND hwnd, UINT msg,
3087 WPARAM wParam, LPARAM lParam, BOOL unicode )
3093 if (!(descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 )))
3095 if (msg == WM_CREATE)
3097 CREATESTRUCTA *lpcs = (CREATESTRUCTA *)lParam;
3098 TRACE_(combo)("\tpassed parent handle = %p\n",lpcs->lpCreateParams);
3099 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
3100 return LISTBOX_Create( hwnd, lphc );
3102 /* Ignore all other messages before we get a WM_CREATE */
3103 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3104 DefWindowProcA( hwnd, msg, wParam, lParam );
3107 TRACE_(combo)("[%p]: msg %s wp %08x lp %08lx\n",
3108 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
3110 if ((lphc = descr->lphc) != NULL)
3115 if ( (CB_GETTYPE(lphc) != CBS_SIMPLE) )
3121 mousePos.x = (INT16)LOWORD(lParam);
3122 mousePos.y = (INT16)HIWORD(lParam);
3125 * If we are in a dropdown combobox, we simulate that
3126 * the mouse is captured to show the tracking of the item.
3128 GetClientRect(hwnd, &clientRect);
3130 if (PtInRect( &clientRect, mousePos ))
3132 captured = descr->captured;
3133 descr->captured = TRUE;
3135 LISTBOX_HandleMouseMove( hwnd, descr,
3136 mousePos.x, mousePos.y);
3138 descr->captured = captured;
3143 LISTBOX_HandleMouseMove( hwnd, descr,
3144 mousePos.x, mousePos.y);
3158 * If the mouse button "up" is not in the listbox,
3159 * we make sure there is no selection by re-selecting the
3160 * item that was selected when the listbox was made visible.
3162 mousePos.x = (INT16)LOWORD(lParam);
3163 mousePos.y = (INT16)HIWORD(lParam);
3165 GetClientRect(hwnd, &clientRect);
3168 * When the user clicks outside the combobox and the focus
3169 * is lost, the owning combobox will send a fake buttonup with
3170 * 0xFFFFFFF as the mouse location, we must also revert the
3171 * selection to the original selection.
3173 if ( (lParam == (LPARAM)-1) ||
3174 (!PtInRect( &clientRect, mousePos )) )
3176 LISTBOX_MoveCaret( hwnd, descr, lphc->droppedIndex, FALSE );
3179 return LISTBOX_HandleLButtonUp( hwnd, descr );
3180 case WM_LBUTTONDBLCLK:
3181 case WM_LBUTTONDOWN:
3182 return LISTBOX_HandleLButtonDownCombo(hwnd, descr, msg, wParam,
3183 (INT16)LOWORD(lParam),
3184 (INT16)HIWORD(lParam) );
3188 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3190 /* for some reason(?) Windows makes it possible to
3191 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3193 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3194 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3195 && (wParam == VK_DOWN || wParam == VK_UP)) )
3197 COMBO_FlipListbox( lphc, FALSE, FALSE );
3201 return LISTBOX_HandleKeyDown( hwnd, descr, wParam );
3203 case LB_SETCURSEL16:
3205 lRet = unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3206 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3207 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
3210 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3216 /* default handling: call listbox wnd proc */
3217 lRet = unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3218 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3220 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
3225 /***********************************************************************
3228 * NOTE: in Windows, winproc address of the ComboLBox is the same
3229 * as that of the Listbox.
3231 LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3233 if (!IsWindow(hwnd)) return 0;
3234 return ComboLBWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3237 /***********************************************************************
3240 LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3242 if (!IsWindow(hwnd)) return 0;
3243 return ComboLBWndProc_common( hwnd, msg, wParam, lParam, TRUE );