4 * Copyright 1996 Alexandre Julliard
12 #include "wine/winuser16.h"
13 #include "wine/winbase16.h"
14 #include "wine/unicode.h"
20 #include "debugtools.h"
22 DEFAULT_DEBUG_CHANNEL(listbox);
23 DECLARE_DEBUG_CHANNEL(combo);
29 * Probably needs improvement:
33 /* Items array granularity */
34 #define LB_ARRAY_GRANULARITY 16
36 /* Scrolling timeout in ms */
37 #define LB_SCROLL_TIMEOUT 50
39 /* Listbox system timer id */
42 /* flag listbox changed while setredraw false - internal style */
43 #define LBS_DISPLAYCHANGED 0x80000000
48 LPWSTR str; /* Item text */
49 BOOL selected; /* Is item selected? */
50 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
51 DWORD data; /* User data */
54 /* Listbox structure */
57 HWND owner; /* Owner window to send notifications to */
58 UINT style; /* Window style */
59 INT width; /* Window width */
60 INT height; /* Window height */
61 LB_ITEMDATA *items; /* Array of items */
62 INT nb_items; /* Number of items */
63 INT top_item; /* Top visible item */
64 INT selected_item; /* Selected item */
65 INT focus_item; /* Item that has the focus */
66 INT anchor_item; /* Anchor item for extended selection */
67 INT item_height; /* Default item height */
68 INT page_size; /* Items per listbox page */
69 INT column_width; /* Column width for multi-column listboxes */
70 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
71 INT horz_pos; /* Horizontal position */
72 INT nb_tabs; /* Number of tabs in array */
73 INT *tabs; /* Array of tabs */
74 BOOL caret_on; /* Is caret on? */
75 BOOL captured; /* Is mouse captured? */
77 HFONT font; /* Current font */
78 LCID locale; /* Current locale for string comparisons */
79 LPHEADCOMBO lphc; /* ComboLBox */
83 #define IS_OWNERDRAW(descr) \
84 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
86 #define HAS_STRINGS(descr) \
87 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
90 #define IS_MULTISELECT(descr) \
91 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
93 #define SEND_NOTIFICATION(hwnd,descr,code) \
94 (SendMessageW( (descr)->owner, WM_COMMAND, \
95 MAKEWPARAM( GetWindowLongA((hwnd),GWL_ID), (code)), (hwnd) ))
97 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
99 /* Current timer status */
109 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
111 static LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
112 static LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
113 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
114 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
116 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect );
118 /*********************************************************************
119 * listbox class descriptor
121 const struct builtin_class_descr LISTBOX_builtin_class =
123 "ListBox", /* name */
124 CS_GLOBALCLASS | CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
125 ListBoxWndProcA, /* procA */
126 ListBoxWndProcW, /* procW */
127 sizeof(LB_DESCR *), /* extra */
128 IDC_ARROWA, /* cursor */
133 /*********************************************************************
134 * combolbox class descriptor
136 const struct builtin_class_descr COMBOLBOX_builtin_class =
138 "ComboLBox", /* name */
139 CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS, /* style */
140 ComboLBWndProcA, /* procA */
141 ComboLBWndProcW, /* procW */
142 sizeof(LB_DESCR *), /* extra */
143 IDC_ARROWA, /* cursor */
148 /* check whether app is a Win 3.1 app */
149 inline static BOOL is_old_app( HWND hwnd )
151 return (GetExpWinVer16( GetWindowLongA(hwnd,GWL_HINSTANCE) ) & 0xFF00 ) == 0x0300;
155 /***********************************************************************
158 void LISTBOX_Dump( HWND hwnd )
162 LB_DESCR *descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 );
164 TRACE( "Listbox:\n" );
165 TRACE( "hwnd=%04x descr=%08x items=%d top=%d\n",
166 hwnd, (UINT)descr, descr->nb_items,
168 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
170 TRACE( "%4d: %-40s %d %08lx %3d\n",
171 i, debugstr_w(item->str), item->selected, item->data, item->height );
176 /***********************************************************************
177 * LISTBOX_GetCurrentPageSize
179 * Return the current page size
181 static INT LISTBOX_GetCurrentPageSize( LB_DESCR *descr )
184 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
185 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
187 if ((height += descr->items[i].height) > descr->height) break;
189 if (i == descr->top_item) return 1;
190 else return i - descr->top_item;
194 /***********************************************************************
195 * LISTBOX_GetMaxTopIndex
197 * Return the maximum possible index for the top of the listbox.
199 static INT LISTBOX_GetMaxTopIndex( LB_DESCR *descr )
203 if (descr->style & LBS_OWNERDRAWVARIABLE)
205 page = descr->height;
206 for (max = descr->nb_items - 1; max >= 0; max--)
207 if ((page -= descr->items[max].height) < 0) break;
208 if (max < descr->nb_items - 1) max++;
210 else if (descr->style & LBS_MULTICOLUMN)
212 if ((page = descr->width / descr->column_width) < 1) page = 1;
213 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
214 max = (max - page) * descr->page_size;
218 max = descr->nb_items - descr->page_size;
220 if (max < 0) max = 0;
225 /***********************************************************************
226 * LISTBOX_UpdateScroll
228 * Update the scrollbars. Should be called whenever the content
229 * of the listbox changes.
231 static void LISTBOX_UpdateScroll( HWND hwnd, LB_DESCR *descr )
235 /* Check the listbox scroll bar flags individually before we call
236 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
237 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
238 scroll bar when we do not need one.
239 if (!(descr->style & WS_VSCROLL)) return;
242 /* It is important that we check descr->style, and not wnd->dwStyle,
243 for WS_VSCROLL, as the former is exactly the one passed in
244 argument to CreateWindow.
245 In Windows (and from now on in Wine :) a listbox created
246 with such a style (no WS_SCROLL) does not update
247 the scrollbar with listbox-related data, thus letting
248 the programmer use it for his/her own purposes. */
250 if (descr->style & LBS_NOREDRAW) return;
251 info.cbSize = sizeof(info);
253 if (descr->style & LBS_MULTICOLUMN)
256 info.nMax = (descr->nb_items - 1) / descr->page_size;
257 info.nPos = descr->top_item / descr->page_size;
258 info.nPage = descr->width / descr->column_width;
259 if (info.nPage < 1) info.nPage = 1;
260 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
261 if (descr->style & LBS_DISABLENOSCROLL)
262 info.fMask |= SIF_DISABLENOSCROLL;
263 if (descr->style & WS_HSCROLL)
264 SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
266 info.fMask = SIF_RANGE;
267 if (descr->style & WS_VSCROLL)
268 SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
273 info.nMax = descr->nb_items - 1;
274 info.nPos = descr->top_item;
275 info.nPage = LISTBOX_GetCurrentPageSize( descr );
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_VSCROLL)
280 SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
282 if (descr->horz_extent)
285 info.nMax = descr->horz_extent - 1;
286 info.nPos = descr->horz_pos;
287 info.nPage = descr->width;
288 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
289 if (descr->style & LBS_DISABLENOSCROLL)
290 info.fMask |= SIF_DISABLENOSCROLL;
291 if (descr->style & WS_HSCROLL)
292 SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
298 /***********************************************************************
301 * Set the top item of the listbox, scrolling up or down if necessary.
303 static LRESULT LISTBOX_SetTopItem( HWND hwnd, LB_DESCR *descr, INT index,
306 INT max = LISTBOX_GetMaxTopIndex( descr );
307 if (index > max) index = max;
308 if (index < 0) index = 0;
309 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
310 if (descr->top_item == index) return LB_OKAY;
311 if (descr->style & LBS_MULTICOLUMN)
313 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
314 if (scroll && (abs(diff) < descr->width))
315 ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
316 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
324 if (descr->style & LBS_OWNERDRAWVARIABLE)
328 if (index > descr->top_item)
330 for (i = index - 1; i >= descr->top_item; i--)
331 diff -= descr->items[i].height;
335 for (i = index; i < descr->top_item; i++)
336 diff += descr->items[i].height;
340 diff = (descr->top_item - index) * descr->item_height;
342 if (abs(diff) < descr->height)
343 ScrollWindowEx( hwnd, 0, diff, NULL, NULL, 0, NULL,
344 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
348 if (!scroll) InvalidateRect( hwnd, NULL, TRUE );
349 descr->top_item = index;
350 LISTBOX_UpdateScroll( hwnd, descr );
355 /***********************************************************************
358 * Update the page size. Should be called when the size of
359 * the client area or the item height changes.
361 static void LISTBOX_UpdatePage( HWND hwnd, LB_DESCR *descr )
365 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
367 if (page_size == descr->page_size) return;
368 descr->page_size = page_size;
369 if (descr->style & LBS_MULTICOLUMN)
370 InvalidateRect( hwnd, NULL, TRUE );
371 LISTBOX_SetTopItem( hwnd, descr, descr->top_item, FALSE );
375 /***********************************************************************
378 * Update the size of the listbox. Should be called when the size of
379 * the client area changes.
381 static void LISTBOX_UpdateSize( HWND hwnd, LB_DESCR *descr )
385 GetClientRect( hwnd, &rect );
386 descr->width = rect.right - rect.left;
387 descr->height = rect.bottom - rect.top;
388 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
393 GetWindowRect( hwnd, &rect );
394 if(descr->item_height != 0)
395 remaining = descr->height % descr->item_height;
398 if ((descr->height > descr->item_height) && remaining)
400 if (is_old_app(hwnd))
401 { /* give a margin for error to 16 bits programs - if we need
402 less than the height of the nonclient area, round to the
403 *next* number of items */
404 int ncheight = rect.bottom - rect.top - descr->height;
405 if ((descr->item_height - remaining) <= ncheight)
406 remaining = remaining - descr->item_height;
408 TRACE("[%04x]: changing height %d -> %d\n",
409 hwnd, descr->height, descr->height - remaining );
410 SetWindowPos( hwnd, 0, 0, 0, rect.right - rect.left,
411 rect.bottom - rect.top - remaining,
412 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
416 TRACE("[%04x]: new size = %d,%d\n", hwnd, descr->width, descr->height );
417 LISTBOX_UpdatePage( hwnd, descr );
418 LISTBOX_UpdateScroll( hwnd, descr );
420 /* Invalidate the focused item so it will be repainted correctly */
421 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
423 InvalidateRect( hwnd, &rect, FALSE );
428 /***********************************************************************
429 * LISTBOX_GetItemRect
431 * Get the rectangle enclosing an item, in listbox client coordinates.
432 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
434 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect )
436 /* Index <= 0 is legal even on empty listboxes */
437 if (index && (index >= descr->nb_items)) return -1;
438 SetRect( rect, 0, 0, descr->width, descr->height );
439 if (descr->style & LBS_MULTICOLUMN)
441 INT col = (index / descr->page_size) -
442 (descr->top_item / descr->page_size);
443 rect->left += col * descr->column_width;
444 rect->right = rect->left + descr->column_width;
445 rect->top += (index % descr->page_size) * descr->item_height;
446 rect->bottom = rect->top + descr->item_height;
448 else if (descr->style & LBS_OWNERDRAWVARIABLE)
451 rect->right += descr->horz_pos;
452 if ((index >= 0) && (index < descr->nb_items))
454 if (index < descr->top_item)
456 for (i = descr->top_item-1; i >= index; i--)
457 rect->top -= descr->items[i].height;
461 for (i = descr->top_item; i < index; i++)
462 rect->top += descr->items[i].height;
464 rect->bottom = rect->top + descr->items[index].height;
470 rect->top += (index - descr->top_item) * descr->item_height;
471 rect->bottom = rect->top + descr->item_height;
472 rect->right += descr->horz_pos;
475 return ((rect->left < descr->width) && (rect->right > 0) &&
476 (rect->top < descr->height) && (rect->bottom > 0));
480 /***********************************************************************
481 * LISTBOX_GetItemFromPoint
483 * Return the item nearest from point (x,y) (in client coordinates).
485 static INT LISTBOX_GetItemFromPoint( LB_DESCR *descr, INT x, INT y )
487 INT index = descr->top_item;
489 if (!descr->nb_items) return -1; /* No items */
490 if (descr->style & LBS_OWNERDRAWVARIABLE)
495 while (index < descr->nb_items)
497 if ((pos += descr->items[index].height) > y) break;
506 if ((pos -= descr->items[index].height) <= y) break;
510 else if (descr->style & LBS_MULTICOLUMN)
512 if (y >= descr->item_height * descr->page_size) return -1;
513 if (y >= 0) index += y / descr->item_height;
514 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
515 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
519 index += (y / descr->item_height);
521 if (index < 0) return 0;
522 if (index >= descr->nb_items) return -1;
527 /***********************************************************************
532 static void LISTBOX_PaintItem( HWND hwnd, LB_DESCR *descr, HDC hdc,
533 const RECT *rect, INT index, UINT action, BOOL ignoreFocus )
535 LB_ITEMDATA *item = NULL;
536 if (index < descr->nb_items) item = &descr->items[index];
538 if (IS_OWNERDRAW(descr))
543 UINT id = GetWindowLongA( hwnd, GWL_ID );
547 if (action == ODA_FOCUS)
548 DrawFocusRect( hdc, rect );
550 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
554 /* some programs mess with the clipping region when
555 drawing the item, *and* restore the previous region
556 after they are done, so a region has better to exist
557 else everything ends clipped */
558 GetClientRect(hwnd, &r);
559 hrgn = CreateRectRgnIndirect(&r);
560 SelectClipRgn( hdc, hrgn);
561 DeleteObject( hrgn );
563 dis.CtlType = ODT_LISTBOX;
566 dis.itemAction = action;
570 if (item && item->selected) dis.itemState |= ODS_SELECTED;
571 if (!ignoreFocus && (descr->focus_item == index) &&
573 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
574 if (!IsWindowEnabled(hwnd)) dis.itemState |= ODS_DISABLED;
575 dis.itemData = item ? item->data : 0;
577 TRACE("[%04x]: drawitem %d (%s) action=%02x state=%02x rect=%d,%d-%d,%d\n",
578 hwnd, index, item ? debugstr_w(item->str) : "", action,
579 dis.itemState, rect->left, rect->top, rect->right, rect->bottom );
580 SendMessageW(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
584 COLORREF oldText = 0, oldBk = 0;
586 if (action == ODA_FOCUS)
588 DrawFocusRect( hdc, rect );
591 if (item && item->selected)
593 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
594 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
597 TRACE("[%04x]: painting %d (%s) action=%02x rect=%d,%d-%d,%d\n",
598 hwnd, index, item ? debugstr_w(item->str) : "", action,
599 rect->left, rect->top, rect->right, rect->bottom );
601 ExtTextOutW( hdc, rect->left + 1, rect->top,
602 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
603 else if (!(descr->style & LBS_USETABSTOPS))
604 ExtTextOutW( hdc, rect->left + 1, rect->top,
605 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
606 strlenW(item->str), NULL );
609 /* Output empty string to paint background in the full width. */
610 ExtTextOutW( hdc, rect->left + 1, rect->top,
611 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
612 TabbedTextOutW( hdc, rect->left + 1 , rect->top,
613 item->str, strlenW(item->str),
614 descr->nb_tabs, descr->tabs, 0);
616 if (item && item->selected)
618 SetBkColor( hdc, oldBk );
619 SetTextColor( hdc, oldText );
621 if (!ignoreFocus && (descr->focus_item == index) &&
623 (descr->in_focus)) DrawFocusRect( hdc, rect );
628 /***********************************************************************
631 * Change the redraw flag.
633 static void LISTBOX_SetRedraw( HWND hwnd, LB_DESCR *descr, BOOL on )
637 if (!(descr->style & LBS_NOREDRAW)) return;
638 descr->style &= ~LBS_NOREDRAW;
639 if (descr->style & LBS_DISPLAYCHANGED)
640 { /* page was changed while setredraw false, refresh automatically */
641 InvalidateRect(hwnd, NULL, TRUE);
642 if ((descr->top_item + descr->page_size) > descr->nb_items)
643 { /* reset top of page if less than number of items/page */
644 descr->top_item = descr->nb_items - descr->page_size;
645 if (descr->top_item < 0) descr->top_item = 0;
647 descr->style &= ~LBS_DISPLAYCHANGED;
649 LISTBOX_UpdateScroll( hwnd, descr );
651 else descr->style |= LBS_NOREDRAW;
655 /***********************************************************************
656 * LISTBOX_RepaintItem
658 * Repaint a single item synchronously.
660 static void LISTBOX_RepaintItem( HWND hwnd, LB_DESCR *descr, INT index,
666 HBRUSH hbrush, oldBrush = 0;
668 /* Do not repaint the item if the item is not visible */
669 if (!IsWindowVisible(hwnd)) return;
670 if (descr->style & LBS_NOREDRAW)
672 descr->style |= LBS_DISPLAYCHANGED;
675 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
676 if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE ))) return;
677 if (descr->font) oldFont = SelectObject( hdc, descr->font );
678 hbrush = SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
680 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
681 if (!IsWindowEnabled(hwnd))
682 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
683 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
684 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, index, action, FALSE );
685 if (oldFont) SelectObject( hdc, oldFont );
686 if (oldBrush) SelectObject( hdc, oldBrush );
687 ReleaseDC( hwnd, hdc );
691 /***********************************************************************
692 * LISTBOX_InitStorage
694 static LRESULT LISTBOX_InitStorage( HWND hwnd, LB_DESCR *descr, INT nb_items )
698 nb_items += LB_ARRAY_GRANULARITY - 1;
699 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
701 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
702 if (!(item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
703 nb_items * sizeof(LB_ITEMDATA) )))
705 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
713 /***********************************************************************
714 * LISTBOX_SetTabStops
716 static BOOL LISTBOX_SetTabStops( HWND hwnd, LB_DESCR *descr, INT count,
717 LPINT tabs, BOOL short_ints )
719 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
720 if (descr->tabs) HeapFree( GetProcessHeap(), 0, descr->tabs );
721 if (!(descr->nb_tabs = count))
726 /* FIXME: count = 1 */
727 if (!(descr->tabs = (INT *)HeapAlloc( GetProcessHeap(), 0,
728 descr->nb_tabs * sizeof(INT) )))
733 LPINT16 p = (LPINT16)tabs;
735 TRACE("[%04x]: settabstops ", hwnd );
736 for (i = 0; i < descr->nb_tabs; i++) {
737 descr->tabs[i] = *p++<<1; /* FIXME */
738 if (TRACE_ON(listbox)) DPRINTF("%hd ", descr->tabs[i]);
740 if (TRACE_ON(listbox)) DPRINTF("\n");
742 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
743 /* FIXME: repaint the window? */
748 /***********************************************************************
751 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPARAM lParam, BOOL unicode )
753 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
754 if (HAS_STRINGS(descr))
757 return strlenW(descr->items[index].str);
759 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
763 LPWSTR buffer = (LPWSTR)lParam;
764 strcpyW( buffer, descr->items[index].str );
765 return strlenW(buffer);
769 LPSTR buffer = (LPSTR)lParam;
770 return WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, buffer, 0x7FFFFFFF, NULL, NULL) - 1;
774 *((LPDWORD)lParam)=*(LPDWORD)(&descr->items[index].data);
775 return sizeof(DWORD);
780 /***********************************************************************
781 * LISTBOX_FindStringPos
783 * Find the nearest string located before a given string in sort order.
784 * If 'exact' is TRUE, return an error if we don't get an exact match.
786 static INT LISTBOX_FindStringPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str,
789 INT index, min, max, res = -1;
791 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
793 max = descr->nb_items;
796 index = (min + max) / 2;
797 if (HAS_STRINGS(descr))
798 res = lstrcmpiW( descr->items[index].str, str );
801 COMPAREITEMSTRUCT cis;
802 UINT id = GetWindowLongA( hwnd, GWL_ID );
804 cis.CtlType = ODT_LISTBOX;
808 cis.itemData1 = descr->items[index].data;
810 cis.itemData2 = (DWORD)str;
811 cis.dwLocaleId = descr->locale;
812 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
814 if (!res) return index;
815 if (res > 0) max = index;
816 else min = index + 1;
818 return exact ? -1 : max;
822 /***********************************************************************
823 * LISTBOX_FindFileStrPos
825 * Find the nearest string located before a given string in directory
826 * sort order (i.e. first files, then directories, then drives).
828 static INT LISTBOX_FindFileStrPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str )
830 INT min, max, res = -1;
832 if (!HAS_STRINGS(descr))
833 return LISTBOX_FindStringPos( hwnd, descr, str, FALSE );
835 max = descr->nb_items;
838 INT index = (min + max) / 2;
839 LPCWSTR p = descr->items[index].str;
840 if (*p == '[') /* drive or directory */
842 if (*str != '[') res = -1;
843 else if (p[1] == '-') /* drive */
845 if (str[1] == '-') res = str[2] - p[2];
850 if (str[1] == '-') res = 1;
851 else res = lstrcmpiW( str, p );
856 if (*str == '[') res = 1;
857 else res = lstrcmpiW( str, p );
859 if (!res) return index;
860 if (res < 0) max = index;
861 else min = index + 1;
867 /***********************************************************************
870 * Find the item beginning with a given string.
872 static INT LISTBOX_FindString( HWND hwnd, LB_DESCR *descr, INT start,
873 LPCWSTR str, BOOL exact )
878 if (start >= descr->nb_items) start = -1;
879 item = descr->items + start + 1;
880 if (HAS_STRINGS(descr))
882 if (!str || ! str[0] ) return LB_ERR;
885 for (i = start + 1; i < descr->nb_items; i++, item++)
886 if (!lstrcmpiW( str, item->str )) return i;
887 for (i = 0, item = descr->items; i <= start; i++, item++)
888 if (!lstrcmpiW( str, item->str )) return i;
892 /* Special case for drives and directories: ignore prefix */
893 #define CHECK_DRIVE(item) \
894 if ((item)->str[0] == '[') \
896 if (!strncmpiW( str, (item)->str+1, len )) return i; \
897 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
901 INT len = strlenW(str);
902 for (i = start + 1; i < descr->nb_items; i++, item++)
904 if (!strncmpiW( str, item->str, len )) return i;
907 for (i = 0, item = descr->items; i <= start; i++, item++)
909 if (!strncmpiW( str, item->str, len )) return i;
917 if (exact && (descr->style & LBS_SORT))
918 /* If sorted, use a WM_COMPAREITEM binary search */
919 return LISTBOX_FindStringPos( hwnd, descr, str, TRUE );
921 /* Otherwise use a linear search */
922 for (i = start + 1; i < descr->nb_items; i++, item++)
923 if (item->data == (DWORD)str) return i;
924 for (i = 0, item = descr->items; i <= start; i++, item++)
925 if (item->data == (DWORD)str) return i;
931 /***********************************************************************
932 * LISTBOX_GetSelCount
934 static LRESULT LISTBOX_GetSelCount( LB_DESCR *descr )
937 LB_ITEMDATA *item = descr->items;
939 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
940 for (i = count = 0; i < descr->nb_items; i++, item++)
941 if (item->selected) count++;
946 /***********************************************************************
947 * LISTBOX_GetSelItems16
949 static LRESULT LISTBOX_GetSelItems16( LB_DESCR *descr, INT16 max, LPINT16 array )
952 LB_ITEMDATA *item = descr->items;
954 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
955 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
956 if (item->selected) array[count++] = (INT16)i;
961 /***********************************************************************
962 * LISTBOX_GetSelItems
964 static LRESULT LISTBOX_GetSelItems( LB_DESCR *descr, INT max, LPINT array )
967 LB_ITEMDATA *item = descr->items;
969 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
970 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
971 if (item->selected) array[count++] = i;
976 /***********************************************************************
979 static LRESULT LISTBOX_Paint( HWND hwnd, LB_DESCR *descr, HDC hdc )
981 INT i, col_pos = descr->page_size - 1;
983 RECT focusRect = {-1, -1, -1, -1};
985 HBRUSH hbrush, oldBrush = 0;
987 if (descr->style & LBS_NOREDRAW) return 0;
989 SetRect( &rect, 0, 0, descr->width, descr->height );
990 if (descr->style & LBS_MULTICOLUMN)
991 rect.right = rect.left + descr->column_width;
992 else if (descr->horz_pos)
994 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
995 rect.right += descr->horz_pos;
998 if (descr->font) oldFont = SelectObject( hdc, descr->font );
999 hbrush = SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1000 hdc, (LPARAM)hwnd );
1001 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1002 if (!IsWindowEnabled(hwnd)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1004 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1007 /* Special case for empty listbox: paint focus rect */
1008 rect.bottom = rect.top + descr->item_height;
1009 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, descr->focus_item,
1011 rect.top = rect.bottom;
1014 /* Paint all the item, regarding the selection
1015 Focus state will be painted after */
1017 for (i = descr->top_item; i < descr->nb_items; i++)
1019 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1020 rect.bottom = rect.top + descr->item_height;
1022 rect.bottom = rect.top + descr->items[i].height;
1024 if (i == descr->focus_item)
1026 /* keep the focus rect, to paint the focus item after */
1027 focusRect.left = rect.left;
1028 focusRect.right = rect.right;
1029 focusRect.top = rect.top;
1030 focusRect.bottom = rect.bottom;
1032 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1033 rect.top = rect.bottom;
1035 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1037 if (!IS_OWNERDRAW(descr))
1039 /* Clear the bottom of the column */
1040 if (rect.top < descr->height)
1042 rect.bottom = descr->height;
1043 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1044 &rect, NULL, 0, NULL );
1048 /* Go to the next column */
1049 rect.left += descr->column_width;
1050 rect.right += descr->column_width;
1052 col_pos = descr->page_size - 1;
1057 if (rect.top >= descr->height) break;
1061 /* Paint the focus item now */
1062 if (focusRect.top != focusRect.bottom && descr->caret_on)
1063 LISTBOX_PaintItem( hwnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1065 if (!IS_OWNERDRAW(descr))
1067 /* Clear the remainder of the client area */
1068 if (rect.top < descr->height)
1070 rect.bottom = descr->height;
1071 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1072 &rect, NULL, 0, NULL );
1074 if (rect.right < descr->width)
1076 rect.left = rect.right;
1077 rect.right = descr->width;
1079 rect.bottom = descr->height;
1080 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1081 &rect, NULL, 0, NULL );
1084 if (oldFont) SelectObject( hdc, oldFont );
1085 if (oldBrush) SelectObject( hdc, oldBrush );
1090 /***********************************************************************
1091 * LISTBOX_InvalidateItems
1093 * Invalidate all items from a given item. If the specified item is not
1094 * visible, nothing happens.
1096 static void LISTBOX_InvalidateItems( HWND hwnd, LB_DESCR *descr, INT index )
1100 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1102 if (descr->style & LBS_NOREDRAW)
1104 descr->style |= LBS_DISPLAYCHANGED;
1107 rect.bottom = descr->height;
1108 InvalidateRect( hwnd, &rect, TRUE );
1109 if (descr->style & LBS_MULTICOLUMN)
1111 /* Repaint the other columns */
1112 rect.left = rect.right;
1113 rect.right = descr->width;
1115 InvalidateRect( hwnd, &rect, TRUE );
1121 /***********************************************************************
1122 * LISTBOX_GetItemHeight
1124 static LRESULT LISTBOX_GetItemHeight( LB_DESCR *descr, INT index )
1126 if (descr->style & LBS_OWNERDRAWVARIABLE)
1128 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1129 return descr->items[index].height;
1131 else return descr->item_height;
1135 /***********************************************************************
1136 * LISTBOX_SetItemHeight
1138 static LRESULT LISTBOX_SetItemHeight( HWND hwnd, LB_DESCR *descr, INT index,
1139 INT height, BOOL repaint )
1141 if (!height) height = 1;
1143 if (descr->style & LBS_OWNERDRAWVARIABLE)
1145 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1146 TRACE("[%04x]: item %d height = %d\n", hwnd, index, height );
1147 descr->items[index].height = height;
1148 LISTBOX_UpdateScroll( hwnd, descr );
1150 LISTBOX_InvalidateItems( hwnd, descr, index );
1152 else if (height != descr->item_height)
1154 TRACE("[%04x]: new height = %d\n", hwnd, height );
1155 descr->item_height = height;
1156 LISTBOX_UpdatePage( hwnd, descr );
1157 LISTBOX_UpdateScroll( hwnd, descr );
1159 InvalidateRect( hwnd, 0, TRUE );
1165 /***********************************************************************
1166 * LISTBOX_SetHorizontalPos
1168 static void LISTBOX_SetHorizontalPos( HWND hwnd, LB_DESCR *descr, INT pos )
1172 if (pos > descr->horz_extent - descr->width)
1173 pos = descr->horz_extent - descr->width;
1174 if (pos < 0) pos = 0;
1175 if (!(diff = descr->horz_pos - pos)) return;
1176 TRACE("[%04x]: new horz pos = %d\n", hwnd, pos );
1177 descr->horz_pos = pos;
1178 LISTBOX_UpdateScroll( hwnd, descr );
1179 if (abs(diff) < descr->width)
1180 ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
1181 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1183 InvalidateRect( hwnd, NULL, TRUE );
1187 /***********************************************************************
1188 * LISTBOX_SetHorizontalExtent
1190 static LRESULT LISTBOX_SetHorizontalExtent( HWND hwnd, LB_DESCR *descr,
1193 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1195 if (extent <= 0) extent = 1;
1196 if (extent == descr->horz_extent) return LB_OKAY;
1197 TRACE("[%04x]: new horz extent = %d\n", hwnd, extent );
1198 descr->horz_extent = extent;
1199 if (descr->horz_pos > extent - descr->width)
1200 LISTBOX_SetHorizontalPos( hwnd, descr, extent - descr->width );
1202 LISTBOX_UpdateScroll( hwnd, descr );
1207 /***********************************************************************
1208 * LISTBOX_SetColumnWidth
1210 static LRESULT LISTBOX_SetColumnWidth( HWND hwnd, LB_DESCR *descr, INT width)
1212 if (width == descr->column_width) return LB_OKAY;
1213 TRACE("[%04x]: new column width = %d\n", hwnd, width );
1214 descr->column_width = width;
1215 LISTBOX_UpdatePage( hwnd, descr );
1220 /***********************************************************************
1223 * Returns the item height.
1225 static INT LISTBOX_SetFont( HWND hwnd, LB_DESCR *descr, HFONT font )
1233 if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE )))
1235 ERR("unable to get DC.\n" );
1238 if (font) oldFont = SelectObject( hdc, font );
1239 GetTextMetricsW( hdc, &tm );
1240 if (oldFont) SelectObject( hdc, oldFont );
1241 ReleaseDC( hwnd, hdc );
1242 if (!IS_OWNERDRAW(descr))
1243 LISTBOX_SetItemHeight( hwnd, descr, 0, tm.tmHeight, FALSE );
1244 return tm.tmHeight ;
1248 /***********************************************************************
1249 * LISTBOX_MakeItemVisible
1251 * Make sure that a given item is partially or fully visible.
1253 static void LISTBOX_MakeItemVisible( HWND hwnd, LB_DESCR *descr, INT index,
1258 if (index <= descr->top_item) top = index;
1259 else if (descr->style & LBS_MULTICOLUMN)
1261 INT cols = descr->width;
1262 if (!fully) cols += descr->column_width - 1;
1263 if (cols >= descr->column_width) cols /= descr->column_width;
1265 if (index < descr->top_item + (descr->page_size * cols)) return;
1266 top = index - descr->page_size * (cols - 1);
1268 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1270 INT height = fully ? descr->items[index].height : 1;
1271 for (top = index; top > descr->top_item; top--)
1272 if ((height += descr->items[top-1].height) > descr->height) break;
1276 if (index < descr->top_item + descr->page_size) return;
1277 if (!fully && (index == descr->top_item + descr->page_size) &&
1278 (descr->height > (descr->page_size * descr->item_height))) return;
1279 top = index - descr->page_size + 1;
1281 LISTBOX_SetTopItem( hwnd, descr, top, TRUE );
1284 /***********************************************************************
1285 * LISTBOX_SetCaretIndex
1288 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1291 static LRESULT LISTBOX_SetCaretIndex( HWND hwnd, LB_DESCR *descr, INT index,
1292 BOOL fully_visible )
1294 INT oldfocus = descr->focus_item;
1296 if (descr->style & LBS_NOSEL) return LB_ERR;
1297 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1298 if (index == oldfocus) return LB_OKAY;
1299 descr->focus_item = index;
1300 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1301 LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
1303 LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
1304 if (descr->caret_on && (descr->in_focus))
1305 LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
1311 /***********************************************************************
1312 * LISTBOX_SelectItemRange
1314 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1316 static LRESULT LISTBOX_SelectItemRange( HWND hwnd, LB_DESCR *descr, INT first,
1321 /* A few sanity checks */
1323 if (descr->style & LBS_NOSEL) return LB_ERR;
1324 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1325 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1326 if (last == -1) last = descr->nb_items - 1;
1327 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1328 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1329 /* selected_item reflects last selected/unselected item on multiple sel */
1330 descr->selected_item = last;
1332 if (on) /* Turn selection on */
1334 for (i = first; i <= last; i++)
1336 if (descr->items[i].selected) continue;
1337 descr->items[i].selected = TRUE;
1338 LISTBOX_RepaintItem( hwnd, descr, i, ODA_SELECT );
1340 LISTBOX_SetCaretIndex( hwnd, descr, last, TRUE );
1342 else /* Turn selection off */
1344 for (i = first; i <= last; i++)
1346 if (!descr->items[i].selected) continue;
1347 descr->items[i].selected = FALSE;
1348 LISTBOX_RepaintItem( hwnd, descr, i, ODA_SELECT );
1354 /***********************************************************************
1355 * LISTBOX_SetSelection
1357 static LRESULT LISTBOX_SetSelection( HWND hwnd, LB_DESCR *descr, INT index,
1358 BOOL on, BOOL send_notify )
1360 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1362 if (descr->style & LBS_NOSEL) return LB_ERR;
1363 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1364 if (descr->style & LBS_MULTIPLESEL)
1366 if (index == -1) /* Select all items */
1367 return LISTBOX_SelectItemRange( hwnd, descr, 0, -1, on );
1368 else /* Only one item */
1369 return LISTBOX_SelectItemRange( hwnd, descr, index, index, on );
1373 INT oldsel = descr->selected_item;
1374 if (index == oldsel) return LB_OKAY;
1375 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1376 if (index != -1) descr->items[index].selected = TRUE;
1377 descr->selected_item = index;
1378 if (oldsel != -1) LISTBOX_RepaintItem( hwnd, descr, oldsel, ODA_SELECT );
1379 if (index != -1) LISTBOX_RepaintItem( hwnd, descr, index, ODA_SELECT );
1380 if (send_notify && descr->nb_items) SEND_NOTIFICATION( hwnd, descr,
1381 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1383 if( descr->lphc ) /* set selection change flag for parent combo */
1384 descr->lphc->wState |= CBF_SELCHANGE;
1390 /***********************************************************************
1393 * Change the caret position and extend the selection to the new caret.
1395 static void LISTBOX_MoveCaret( HWND hwnd, LB_DESCR *descr, INT index,
1396 BOOL fully_visible )
1398 INT oldfocus = descr->focus_item;
1400 if ((index < 0) || (index >= descr->nb_items))
1403 /* Important, repaint needs to be done in this order if
1404 you want to mimic Windows behavior:
1405 1. Remove the focus and paint the item
1406 2. Remove the selection and paint the item(s)
1407 3. Set the selection and repaint the item(s)
1408 4. Set the focus to 'index' and repaint the item */
1410 /* 1. remove the focus and repaint the item */
1411 descr->focus_item = -1;
1412 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1413 LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
1415 /* 2. then turn off the previous selection */
1416 /* 3. repaint the new selected item */
1417 if (descr->style & LBS_EXTENDEDSEL)
1419 if (descr->anchor_item != -1)
1421 INT first = min( index, descr->anchor_item );
1422 INT last = max( index, descr->anchor_item );
1424 LISTBOX_SelectItemRange( hwnd, descr, 0, first - 1, FALSE );
1425 LISTBOX_SelectItemRange( hwnd, descr, last + 1, -1, FALSE );
1426 LISTBOX_SelectItemRange( hwnd, descr, first, last, TRUE );
1429 else if (!(descr->style & LBS_MULTIPLESEL))
1431 /* Set selection to new caret item */
1432 LISTBOX_SetSelection( hwnd, descr, index, TRUE, FALSE );
1435 /* 4. repaint the new item with the focus */
1436 descr->focus_item = index;
1437 LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
1438 if (descr->caret_on && (descr->in_focus))
1439 LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
1443 /***********************************************************************
1444 * LISTBOX_InsertItem
1446 static LRESULT LISTBOX_InsertItem( HWND hwnd, LB_DESCR *descr, INT index,
1447 LPWSTR str, DWORD data )
1451 INT oldfocus = descr->focus_item;
1453 if (index == -1) index = descr->nb_items;
1454 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1455 if (!descr->items) max_items = 0;
1456 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1457 if (descr->nb_items == max_items)
1459 /* We need to grow the array */
1460 max_items += LB_ARRAY_GRANULARITY;
1461 if (!(item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1462 max_items * sizeof(LB_ITEMDATA) )))
1464 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
1467 descr->items = item;
1470 /* Insert the item structure */
1472 item = &descr->items[index];
1473 if (index < descr->nb_items)
1474 RtlMoveMemory( item + 1, item,
1475 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1479 item->selected = FALSE;
1482 /* Get item height */
1484 if (descr->style & LBS_OWNERDRAWVARIABLE)
1486 MEASUREITEMSTRUCT mis;
1487 UINT id = GetWindowLongA( hwnd, GWL_ID );
1489 mis.CtlType = ODT_LISTBOX;
1492 mis.itemData = descr->items[index].data;
1493 mis.itemHeight = descr->item_height;
1494 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1495 item->height = mis.itemHeight ? mis.itemHeight : 1;
1496 TRACE("[%04x]: measure item %d (%s) = %d\n",
1497 hwnd, index, str ? debugstr_w(str) : "", item->height );
1500 /* Repaint the items */
1502 LISTBOX_UpdateScroll( hwnd, descr );
1503 LISTBOX_InvalidateItems( hwnd, descr, index );
1505 /* Move selection and focused item */
1506 /* If listbox was empty, set focus to the first item */
1507 if (descr->nb_items == 1)
1508 LISTBOX_SetCaretIndex( hwnd, descr, 0, FALSE );
1509 /* single select don't change selection index in win31 */
1510 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1512 descr->selected_item++;
1513 LISTBOX_SetSelection( hwnd, descr, descr->selected_item-1, TRUE, FALSE );
1517 if (index <= descr->selected_item)
1519 descr->selected_item++;
1520 descr->focus_item = oldfocus; /* focus not changed */
1527 /***********************************************************************
1528 * LISTBOX_InsertString
1530 static LRESULT LISTBOX_InsertString( HWND hwnd, LB_DESCR *descr, INT index,
1533 LPWSTR new_str = NULL;
1537 if (HAS_STRINGS(descr))
1539 static const WCHAR empty_stringW[] = { 0 };
1540 if (!str) str = empty_stringW;
1541 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1543 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
1546 strcpyW(new_str, str);
1548 else data = (DWORD)str;
1550 if (index == -1) index = descr->nb_items;
1551 if ((ret = LISTBOX_InsertItem( hwnd, descr, index, new_str, data )) != 0)
1553 if (new_str) HeapFree( GetProcessHeap(), 0, new_str );
1557 TRACE("[%04x]: added item %d %s\n",
1558 hwnd, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1563 /***********************************************************************
1564 * LISTBOX_DeleteItem
1566 * Delete the content of an item. 'index' must be a valid index.
1568 static void LISTBOX_DeleteItem( HWND hwnd, LB_DESCR *descr, INT index )
1570 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1571 * while Win95 sends it for all items with user data.
1572 * It's probably better to send it too often than not
1573 * often enough, so this is what we do here.
1575 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1577 DELETEITEMSTRUCT dis;
1578 UINT id = GetWindowLongA( hwnd, GWL_ID );
1580 dis.CtlType = ODT_LISTBOX;
1583 dis.hwndItem = hwnd;
1584 dis.itemData = descr->items[index].data;
1585 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1587 if (HAS_STRINGS(descr) && descr->items[index].str)
1588 HeapFree( GetProcessHeap(), 0, descr->items[index].str );
1592 /***********************************************************************
1593 * LISTBOX_RemoveItem
1595 * Remove an item from the listbox and delete its content.
1597 static LRESULT LISTBOX_RemoveItem( HWND hwnd, LB_DESCR *descr, INT index )
1602 if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1603 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1605 /* We need to invalidate the original rect instead of the updated one. */
1606 LISTBOX_InvalidateItems( hwnd, descr, index );
1608 LISTBOX_DeleteItem( hwnd, descr, index );
1610 /* Remove the item */
1612 item = &descr->items[index];
1613 if (index < descr->nb_items-1)
1614 RtlMoveMemory( item, item + 1,
1615 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1617 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1619 /* Shrink the item array if possible */
1621 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1622 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1624 max_items -= LB_ARRAY_GRANULARITY;
1625 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1626 max_items * sizeof(LB_ITEMDATA) );
1627 if (item) descr->items = item;
1629 /* Repaint the items */
1631 LISTBOX_UpdateScroll( hwnd, descr );
1632 /* if we removed the scrollbar, reset the top of the list
1633 (correct for owner-drawn ???) */
1634 if (descr->nb_items == descr->page_size)
1635 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1637 /* Move selection and focused item */
1638 if (!IS_MULTISELECT(descr))
1640 if (index == descr->selected_item)
1641 descr->selected_item = -1;
1642 else if (index < descr->selected_item)
1644 descr->selected_item--;
1645 if (ISWIN31) /* win 31 do not change the selected item number */
1646 LISTBOX_SetSelection( hwnd, descr, descr->selected_item + 1, TRUE, FALSE);
1650 if (descr->focus_item >= descr->nb_items)
1652 descr->focus_item = descr->nb_items - 1;
1653 if (descr->focus_item < 0) descr->focus_item = 0;
1659 /***********************************************************************
1660 * LISTBOX_ResetContent
1662 static void LISTBOX_ResetContent( HWND hwnd, LB_DESCR *descr )
1666 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( hwnd, descr, i );
1667 if (descr->items) HeapFree( GetProcessHeap(), 0, descr->items );
1668 descr->nb_items = 0;
1669 descr->top_item = 0;
1670 descr->selected_item = -1;
1671 descr->focus_item = 0;
1672 descr->anchor_item = -1;
1673 descr->items = NULL;
1677 /***********************************************************************
1680 static LRESULT LISTBOX_SetCount( HWND hwnd, LB_DESCR *descr, INT count )
1684 if (HAS_STRINGS(descr)) return LB_ERR;
1685 /* FIXME: this is far from optimal... */
1686 if (count > descr->nb_items)
1688 while (count > descr->nb_items)
1689 if ((ret = LISTBOX_InsertString( hwnd, descr, -1, 0 )) < 0)
1692 else if (count < descr->nb_items)
1694 while (count < descr->nb_items)
1695 if ((ret = LISTBOX_RemoveItem( hwnd, descr, -1 )) < 0)
1702 /***********************************************************************
1705 static LRESULT LISTBOX_Directory( HWND hwnd, LB_DESCR *descr, UINT attrib,
1706 LPCWSTR filespec, BOOL long_names )
1709 LRESULT ret = LB_OKAY;
1710 WIN32_FIND_DATAW entry;
1713 /* don't scan directory if we just want drives exclusively */
1714 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1715 /* scan directory */
1716 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1718 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1725 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1727 static const WCHAR bracketW[] = { ']',0 };
1728 static const WCHAR dotW[] = { '.',0 };
1729 if (!(attrib & DDL_DIRECTORY) ||
1730 !strcmpW( entry.cAlternateFileName, dotW )) continue;
1732 if (long_names) strcpyW( buffer + 1, entry.cFileName );
1733 else strcpyW( buffer + 1, entry.cAlternateFileName );
1734 strcatW(buffer, bracketW);
1736 else /* not a directory */
1738 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1739 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1741 if ((attrib & DDL_EXCLUSIVE) &&
1742 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1745 if (long_names) strcpyW( buffer, entry.cFileName );
1746 else strcpyW( buffer, entry.cAlternateFileName );
1748 if (!long_names) CharLowerW( buffer );
1749 pos = LISTBOX_FindFileStrPos( hwnd, descr, buffer );
1750 if ((ret = LISTBOX_InsertString( hwnd, descr, pos, buffer )) < 0)
1752 } while (FindNextFileW( handle, &entry ));
1753 FindClose( handle );
1758 if ((ret >= 0) && (attrib & DDL_DRIVES))
1760 WCHAR buffer[] = {'[','-','a','-',']',0};
1761 WCHAR root[] = {'A',':','\\',0};
1763 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1765 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1766 if ((ret = LISTBOX_InsertString( hwnd, descr, -1, buffer )) < 0)
1774 /***********************************************************************
1775 * LISTBOX_HandleVScroll
1777 static LRESULT LISTBOX_HandleVScroll( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1781 if (descr->style & LBS_MULTICOLUMN) return 0;
1782 switch(LOWORD(wParam))
1785 LISTBOX_SetTopItem( hwnd, descr, descr->top_item - 1, TRUE );
1788 LISTBOX_SetTopItem( hwnd, descr, descr->top_item + 1, TRUE );
1791 LISTBOX_SetTopItem( hwnd, descr, descr->top_item -
1792 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1795 LISTBOX_SetTopItem( hwnd, descr, descr->top_item +
1796 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1798 case SB_THUMBPOSITION:
1799 LISTBOX_SetTopItem( hwnd, descr, HIWORD(wParam), TRUE );
1802 info.cbSize = sizeof(info);
1803 info.fMask = SIF_TRACKPOS;
1804 GetScrollInfo( hwnd, SB_VERT, &info );
1805 LISTBOX_SetTopItem( hwnd, descr, info.nTrackPos, TRUE );
1808 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1811 LISTBOX_SetTopItem( hwnd, descr, descr->nb_items, TRUE );
1818 /***********************************************************************
1819 * LISTBOX_HandleHScroll
1821 static LRESULT LISTBOX_HandleHScroll( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1826 if (descr->style & LBS_MULTICOLUMN)
1828 switch(LOWORD(wParam))
1831 LISTBOX_SetTopItem( hwnd, descr, descr->top_item-descr->page_size,
1835 LISTBOX_SetTopItem( hwnd, descr, descr->top_item+descr->page_size,
1839 page = descr->width / descr->column_width;
1840 if (page < 1) page = 1;
1841 LISTBOX_SetTopItem( hwnd, descr,
1842 descr->top_item - page * descr->page_size, TRUE );
1845 page = descr->width / descr->column_width;
1846 if (page < 1) page = 1;
1847 LISTBOX_SetTopItem( hwnd, descr,
1848 descr->top_item + page * descr->page_size, TRUE );
1850 case SB_THUMBPOSITION:
1851 LISTBOX_SetTopItem( hwnd, descr, HIWORD(wParam)*descr->page_size,
1855 info.cbSize = sizeof(info);
1856 info.fMask = SIF_TRACKPOS;
1857 GetScrollInfo( hwnd, SB_VERT, &info );
1858 LISTBOX_SetTopItem( hwnd, descr, info.nTrackPos*descr->page_size,
1862 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1865 LISTBOX_SetTopItem( hwnd, descr, descr->nb_items, TRUE );
1869 else if (descr->horz_extent)
1871 switch(LOWORD(wParam))
1874 LISTBOX_SetHorizontalPos( hwnd, descr, descr->horz_pos - 1 );
1877 LISTBOX_SetHorizontalPos( hwnd, descr, descr->horz_pos + 1 );
1880 LISTBOX_SetHorizontalPos( hwnd, descr,
1881 descr->horz_pos - descr->width );
1884 LISTBOX_SetHorizontalPos( hwnd, descr,
1885 descr->horz_pos + descr->width );
1887 case SB_THUMBPOSITION:
1888 LISTBOX_SetHorizontalPos( hwnd, descr, HIWORD(wParam) );
1891 info.cbSize = sizeof(info);
1892 info.fMask = SIF_TRACKPOS;
1893 GetScrollInfo( hwnd, SB_HORZ, &info );
1894 LISTBOX_SetHorizontalPos( hwnd, descr, info.nTrackPos );
1897 LISTBOX_SetHorizontalPos( hwnd, descr, 0 );
1900 LISTBOX_SetHorizontalPos( hwnd, descr,
1901 descr->horz_extent - descr->width );
1908 static LRESULT LISTBOX_HandleMouseWheel(HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1910 short gcWheelDelta = 0;
1911 UINT pulScrollLines = 3;
1913 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1915 gcWheelDelta -= (short) HIWORD(wParam);
1917 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1919 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1920 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1921 LISTBOX_SetTopItem( hwnd, descr, descr->top_item + cLineScroll, TRUE );
1926 /***********************************************************************
1927 * LISTBOX_HandleLButtonDown
1929 static LRESULT LISTBOX_HandleLButtonDown( HWND hwnd, LB_DESCR *descr,
1930 WPARAM wParam, INT x, INT y )
1932 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
1933 TRACE("[%04x]: lbuttondown %d,%d item %d\n", hwnd, x, y, index );
1934 if (!descr->caret_on && (descr->in_focus)) return 0;
1936 if (!descr->in_focus)
1938 if( !descr->lphc ) SetFocus( hwnd );
1939 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
1942 if (index == -1) return 0;
1944 if (descr->style & LBS_EXTENDEDSEL)
1946 /* we should perhaps make sure that all items are deselected
1947 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1948 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1949 LISTBOX_SetSelection( hwnd, descr, -1, FALSE, FALSE);
1952 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1953 if (wParam & MK_CONTROL)
1955 LISTBOX_SetCaretIndex( hwnd, descr, index, FALSE );
1956 LISTBOX_SetSelection( hwnd, descr, index,
1957 !descr->items[index].selected,
1958 (descr->style & LBS_NOTIFY) != 0);
1960 else LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
1964 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
1965 LISTBOX_SetSelection( hwnd, descr, index,
1966 (!(descr->style & LBS_MULTIPLESEL) ||
1967 !descr->items[index].selected),
1968 (descr->style & LBS_NOTIFY) != 0 );
1971 descr->captured = TRUE;
1976 if (descr->style & LBS_NOTIFY )
1977 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
1978 MAKELPARAM( x, y ) );
1979 if (GetWindowLongA( hwnd, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
1986 if (DragDetect( hwnd, pt ))
1987 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
1994 /*************************************************************************
1995 * LISTBOX_HandleLButtonDownCombo [Internal]
1997 * Process LButtonDown message for the ComboListBox
2000 * pWnd [I] The windows internal structure
2001 * pDescr [I] The ListBox internal structure
2002 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2003 * x [I] X Mouse Coordinate
2004 * y [I] Y Mouse Coordinate
2007 * 0 since we are processing the WM_LBUTTONDOWN Message
2010 * This function is only to be used when a ListBox is a ComboListBox
2013 static LRESULT LISTBOX_HandleLButtonDownCombo( HWND hwnd, LB_DESCR *pDescr,
2014 UINT msg, WPARAM wParam, INT x, INT y)
2016 RECT clientRect, screenRect;
2022 GetClientRect(hwnd, &clientRect);
2024 if(PtInRect(&clientRect, mousePos))
2026 /* MousePos is in client, resume normal processing */
2027 if (msg == WM_LBUTTONDOWN)
2029 pDescr->lphc->droppedIndex = pDescr->nb_items ? pDescr->selected_item : -1;
2030 return LISTBOX_HandleLButtonDown( hwnd, pDescr, wParam, x, y);
2032 else if (pDescr->style & LBS_NOTIFY)
2033 SEND_NOTIFICATION( hwnd, pDescr, LBN_DBLCLK );
2038 POINT screenMousePos;
2039 HWND hWndOldCapture;
2041 /* Check the Non-Client Area */
2042 screenMousePos = mousePos;
2043 hWndOldCapture = GetCapture();
2045 GetWindowRect(hwnd, &screenRect);
2046 ClientToScreen(hwnd, &screenMousePos);
2048 if(!PtInRect(&screenRect, screenMousePos))
2050 LISTBOX_SetCaretIndex( hwnd, pDescr, pDescr->lphc->droppedIndex, FALSE );
2051 LISTBOX_SetSelection( hwnd, pDescr, pDescr->lphc->droppedIndex, FALSE, FALSE );
2052 COMBO_FlipListbox( pDescr->lphc, FALSE, FALSE );
2057 /* Check to see the NC is a scrollbar */
2059 LONG style = GetWindowLongA( hwnd, GWL_STYLE );
2060 /* Check Vertical scroll bar */
2061 if (style & WS_VSCROLL)
2063 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2064 if (PtInRect( &clientRect, mousePos ))
2066 nHitTestType = HTVSCROLL;
2069 /* Check horizontal scroll bar */
2070 if (style & WS_HSCROLL)
2072 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2073 if (PtInRect( &clientRect, mousePos ))
2075 nHitTestType = HTHSCROLL;
2078 /* Windows sends this message when a scrollbar is clicked
2081 if(nHitTestType != 0)
2083 SendMessageW(hwnd, WM_NCLBUTTONDOWN, nHitTestType,
2084 MAKELONG(screenMousePos.x, screenMousePos.y));
2086 /* Resume the Capture after scrolling is complete
2088 if(hWndOldCapture != 0)
2090 SetCapture(hWndOldCapture);
2097 /***********************************************************************
2098 * LISTBOX_HandleLButtonUp
2100 static LRESULT LISTBOX_HandleLButtonUp( HWND hwnd, LB_DESCR *descr )
2102 if (LISTBOX_Timer != LB_TIMER_NONE)
2103 KillSystemTimer( hwnd, LB_TIMER_ID );
2104 LISTBOX_Timer = LB_TIMER_NONE;
2105 if (descr->captured)
2107 descr->captured = FALSE;
2108 if (GetCapture() == hwnd) ReleaseCapture();
2109 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2110 SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2116 /***********************************************************************
2117 * LISTBOX_HandleTimer
2119 * Handle scrolling upon a timer event.
2120 * Return TRUE if scrolling should continue.
2122 static LRESULT LISTBOX_HandleTimer( HWND hwnd, LB_DESCR *descr,
2123 INT index, TIMER_DIRECTION dir )
2128 if (descr->top_item) index = descr->top_item - 1;
2132 if (descr->top_item) index -= descr->page_size;
2135 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2136 if (index == descr->focus_item) index++;
2137 if (index >= descr->nb_items) index = descr->nb_items - 1;
2139 case LB_TIMER_RIGHT:
2140 if (index + descr->page_size < descr->nb_items)
2141 index += descr->page_size;
2146 if (index == descr->focus_item) return FALSE;
2147 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
2152 /***********************************************************************
2153 * LISTBOX_HandleSystemTimer
2155 * WM_SYSTIMER handler.
2157 static LRESULT LISTBOX_HandleSystemTimer( HWND hwnd, LB_DESCR *descr )
2159 if (!LISTBOX_HandleTimer( hwnd, descr, descr->focus_item, LISTBOX_Timer ))
2161 KillSystemTimer( hwnd, LB_TIMER_ID );
2162 LISTBOX_Timer = LB_TIMER_NONE;
2168 /***********************************************************************
2169 * LISTBOX_HandleMouseMove
2171 * WM_MOUSEMOVE handler.
2173 static void LISTBOX_HandleMouseMove( HWND hwnd, LB_DESCR *descr,
2177 TIMER_DIRECTION dir = LB_TIMER_NONE;
2179 if (!descr->captured) return;
2181 if (descr->style & LBS_MULTICOLUMN)
2184 else if (y >= descr->item_height * descr->page_size)
2185 y = descr->item_height * descr->page_size - 1;
2189 dir = LB_TIMER_LEFT;
2192 else if (x >= descr->width)
2194 dir = LB_TIMER_RIGHT;
2195 x = descr->width - 1;
2200 if (y < 0) dir = LB_TIMER_UP; /* above */
2201 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2204 index = LISTBOX_GetItemFromPoint( descr, x, y );
2205 if (index == -1) index = descr->focus_item;
2206 if (!LISTBOX_HandleTimer( hwnd, descr, index, dir )) dir = LB_TIMER_NONE;
2208 /* Start/stop the system timer */
2210 if (dir != LB_TIMER_NONE)
2211 SetSystemTimer( hwnd, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2212 else if (LISTBOX_Timer != LB_TIMER_NONE)
2213 KillSystemTimer( hwnd, LB_TIMER_ID );
2214 LISTBOX_Timer = dir;
2218 /***********************************************************************
2219 * LISTBOX_HandleKeyDown
2221 static LRESULT LISTBOX_HandleKeyDown( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
2224 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2225 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2226 bForceSelection = FALSE; /* only for single select list */
2228 if (descr->style & LBS_WANTKEYBOARDINPUT)
2230 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2231 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2233 if (caret == -2) return 0;
2235 if (caret == -1) switch(wParam)
2238 if (descr->style & LBS_MULTICOLUMN)
2240 bForceSelection = FALSE;
2241 if (descr->focus_item >= descr->page_size)
2242 caret = descr->focus_item - descr->page_size;
2247 caret = descr->focus_item - 1;
2248 if (caret < 0) caret = 0;
2251 if (descr->style & LBS_MULTICOLUMN)
2253 bForceSelection = FALSE;
2254 if (descr->focus_item + descr->page_size < descr->nb_items)
2255 caret = descr->focus_item + descr->page_size;
2260 caret = descr->focus_item + 1;
2261 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2265 if (descr->style & LBS_MULTICOLUMN)
2267 INT page = descr->width / descr->column_width;
2268 if (page < 1) page = 1;
2269 caret = descr->focus_item - (page * descr->page_size) + 1;
2271 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2272 if (caret < 0) caret = 0;
2275 if (descr->style & LBS_MULTICOLUMN)
2277 INT page = descr->width / descr->column_width;
2278 if (page < 1) page = 1;
2279 caret = descr->focus_item + (page * descr->page_size) - 1;
2281 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2282 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2288 caret = descr->nb_items - 1;
2291 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2292 else if (descr->style & LBS_MULTIPLESEL)
2294 LISTBOX_SetSelection( hwnd, descr, descr->focus_item,
2295 !descr->items[descr->focus_item].selected,
2296 (descr->style & LBS_NOTIFY) != 0 );
2300 bForceSelection = FALSE;
2302 if (bForceSelection) /* focused item is used instead of key */
2303 caret = descr->focus_item;
2306 if ((descr->style & LBS_EXTENDEDSEL) &&
2307 !(GetKeyState( VK_SHIFT ) & 0x8000))
2308 descr->anchor_item = caret;
2309 LISTBOX_MoveCaret( hwnd, descr, caret, TRUE );
2310 LISTBOX_SetSelection( hwnd, descr, caret, TRUE, FALSE);
2311 if (descr->style & LBS_NOTIFY)
2315 /* make sure that combo parent doesn't hide us */
2316 descr->lphc->wState |= CBF_NOROLLUP;
2318 if (descr->nb_items) SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2325 /***********************************************************************
2326 * LISTBOX_HandleChar
2328 static LRESULT LISTBOX_HandleChar( HWND hwnd, LB_DESCR *descr, WCHAR charW )
2336 if (descr->style & LBS_WANTKEYBOARDINPUT)
2338 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2339 MAKEWPARAM(charW, descr->focus_item),
2341 if (caret == -2) return 0;
2344 caret = LISTBOX_FindString( hwnd, descr, descr->focus_item, str, FALSE);
2347 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2348 LISTBOX_SetSelection( hwnd, descr, caret, TRUE, FALSE);
2349 LISTBOX_MoveCaret( hwnd, descr, caret, TRUE );
2350 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2351 SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2357 /***********************************************************************
2360 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2363 MEASUREITEMSTRUCT mis;
2366 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2369 GetClientRect( hwnd, &rect );
2370 descr->owner = GetParent( hwnd );
2371 descr->style = GetWindowLongA( hwnd, GWL_STYLE );
2372 descr->width = rect.right - rect.left;
2373 descr->height = rect.bottom - rect.top;
2374 descr->items = NULL;
2375 descr->nb_items = 0;
2376 descr->top_item = 0;
2377 descr->selected_item = -1;
2378 descr->focus_item = 0;
2379 descr->anchor_item = -1;
2380 descr->item_height = 1;
2381 descr->page_size = 1;
2382 descr->column_width = 150;
2383 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0;
2384 descr->horz_pos = 0;
2387 descr->caret_on = lphc ? FALSE : TRUE;
2388 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2389 descr->in_focus = FALSE;
2390 descr->captured = FALSE;
2392 descr->locale = 0; /* FIXME */
2395 if (is_old_app(hwnd) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2397 /* Win95 document "List Box Differences" from MSDN:
2398 If a list box in a version 3.x application has either the
2399 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2400 horizontal and vertical scroll bars.
2402 descr->style |= WS_VSCROLL | WS_HSCROLL;
2407 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2408 hwnd, descr->owner, lphc->self );
2409 descr->owner = lphc->self;
2412 SetWindowLongA( hwnd, 0, (LONG)descr );
2414 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2416 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2417 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2418 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2419 descr->item_height = LISTBOX_SetFont( hwnd, descr, 0 );
2421 if (descr->style & LBS_OWNERDRAWFIXED)
2423 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2425 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2426 descr->item_height = lphc->fixedOwnerDrawHeight;
2430 UINT id = GetWindowLongA( hwnd, GWL_ID );
2431 mis.CtlType = ODT_LISTBOX;
2436 mis.itemHeight = descr->item_height;
2437 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2438 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2442 TRACE("owner: %04x, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2447 /***********************************************************************
2450 static BOOL LISTBOX_Destroy( HWND hwnd, LB_DESCR *descr )
2452 LISTBOX_ResetContent( hwnd, descr );
2453 SetWindowLongA( hwnd, 0, 0 );
2454 HeapFree( GetProcessHeap(), 0, descr );
2459 /***********************************************************************
2460 * ListBoxWndProc_common
2462 static LRESULT WINAPI ListBoxWndProc_common( HWND hwnd, UINT msg,
2463 WPARAM wParam, LPARAM lParam, BOOL unicode )
2468 if (!(descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 )))
2470 if (msg == WM_CREATE)
2472 if (!LISTBOX_Create( hwnd, NULL ))
2474 TRACE("creating wnd=%04x descr=%lx\n", hwnd, GetWindowLongA( hwnd, 0 ) );
2477 /* Ignore all other messages before we get a WM_CREATE */
2478 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2479 DefWindowProcA( hwnd, msg, wParam, lParam );
2482 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2483 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
2486 case LB_RESETCONTENT16:
2487 case LB_RESETCONTENT:
2488 LISTBOX_ResetContent( hwnd, descr );
2489 LISTBOX_UpdateScroll( hwnd, descr );
2490 InvalidateRect( hwnd, NULL, TRUE );
2493 case LB_ADDSTRING16:
2494 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2500 if(unicode || !HAS_STRINGS(descr))
2501 textW = (LPWSTR)lParam;
2504 LPSTR textA = (LPSTR)lParam;
2505 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2506 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2507 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2509 wParam = LISTBOX_FindStringPos( hwnd, descr, textW, FALSE );
2510 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2511 if (!unicode && HAS_STRINGS(descr))
2512 HeapFree(GetProcessHeap(), 0, textW);
2516 case LB_INSERTSTRING16:
2517 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2518 wParam = (INT)(INT16)wParam;
2520 case LB_INSERTSTRING:
2524 if(unicode || !HAS_STRINGS(descr))
2525 textW = (LPWSTR)lParam;
2528 LPSTR textA = (LPSTR)lParam;
2529 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2530 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2531 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2533 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2534 if(!unicode && HAS_STRINGS(descr))
2535 HeapFree(GetProcessHeap(), 0, textW);
2540 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2546 if(unicode || !HAS_STRINGS(descr))
2547 textW = (LPWSTR)lParam;
2550 LPSTR textA = (LPSTR)lParam;
2551 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2552 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2553 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2555 wParam = LISTBOX_FindFileStrPos( hwnd, descr, textW );
2556 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2557 if(!unicode && HAS_STRINGS(descr))
2558 HeapFree(GetProcessHeap(), 0, textW);
2562 case LB_DELETESTRING16:
2563 case LB_DELETESTRING:
2564 if (LISTBOX_RemoveItem( hwnd, descr, wParam) != LB_ERR)
2565 return descr->nb_items;
2569 case LB_GETITEMDATA16:
2570 case LB_GETITEMDATA:
2571 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2573 return descr->items[wParam].data;
2575 case LB_SETITEMDATA16:
2576 case LB_SETITEMDATA:
2577 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2579 descr->items[wParam].data = (DWORD)lParam;
2584 return descr->nb_items;
2587 lParam = (LPARAM)MapSL(lParam);
2590 return LISTBOX_GetText( descr, wParam, lParam, unicode );
2592 case LB_GETTEXTLEN16:
2595 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2597 return (HAS_STRINGS(descr) ? strlenW(descr->items[wParam].str)
2600 case LB_GETCURSEL16:
2602 if (descr->nb_items==0)
2604 if (!IS_MULTISELECT(descr))
2605 return descr->selected_item;
2607 if (descr->selected_item!=-1)
2608 return descr->selected_item;
2610 return descr->focus_item;
2611 /* otherwise, if the user tries to move the selection with the */
2612 /* arrow keys, we will give the application something to choke on */
2613 case LB_GETTOPINDEX16:
2614 case LB_GETTOPINDEX:
2615 return descr->top_item;
2617 case LB_GETITEMHEIGHT16:
2618 case LB_GETITEMHEIGHT:
2619 return LISTBOX_GetItemHeight( descr, wParam );
2621 case LB_SETITEMHEIGHT16:
2622 lParam = LOWORD(lParam);
2624 case LB_SETITEMHEIGHT:
2625 return LISTBOX_SetItemHeight( hwnd, descr, wParam, lParam, TRUE );
2627 case LB_ITEMFROMPOINT:
2632 pt.x = LOWORD(lParam);
2633 pt.y = HIWORD(lParam);
2636 rect.right = descr->width;
2637 rect.bottom = descr->height;
2639 return MAKELONG( LISTBOX_GetItemFromPoint(descr, pt.x, pt.y),
2640 !PtInRect( &rect, pt ) );
2643 case LB_SETCARETINDEX16:
2644 case LB_SETCARETINDEX:
2645 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2646 if (LISTBOX_SetCaretIndex( hwnd, descr, wParam, !lParam ) == LB_ERR)
2653 case LB_GETCARETINDEX16:
2654 case LB_GETCARETINDEX:
2655 return descr->focus_item;
2657 case LB_SETTOPINDEX16:
2658 case LB_SETTOPINDEX:
2659 return LISTBOX_SetTopItem( hwnd, descr, wParam, TRUE );
2661 case LB_SETCOLUMNWIDTH16:
2662 case LB_SETCOLUMNWIDTH:
2663 return LISTBOX_SetColumnWidth( hwnd, descr, wParam );
2665 case LB_GETITEMRECT16:
2668 ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2669 CONV_RECT32TO16( &rect, MapSL(lParam) );
2673 case LB_GETITEMRECT:
2674 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2676 case LB_FINDSTRING16:
2677 wParam = (INT)(INT16)wParam;
2678 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2684 if(unicode || !HAS_STRINGS(descr))
2685 textW = (LPWSTR)lParam;
2688 LPSTR textA = (LPSTR)lParam;
2689 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2690 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2691 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2693 ret = LISTBOX_FindString( hwnd, descr, wParam, textW, FALSE );
2694 if(!unicode && HAS_STRINGS(descr))
2695 HeapFree(GetProcessHeap(), 0, textW);
2699 case LB_FINDSTRINGEXACT16:
2700 wParam = (INT)(INT16)wParam;
2701 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2703 case LB_FINDSTRINGEXACT:
2707 if(unicode || !HAS_STRINGS(descr))
2708 textW = (LPWSTR)lParam;
2711 LPSTR textA = (LPSTR)lParam;
2712 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2713 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2714 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2716 ret = LISTBOX_FindString( hwnd, descr, wParam, textW, TRUE );
2717 if(!unicode && HAS_STRINGS(descr))
2718 HeapFree(GetProcessHeap(), 0, textW);
2722 case LB_SELECTSTRING16:
2723 wParam = (INT)(INT16)wParam;
2724 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2726 case LB_SELECTSTRING:
2731 if(HAS_STRINGS(descr))
2732 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2733 debugstr_a((LPSTR)lParam));
2734 if(unicode || !HAS_STRINGS(descr))
2735 textW = (LPWSTR)lParam;
2738 LPSTR textA = (LPSTR)lParam;
2739 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2740 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2741 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2743 index = LISTBOX_FindString( hwnd, descr, wParam, textW, FALSE );
2744 if(!unicode && HAS_STRINGS(descr))
2745 HeapFree(GetProcessHeap(), 0, textW);
2746 if (index != LB_ERR)
2748 LISTBOX_SetCaretIndex( hwnd, descr, index, TRUE );
2749 LISTBOX_SetSelection( hwnd, descr, index, TRUE, FALSE );
2755 wParam = (INT)(INT16)wParam;
2758 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2760 return descr->items[wParam].selected;
2763 lParam = (INT)(INT16)lParam;
2766 return LISTBOX_SetSelection( hwnd, descr, lParam, wParam, FALSE );
2768 case LB_SETCURSEL16:
2769 wParam = (INT)(INT16)wParam;
2772 if (IS_MULTISELECT(descr)) return LB_ERR;
2773 LISTBOX_SetCaretIndex( hwnd, descr, wParam, TRUE );
2774 return LISTBOX_SetSelection( hwnd, descr, wParam, TRUE, FALSE );
2776 case LB_GETSELCOUNT16:
2777 case LB_GETSELCOUNT:
2778 return LISTBOX_GetSelCount( descr );
2780 case LB_GETSELITEMS16:
2781 return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2783 case LB_GETSELITEMS:
2784 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2786 case LB_SELITEMRANGE16:
2787 case LB_SELITEMRANGE:
2788 if (LOWORD(lParam) <= HIWORD(lParam))
2789 return LISTBOX_SelectItemRange( hwnd, descr, LOWORD(lParam),
2790 HIWORD(lParam), wParam );
2792 return LISTBOX_SelectItemRange( hwnd, descr, HIWORD(lParam),
2793 LOWORD(lParam), wParam );
2795 case LB_SELITEMRANGEEX16:
2796 case LB_SELITEMRANGEEX:
2797 if ((INT)lParam >= (INT)wParam)
2798 return LISTBOX_SelectItemRange( hwnd, descr, wParam, lParam, TRUE );
2800 return LISTBOX_SelectItemRange( hwnd, descr, lParam, wParam, FALSE);
2802 case LB_GETHORIZONTALEXTENT16:
2803 case LB_GETHORIZONTALEXTENT:
2804 return descr->horz_extent;
2806 case LB_SETHORIZONTALEXTENT16:
2807 case LB_SETHORIZONTALEXTENT:
2808 return LISTBOX_SetHorizontalExtent( hwnd, descr, wParam );
2810 case LB_GETANCHORINDEX16:
2811 case LB_GETANCHORINDEX:
2812 return descr->anchor_item;
2814 case LB_SETANCHORINDEX16:
2815 wParam = (INT)(INT16)wParam;
2817 case LB_SETANCHORINDEX:
2818 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2820 descr->anchor_item = (INT)wParam;
2824 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2825 * be set automatically (this is different in Win32) */
2826 if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2827 lParam = (LPARAM)MapSL(lParam);
2834 textW = (LPWSTR)lParam;
2837 LPSTR textA = (LPSTR)lParam;
2838 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2839 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2840 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2842 ret = LISTBOX_Directory( hwnd, descr, wParam, textW, msg == LB_DIR );
2844 HeapFree(GetProcessHeap(), 0, textW);
2849 return descr->locale;
2852 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2855 case LB_INITSTORAGE:
2856 return LISTBOX_InitStorage( hwnd, descr, wParam );
2859 return LISTBOX_SetCount( hwnd, descr, (INT)wParam );
2861 case LB_SETTABSTOPS16:
2862 return LISTBOX_SetTabStops( hwnd, descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2864 case LB_SETTABSTOPS:
2865 return LISTBOX_SetTabStops( hwnd, descr, wParam, (LPINT)lParam, FALSE );
2869 if (descr->caret_on)
2871 descr->caret_on = TRUE;
2872 if ((descr->focus_item != -1) && (descr->in_focus))
2873 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2878 if (!descr->caret_on)
2880 descr->caret_on = FALSE;
2881 if ((descr->focus_item != -1) && (descr->in_focus))
2882 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2886 return LISTBOX_Destroy( hwnd, descr );
2889 InvalidateRect( hwnd, NULL, TRUE );
2893 LISTBOX_SetRedraw( hwnd, descr, wParam != 0 );
2897 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2902 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( hwnd, &ps );
2903 ret = LISTBOX_Paint( hwnd, descr, hdc );
2904 if( !wParam ) EndPaint( hwnd, &ps );
2908 LISTBOX_UpdateSize( hwnd, descr );
2913 LISTBOX_SetFont( hwnd, descr, (HFONT)wParam );
2914 if (lParam) InvalidateRect( hwnd, 0, TRUE );
2917 descr->in_focus = TRUE;
2918 descr->caret_on = TRUE;
2919 if (descr->focus_item != -1)
2920 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2921 SEND_NOTIFICATION( hwnd, descr, LBN_SETFOCUS );
2924 descr->in_focus = FALSE;
2925 if ((descr->focus_item != -1) && descr->caret_on)
2926 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2927 SEND_NOTIFICATION( hwnd, descr, LBN_KILLFOCUS );
2930 return LISTBOX_HandleHScroll( hwnd, descr, wParam );
2932 return LISTBOX_HandleVScroll( hwnd, descr, wParam );
2934 if (wParam & (MK_SHIFT | MK_CONTROL))
2935 return DefWindowProcW( hwnd, msg, wParam, lParam );
2936 return LISTBOX_HandleMouseWheel( hwnd, descr, wParam );
2937 case WM_LBUTTONDOWN:
2938 return LISTBOX_HandleLButtonDown( hwnd, descr, wParam,
2939 (INT16)LOWORD(lParam),
2940 (INT16)HIWORD(lParam) );
2941 case WM_LBUTTONDBLCLK:
2942 if (descr->style & LBS_NOTIFY)
2943 SEND_NOTIFICATION( hwnd, descr, LBN_DBLCLK );
2946 if (GetCapture() == hwnd)
2947 LISTBOX_HandleMouseMove( hwnd, descr, (INT16)LOWORD(lParam),
2948 (INT16)HIWORD(lParam) );
2951 return LISTBOX_HandleLButtonUp( hwnd, descr );
2953 return LISTBOX_HandleKeyDown( hwnd, descr, wParam );
2958 charW = (WCHAR)wParam;
2961 CHAR charA = (CHAR)wParam;
2962 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
2964 return LISTBOX_HandleChar( hwnd, descr, charW );
2967 return LISTBOX_HandleSystemTimer( hwnd, descr );
2969 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
2972 HBRUSH hbrush = SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
2973 wParam, (LPARAM)hwnd );
2974 TRACE("hbrush = %04x\n", hbrush);
2976 hbrush = GetSysColorBrush(COLOR_WINDOW);
2979 GetClientRect(hwnd, &rect);
2980 FillRect((HDC)wParam, &rect, hbrush);
2986 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
2987 SendMessageA( descr->owner, msg, wParam, lParam );
2991 case WM_QUERYDROPOBJECT:
2996 LPDRAGINFO16 dragInfo = MapSL( lParam );
2997 dragInfo->l = LISTBOX_GetItemFromPoint( descr, dragInfo->pt.x,
2999 return SendMessage16( descr->owner, msg, wParam, lParam );
3004 if ((msg >= WM_USER) && (msg < 0xc000))
3005 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
3006 hwnd, msg, wParam, lParam );
3007 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3008 DefWindowProcA( hwnd, msg, wParam, lParam );
3013 /***********************************************************************
3016 * This is just a wrapper for the real wndproc, it only does window locking
3019 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3021 if (!IsWindow(hwnd)) return 0;
3022 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3025 /***********************************************************************
3028 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3030 if (!IsWindow(hwnd)) return 0;
3031 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE );
3034 /***********************************************************************
3035 * ComboLBWndProc_common
3037 * The real combo listbox wndproc
3039 static LRESULT WINAPI ComboLBWndProc_common( HWND hwnd, UINT msg,
3040 WPARAM wParam, LPARAM lParam, BOOL unicode )
3043 LB_DESCR *descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 );
3045 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
3046 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
3048 if( descr || msg == WM_CREATE )
3050 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
3056 CREATESTRUCTA *lpcs = (CREATESTRUCTA *)lParam;
3057 TRACE_(combo)("\tpassed parent handle = %p\n",lpcs->lpCreateParams);
3058 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
3059 return LISTBOX_Create( hwnd, lphc );
3062 if ( (TWEAK_WineLook > WIN31_LOOK) &&
3063 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
3069 mousePos.x = (INT16)LOWORD(lParam);
3070 mousePos.y = (INT16)HIWORD(lParam);
3073 * If we are in a dropdown combobox, we simulate that
3074 * the mouse is captured to show the tracking of the item.
3076 GetClientRect(hwnd, &clientRect);
3078 if (PtInRect( &clientRect, mousePos ))
3080 captured = descr->captured;
3081 descr->captured = TRUE;
3083 LISTBOX_HandleMouseMove( hwnd, descr,
3084 mousePos.x, mousePos.y);
3086 descr->captured = captured;
3091 LISTBOX_HandleMouseMove( hwnd, descr,
3092 mousePos.x, mousePos.y);
3101 * If we are in Win3.1 look, go with the default behavior.
3103 return unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3104 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3107 if (TWEAK_WineLook > WIN31_LOOK)
3113 * If the mouse button "up" is not in the listbox,
3114 * we make sure there is no selection by re-selecting the
3115 * item that was selected when the listbox was made visible.
3117 mousePos.x = (INT16)LOWORD(lParam);
3118 mousePos.y = (INT16)HIWORD(lParam);
3120 GetClientRect(hwnd, &clientRect);
3123 * When the user clicks outside the combobox and the focus
3124 * is lost, the owning combobox will send a fake buttonup with
3125 * 0xFFFFFFF as the mouse location, we must also revert the
3126 * selection to the original selection.
3128 if ( (lParam == (LPARAM)-1) ||
3129 (!PtInRect( &clientRect, mousePos )) )
3131 LISTBOX_MoveCaret( hwnd, descr, lphc->droppedIndex, FALSE );
3134 return LISTBOX_HandleLButtonUp( hwnd, descr );
3135 case WM_LBUTTONDBLCLK:
3136 case WM_LBUTTONDOWN:
3137 return LISTBOX_HandleLButtonDownCombo(hwnd, descr, msg, wParam,
3138 (INT16)LOWORD(lParam),
3139 (INT16)HIWORD(lParam) );
3143 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3145 /* for some reason(?) Windows makes it possible to
3146 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3148 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3149 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3150 && (wParam == VK_DOWN || wParam == VK_UP)) )
3152 COMBO_FlipListbox( lphc, FALSE, FALSE );
3156 return LISTBOX_HandleKeyDown( hwnd, descr, wParam );
3158 case LB_SETCURSEL16:
3160 lRet = unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3161 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3162 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
3165 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3170 return unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3171 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3174 lRet = unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3175 DefWindowProcA( hwnd, msg, wParam, lParam );
3177 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
3182 /***********************************************************************
3185 * NOTE: in Windows, winproc address of the ComboLBox is the same
3186 * as that of the Listbox.
3188 LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3190 if (!IsWindow(hwnd)) return 0;
3191 return ComboLBWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3194 /***********************************************************************
3197 LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3199 if (!IsWindow(hwnd)) return 0;
3200 return ComboLBWndProc_common( hwnd, msg, wParam, lParam, TRUE );