4 * Copyright 1997 Alex Korobka
6 * FIXME: roll up in Netscape 3.01.
15 #include "wine/winuser16.h"
21 #include "debugtools.h"
23 DEFAULT_DEBUG_CHANNEL(combo);
25 /* bits in the dwKeyData */
26 #define KEYDATA_ALT 0x2000
27 #define KEYDATA_PREVSTATE 0x4000
30 * Additional combo box definitions
33 #define CB_GETPTR( wnd ) (*(LPHEADCOMBO*)((wnd)->wExtra))
34 #define CB_NOTIFY( lphc, code ) \
35 (SendMessageW((lphc)->owner, WM_COMMAND, \
36 MAKEWPARAM((lphc)->self->wIDmenu, (code)), (lphc)->self->hwndSelf))
37 #define CB_GETEDITTEXTLENGTH( lphc ) \
38 (SendMessageW((lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ))
40 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
45 static HBITMAP hComboBmp = 0;
46 static UINT CBitHeight, CBitWidth;
49 * Look and feel dependant "constants"
52 #define COMBO_YBORDERGAP 5
53 #define COMBO_XBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
54 #define COMBO_YBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
55 #define COMBO_EDITBUTTONSPACE() ( (TWEAK_WineLook == WIN31_LOOK) ? 8 : 0 )
56 #define EDIT_CONTROL_PADDING() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 1 )
58 static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
59 static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
61 /*********************************************************************
62 * combo class descriptor
64 const struct builtin_class_descr COMBO_builtin_class =
66 "ComboBox", /* name */
67 CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS, /* style */
68 ComboWndProcA, /* procA */
69 ComboWndProcW, /* procW */
70 sizeof(HEADCOMBO *), /* extra */
71 IDC_ARROWA, /* cursor */
76 /***********************************************************************
79 * Load combo button bitmap.
81 static BOOL COMBO_Init()
85 if( hComboBmp ) return TRUE;
86 if( (hDC = CreateCompatibleDC(0)) )
89 if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
95 GetObjectW( hComboBmp, sizeof(bm), &bm );
96 CBitHeight = bm.bmHeight;
97 CBitWidth = bm.bmWidth;
99 TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
101 hPrevB = SelectObject( hDC, hComboBmp);
102 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
103 InvertRect( hDC, &r );
104 SelectObject( hDC, hPrevB );
113 /***********************************************************************
116 static LRESULT COMBO_NCCreate(WND* wnd, LONG style)
120 if ( wnd && COMBO_Init() &&
121 (lphc = HeapAlloc(GetProcessHeap(), 0, sizeof(HEADCOMBO))) )
123 memset( lphc, 0, sizeof(HEADCOMBO) );
124 *(LPHEADCOMBO*)wnd->wExtra = lphc;
126 /* some braindead apps do try to use scrollbar/border flags */
128 lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
129 wnd->dwStyle &= ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
132 * We also have to remove the client edge style to make sure
133 * we don't end-up with a non client area.
135 wnd->dwExStyle &= ~(WS_EX_CLIENTEDGE);
137 if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
138 lphc->dwStyle |= CBS_HASSTRINGS;
139 if( !(wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) )
140 lphc->wState |= CBF_NOTIFY;
142 TRACE("[0x%08x], style = %08x\n",
143 (UINT)lphc, lphc->dwStyle );
145 return (LRESULT)(UINT)wnd->hwndSelf;
147 return (LRESULT)FALSE;
150 /***********************************************************************
153 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
158 WND* wnd = lphc->self;
160 TRACE("[%04x]: freeing storage\n", CB_HWND(lphc));
162 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
163 DestroyWindow( lphc->hWndLBox );
165 HeapFree( GetProcessHeap(), 0, lphc );
171 /***********************************************************************
172 * CBGetTextAreaHeight
174 * This method will calculate the height of the text area of the
176 * The height of the text area is set in two ways.
177 * It can be set explicitly through a combobox message or through a
178 * WM_MEASUREITEM callback.
179 * If this is not the case, the height is set to 13 dialog units.
180 * This height was determined through experimentation.
182 static INT CBGetTextAreaHeight(
188 if( lphc->editHeight ) /* explicitly set height */
190 iTextItemHeight = lphc->editHeight;
195 HDC hDC = GetDC(hwnd);
200 hPrevFont = SelectObject( hDC, lphc->hFont );
202 GetTextMetricsW(hDC, &tm);
204 baseUnitY = tm.tmHeight;
207 SelectObject( hDC, hPrevFont );
209 ReleaseDC(hwnd, hDC);
211 iTextItemHeight = ((13 * baseUnitY) / 8);
214 * This "formula" calculates the height of the complete control.
215 * To calculate the height of the text area, we have to remove the
218 iTextItemHeight -= 2*COMBO_YBORDERSIZE();
222 * Check the ownerdraw case if we haven't asked the parent the size
225 if ( CB_OWNERDRAWN(lphc) &&
226 (lphc->wState & CBF_MEASUREITEM) )
228 MEASUREITEMSTRUCT measureItem;
230 INT originalItemHeight = iTextItemHeight;
233 * We use the client rect for the width of the item.
235 GetClientRect(hwnd, &clientRect);
237 lphc->wState &= ~CBF_MEASUREITEM;
240 * Send a first one to measure the size of the text area
242 measureItem.CtlType = ODT_COMBOBOX;
243 measureItem.CtlID = lphc->self->wIDmenu;
244 measureItem.itemID = -1;
245 measureItem.itemWidth = clientRect.right;
246 measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
247 measureItem.itemData = 0;
248 SendMessageW(lphc->owner, WM_MEASUREITEM,
249 (WPARAM)measureItem.CtlID, (LPARAM)&measureItem);
250 iTextItemHeight = 6 + measureItem.itemHeight;
253 * Send a second one in the case of a fixed ownerdraw list to calculate the
254 * size of the list items. (we basically do this on behalf of the listbox)
256 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
258 measureItem.CtlType = ODT_COMBOBOX;
259 measureItem.CtlID = lphc->self->wIDmenu;
260 measureItem.itemID = 0;
261 measureItem.itemWidth = clientRect.right;
262 measureItem.itemHeight = originalItemHeight;
263 measureItem.itemData = 0;
264 SendMessageW(lphc->owner, WM_MEASUREITEM,
265 (WPARAM)measureItem.CtlID, (LPARAM)&measureItem);
266 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
270 * Keep the size for the next time
272 lphc->editHeight = iTextItemHeight;
275 return iTextItemHeight;
278 /***********************************************************************
281 * The dummy resize is used for listboxes that have a popup to trigger
282 * a re-arranging of the contents of the combobox and the recalculation
283 * of the size of the "real" control window.
285 static void CBForceDummyResize(
291 newComboHeight = CBGetTextAreaHeight(CB_HWND(lphc),lphc) + 2*COMBO_YBORDERSIZE();
293 GetWindowRect(CB_HWND(lphc), &windowRect);
296 * We have to be careful, resizing a combobox also has the meaning that the
297 * dropped rect will be resized. In this case, we want to trigger a resize
298 * to recalculate layout but we don't want to change the dropped rectangle
299 * So, we pass the height of text area of control as the height.
300 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
303 SetWindowPos( CB_HWND(lphc),
306 windowRect.right - windowRect.left,
308 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
311 /***********************************************************************
314 * Set up component coordinates given valid lphc->RectCombo.
316 static void CBCalcPlacement(
324 * Again, start with the client rectangle.
326 GetClientRect(hwnd, lprEdit);
331 InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
334 * Chop off the bottom part to fit with the height of the text area.
336 lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
339 * The button starts the same vertical position as the text area.
341 CopyRect(lprButton, lprEdit);
344 * If the combobox is "simple" there is no button.
346 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
347 lprButton->left = lprButton->right = lprButton->bottom = 0;
351 * Let's assume the combobox button is the same width as the
353 * size the button horizontally and cut-off the text area.
355 lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
356 lprEdit->right = lprButton->left;
360 * In the case of a dropdown, there is an additional spacing between the
361 * text area and the button.
363 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
365 lprEdit->right -= COMBO_EDITBUTTONSPACE();
369 * If we have an edit control, we space it away from the borders slightly.
371 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
373 InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
377 * Adjust the size of the listbox popup.
379 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
382 * Use the client rectangle to initialize the listbox rectangle
384 GetClientRect(hwnd, lprLB);
387 * Then, chop-off the top part.
389 lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
394 * Make sure the dropped width is as large as the combobox itself.
396 if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
398 lprLB->right = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
401 * In the case of a dropdown, the popup listbox is offset to the right.
402 * so, we want to make sure it's flush with the right side of the
405 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
406 lprLB->right -= COMBO_EDITBUTTONSPACE();
409 lprLB->right = lprLB->left + lphc->droppedWidth;
412 TRACE("\ttext\t= (%i,%i-%i,%i)\n",
413 lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom);
415 TRACE("\tbutton\t= (%i,%i-%i,%i)\n",
416 lprButton->left, lprButton->top, lprButton->right, lprButton->bottom);
418 TRACE("\tlbox\t= (%i,%i-%i,%i)\n",
419 lprLB->left, lprLB->top, lprLB->right, lprLB->bottom );
422 /***********************************************************************
423 * CBGetDroppedControlRect
425 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
427 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
428 of the combo box and the lower right corner of the listbox */
430 GetWindowRect(lphc->self->hwndSelf, lpRect);
432 lpRect->right = lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
433 lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
437 /***********************************************************************
438 * COMBO_WindowPosChanging
440 static LRESULT COMBO_WindowPosChanging(
443 WINDOWPOS* posChanging)
446 * We need to override the WM_WINDOWPOSCHANGING method to handle all
447 * the non-simple comboboxes. The problem is that those controls are
448 * always the same height. We have to make sure they are not resized
451 if ( ( CB_GETTYPE(lphc) != CBS_SIMPLE ) &&
452 ((posChanging->flags & SWP_NOSIZE) == 0) )
456 newComboHeight = CBGetTextAreaHeight(hwnd,lphc) +
457 2*COMBO_YBORDERSIZE();
460 * Resizing a combobox has another side effect, it resizes the dropped
461 * rectangle as well. However, it does it only if the new height for the
462 * combobox is different from the height it should have. In other words,
463 * if the application resizing the combobox only had the intention to resize
464 * the actual control, for example, to do the layout of a dialog that is
465 * resized, the height of the dropdown is not changed.
467 if (posChanging->cy != newComboHeight)
469 TRACE("posChanging->cy=%d, newComboHeight=%d, oldbot=%d, oldtop=%d\n",
470 posChanging->cy, newComboHeight, lphc->droppedRect.bottom,
471 lphc->droppedRect.top);
472 lphc->droppedRect.bottom = lphc->droppedRect.top + posChanging->cy - newComboHeight;
474 posChanging->cy = newComboHeight;
481 /***********************************************************************
484 static LRESULT COMBO_Create( LPHEADCOMBO lphc, WND* wnd, HWND hwndParent, LONG style )
486 static const WCHAR clbName[] = {'C','o','m','b','o','L','B','o','x',0};
487 static const WCHAR editName[] = {'E','d','i','t',0};
489 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
490 if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
493 lphc->owner = hwndParent;
496 * The item height and dropped width are not set when the control
499 lphc->droppedWidth = lphc->editHeight = 0;
502 * The first time we go through, we want to measure the ownerdraw item
504 lphc->wState |= CBF_MEASUREITEM;
506 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
508 if( lphc->owner || !(style & WS_VISIBLE) )
514 * Initialize the dropped rect to the size of the client area of the
515 * control and then, force all the areas of the combobox to be
518 GetClientRect( wnd->hwndSelf, &lphc->droppedRect );
520 CBCalcPlacement(wnd->hwndSelf,
524 &lphc->droppedRect );
527 * Adjust the position of the popup listbox if it's necessary
529 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
531 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
534 * If it's a dropdown, the listbox is offset
536 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
537 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
539 ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect);
540 ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect.right);
543 /* create listbox popup */
545 lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
546 (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
548 if( lphc->dwStyle & CBS_SORT )
549 lbeStyle |= LBS_SORT;
550 if( lphc->dwStyle & CBS_HASSTRINGS )
551 lbeStyle |= LBS_HASSTRINGS;
552 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
553 lbeStyle |= LBS_NOINTEGRALHEIGHT;
554 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
555 lbeStyle |= LBS_DISABLENOSCROLL;
557 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
559 lbeStyle |= WS_VISIBLE;
562 * In win 95 look n feel, the listbox in the simple combobox has
563 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
565 if (TWEAK_WineLook > WIN31_LOOK)
567 lbeStyle &= ~WS_BORDER;
568 lbeExStyle |= WS_EX_CLIENTEDGE;
572 lphc->hWndLBox = CreateWindowExW(lbeExStyle,
576 lphc->droppedRect.left,
577 lphc->droppedRect.top,
578 lphc->droppedRect.right - lphc->droppedRect.left,
579 lphc->droppedRect.bottom - lphc->droppedRect.top,
580 lphc->self->hwndSelf,
581 (HMENU)ID_CB_LISTBOX,
582 lphc->self->hInstance,
588 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
591 * In Win95 look, the border fo the edit control is
592 * provided by the combobox
594 if (TWEAK_WineLook == WIN31_LOOK)
595 lbeStyle |= WS_BORDER;
597 if( lphc->wState & CBF_EDIT )
599 if( lphc->dwStyle & CBS_OEMCONVERT )
600 lbeStyle |= ES_OEMCONVERT;
601 if( lphc->dwStyle & CBS_AUTOHSCROLL )
602 lbeStyle |= ES_AUTOHSCROLL;
603 if( lphc->dwStyle & CBS_LOWERCASE )
604 lbeStyle |= ES_LOWERCASE;
605 else if( lphc->dwStyle & CBS_UPPERCASE )
606 lbeStyle |= ES_UPPERCASE;
608 lphc->hWndEdit = CreateWindowExW(0,
612 lphc->textRect.left, lphc->textRect.top,
613 lphc->textRect.right - lphc->textRect.left,
614 lphc->textRect.bottom - lphc->textRect.top,
615 lphc->self->hwndSelf,
617 lphc->self->hInstance,
620 if( !lphc->hWndEdit )
626 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
628 /* Now do the trick with parent */
629 SetParent(lphc->hWndLBox, HWND_DESKTOP);
631 * If the combo is a dropdown, we must resize the control
632 * to fit only the text area and button. To do this,
633 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
634 * will take care of setting the height for us.
636 CBForceDummyResize(lphc);
639 TRACE("init done\n");
640 return wnd->hwndSelf;
642 ERR("edit control failure.\n");
643 } else ERR("listbox failure.\n");
644 } else ERR("no owner for visible combo.\n");
646 /* CreateWindow() will send WM_NCDESTROY to cleanup */
651 /***********************************************************************
654 * Paint combo button (normal, pressed, and disabled states).
656 static void CBPaintButton(
661 if( lphc->wState & CBF_NOREDRAW )
664 if (TWEAK_WineLook == WIN31_LOOK)
670 COLORREF oldTextColor, oldBkColor;
673 hPrevBrush = SelectObject(hdc, GetSysColorBrush(COLOR_BTNFACE));
676 * Draw the button background
681 rectButton.right-rectButton.left,
682 rectButton.bottom-rectButton.top,
685 if( (bBool = lphc->wState & CBF_BUTTONDOWN) )
687 DrawEdge( hdc, &rectButton, EDGE_SUNKEN, BF_RECT );
691 DrawEdge( hdc, &rectButton, EDGE_RAISED, BF_RECT );
695 * Remove the edge of the button from the rectangle
696 * and calculate the position of the bitmap.
698 InflateRect( &rectButton, -2, -2);
700 x = (rectButton.left + rectButton.right - CBitWidth) >> 1;
701 y = (rectButton.top + rectButton.bottom - CBitHeight) >> 1;
704 hMemDC = CreateCompatibleDC( hdc );
705 SelectObject( hMemDC, hComboBmp );
706 oldTextColor = SetTextColor( hdc, GetSysColor(COLOR_BTNFACE) );
707 oldBkColor = SetBkColor( hdc, CB_DISABLED(lphc) ? RGB(128,128,128) :
709 BitBlt( hdc, x, y, CBitWidth, CBitHeight, hMemDC, 0, 0, SRCCOPY );
710 SetBkColor( hdc, oldBkColor );
711 SetTextColor( hdc, oldTextColor );
713 SelectObject( hdc, hPrevBrush );
717 UINT buttonState = DFCS_SCROLLCOMBOBOX;
719 if (lphc->wState & CBF_BUTTONDOWN)
721 buttonState |= DFCS_PUSHED;
724 if (CB_DISABLED(lphc))
726 buttonState |= DFCS_INACTIVE;
729 DrawFrameControl(hdc,
736 /***********************************************************************
739 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
741 static void CBPaintText(
749 if( lphc->wState & CBF_NOREDRAW ) return;
753 /* follow Windows combobox that sends a bunch of text
754 * inquiries to its listbox while processing WM_PAINT. */
756 if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
758 size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
759 if( (pText = HeapAlloc( GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR))) )
761 SendMessageW(lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText);
762 pText[size] = '\0'; /* just in case */
766 if( !CB_OWNERDRAWN(lphc) )
769 if( lphc->wState & CBF_EDIT )
771 static const WCHAR empty_stringW[] = { 0 };
772 if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW );
773 if( lphc->wState & CBF_FOCUSED )
774 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
776 else /* paint text field ourselves */
778 UINT itemState = ODS_COMBOBOXEDIT;
779 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
782 * Give ourselves some space.
784 InflateRect( &rectEdit, -1, -1 );
786 if( CB_OWNERDRAWN(lphc) )
791 /* setup state for DRAWITEM message. Owner will highlight */
792 if ( (lphc->wState & CBF_FOCUSED) &&
793 !(lphc->wState & CBF_DROPPED) )
794 itemState |= ODS_SELECTED | ODS_FOCUS;
797 * Save the current clip region.
798 * To retrieve the clip region, we need to create one "dummy"
801 clipRegion = CreateRectRgnIndirect(&rectEdit);
803 if (GetClipRgn(hdc, clipRegion)!=1)
805 DeleteObject(clipRegion);
806 clipRegion=(HRGN)NULL;
809 if ( lphc->self->dwStyle & WS_DISABLED )
810 itemState |= ODS_DISABLED;
812 dis.CtlType = ODT_COMBOBOX;
813 dis.CtlID = lphc->self->wIDmenu;
814 dis.hwndItem = lphc->self->hwndSelf;
815 dis.itemAction = ODA_DRAWENTIRE;
817 dis.itemState = itemState;
819 dis.rcItem = rectEdit;
820 dis.itemData = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA,
824 * Clip the DC and have the parent draw the item.
826 IntersectClipRect(hdc,
827 rectEdit.left, rectEdit.top,
828 rectEdit.right, rectEdit.bottom);
830 SendMessageW(lphc->owner, WM_DRAWITEM,
831 lphc->self->wIDmenu, (LPARAM)&dis );
834 * Reset the clipping region.
836 SelectClipRgn(hdc, clipRegion);
840 static const WCHAR empty_stringW[] = { 0 };
842 if ( (lphc->wState & CBF_FOCUSED) &&
843 !(lphc->wState & CBF_DROPPED) ) {
846 FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
847 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
848 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
854 ETO_OPAQUE | ETO_CLIPPED,
856 pText ? pText : empty_stringW , size, NULL );
858 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
859 DrawFocusRect( hdc, &rectEdit );
863 SelectObject(hdc, hPrevFont );
866 HeapFree( GetProcessHeap(), 0, pText );
869 /***********************************************************************
872 static void CBPaintBorder(
879 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
881 GetClientRect(hwnd, &clientRect);
885 CopyRect(&clientRect, &lphc->textRect);
887 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
888 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
891 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
894 /***********************************************************************
895 * COMBO_PrepareColors
897 * This method will sent the appropriate WM_CTLCOLOR message to
898 * prepare and setup the colors for the combo's DC.
900 * It also returns the brush to use for the background.
902 static HBRUSH COMBO_PrepareColors(
909 * Get the background brush for this control.
911 if (CB_DISABLED(lphc))
913 hBkgBrush = SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
914 hDC, lphc->self->hwndSelf );
917 * We have to change the text color since WM_CTLCOLORSTATIC will
918 * set it to the "enabled" color. This is the same behavior as the
921 SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
925 if (lphc->wState & CBF_EDIT)
927 hBkgBrush = SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
928 hDC, lphc->self->hwndSelf );
932 hBkgBrush = SendMessageW(lphc->owner, WM_CTLCOLORLISTBOX,
933 hDC, lphc->self->hwndSelf );
941 hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
946 /***********************************************************************
947 * COMBO_EraseBackground
949 static LRESULT COMBO_EraseBackground(
957 if(lphc->wState & CBF_EDIT)
960 hDC = (hParamDC) ? hParamDC
963 * Retrieve the background brush
965 hBkgBrush = COMBO_PrepareColors(lphc, hDC);
967 FillRect(hDC, &lphc->textRect, hBkgBrush);
970 ReleaseDC(hwnd, hDC);
975 /***********************************************************************
978 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
983 hDC = (hParamDC) ? hParamDC
984 : BeginPaint( lphc->self->hwndSelf, &ps);
986 TRACE("hdc=%04x\n", hDC);
988 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
990 HBRUSH hPrevBrush, hBkgBrush;
993 * Retrieve the background brush and select it in the
996 hBkgBrush = COMBO_PrepareColors(lphc, hDC);
998 hPrevBrush = SelectObject( hDC, hBkgBrush );
1001 * In non 3.1 look, there is a sunken border on the combobox
1003 if (TWEAK_WineLook != WIN31_LOOK)
1005 CBPaintBorder(CB_HWND(lphc), lphc, hDC);
1008 if( !IsRectEmpty(&lphc->buttonRect) )
1010 CBPaintButton(lphc, hDC, lphc->buttonRect);
1013 /* paint the edit control padding area */
1014 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
1016 RECT rPadEdit = lphc->textRect;
1018 InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
1020 FrameRect( hDC, &rPadEdit, GetSysColorBrush(COLOR_WINDOW) );
1023 if( !(lphc->wState & CBF_EDIT) )
1026 * The text area has a border only in Win 3.1 look.
1028 if (TWEAK_WineLook == WIN31_LOOK)
1030 HPEN hPrevPen = SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
1033 lphc->textRect.left, lphc->textRect.top,
1034 lphc->textRect.right - 1, lphc->textRect.bottom - 1);
1036 SelectObject( hDC, hPrevPen );
1039 CBPaintText( lphc, hDC, lphc->textRect);
1043 SelectObject( hDC, hPrevBrush );
1047 EndPaint(lphc->self->hwndSelf, &ps);
1052 /***********************************************************************
1055 * Select listbox entry according to the contents of the edit control.
1057 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
1060 LPWSTR pText = NULL;
1063 length = CB_GETEDITTEXTLENGTH( lphc );
1066 pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1068 TRACE("\t edit text length %i\n", length );
1072 if( length ) GetWindowTextW( lphc->hWndEdit, pText, length + 1);
1073 else pText[0] = '\0';
1074 idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING,
1075 (WPARAM)(-1), (LPARAM)pText );
1076 HeapFree( GetProcessHeap(), 0, pText );
1079 SendMessageW(lphc->hWndLBox, LB_SETCURSEL, (WPARAM)(bSelect ? idx : -1), 0);
1081 /* probably superfluous but Windows sends this too */
1082 SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
1083 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
1088 /***********************************************************************
1091 * Copy a listbox entry to the edit control.
1093 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
1096 LPWSTR pText = NULL;
1097 static const WCHAR empty_stringW[] = { 0 };
1099 TRACE("\t %i\n", index );
1101 if( index >= 0 ) /* got an entry */
1103 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
1106 if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) )
1108 SendMessageW(lphc->hWndLBox, LB_GETTEXT,
1109 (WPARAM)index, (LPARAM)pText );
1114 lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1115 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW);
1116 lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1118 if( lphc->wState & CBF_FOCUSED )
1119 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1122 HeapFree( GetProcessHeap(), 0, pText );
1125 /***********************************************************************
1128 * Show listbox popup.
1130 static void CBDropDown( LPHEADCOMBO lphc )
1136 TRACE("[%04x]: drop down\n", CB_HWND(lphc));
1138 CB_NOTIFY( lphc, CBN_DROPDOWN );
1142 lphc->wState |= CBF_DROPPED;
1143 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1145 lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
1147 /* Update edit only if item is in the list */
1148 if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
1149 CBUpdateEdit( lphc, lphc->droppedIndex );
1153 lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1155 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
1156 (WPARAM)(lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex), 0 );
1157 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1160 /* now set popup position */
1161 GetWindowRect( lphc->self->hwndSelf, &rect );
1164 * If it's a dropdown, the listbox is offset
1166 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1167 rect.left += COMBO_EDITBUTTONSPACE();
1169 /* if the dropped height is greater than the total height of the dropped
1170 items list, then force the drop down list height to be the total height
1171 of the items in the dropped list */
1173 /* And Remove any extra space (Best Fit) */
1174 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1175 /* if listbox length has been set directly by its handle */
1176 GetWindowRect(lphc->hWndLBox, &r);
1177 if (nDroppedHeight < r.bottom - r.top)
1178 nDroppedHeight = r.bottom - r.top;
1179 nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1185 nHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
1188 if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
1189 nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
1192 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1193 if( (rect.bottom + nDroppedHeight) >= GetSystemMetrics( SM_CYSCREEN ) )
1194 rect.bottom = rect.top - nDroppedHeight;
1196 SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1197 lphc->droppedRect.right - lphc->droppedRect.left,
1199 SWP_NOACTIVATE | SWP_SHOWWINDOW);
1202 if( !(lphc->wState & CBF_NOREDRAW) )
1203 RedrawWindow( lphc->self->hwndSelf, NULL, 0, RDW_INVALIDATE |
1204 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1206 EnableWindow( lphc->hWndLBox, TRUE );
1207 if (GetCapture() != lphc->self->hwndSelf)
1208 SetCapture(lphc->hWndLBox);
1211 /***********************************************************************
1214 * Hide listbox popup.
1216 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1218 HWND hWnd = lphc->self->hwndSelf;
1220 TRACE("[%04x]: sel ok? [%i] dropped? [%i]\n",
1221 CB_HWND(lphc), (INT)ok, (INT)(lphc->wState & CBF_DROPPED));
1223 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1225 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1228 if( lphc->wState & CBF_DROPPED )
1232 lphc->wState &= ~CBF_DROPPED;
1233 ShowWindow( lphc->hWndLBox, SW_HIDE );
1235 if(GetCapture() == lphc->hWndLBox)
1240 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1242 rect = lphc->buttonRect;
1253 rect = lphc->textRect;
1258 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1259 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1260 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1261 CB_NOTIFY( lphc, CBN_CLOSEUP );
1266 /***********************************************************************
1269 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1271 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1273 if( lphc->wState & CBF_DROPPED )
1275 CBRollUp( lphc, ok, bRedrawButton );
1283 /***********************************************************************
1286 static void CBRepaintButton( LPHEADCOMBO lphc )
1288 InvalidateRect(CB_HWND(lphc), &lphc->buttonRect, TRUE);
1289 UpdateWindow(CB_HWND(lphc));
1292 /***********************************************************************
1295 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1297 if( !(lphc->wState & CBF_FOCUSED) )
1299 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1300 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1302 /* This is wrong. Message sequences seem to indicate that this
1303 is set *after* the notify. */
1304 /* lphc->wState |= CBF_FOCUSED; */
1306 if( !(lphc->wState & CBF_EDIT) )
1307 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1309 CB_NOTIFY( lphc, CBN_SETFOCUS );
1310 lphc->wState |= CBF_FOCUSED;
1314 /***********************************************************************
1317 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1319 HWND hWnd = lphc->self->hwndSelf;
1321 if( lphc->wState & CBF_FOCUSED )
1323 CBRollUp( lphc, FALSE, TRUE );
1324 if( IsWindow( hWnd ) )
1326 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1327 SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1329 lphc->wState &= ~CBF_FOCUSED;
1332 if( !(lphc->wState & CBF_EDIT) )
1333 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1335 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1340 /***********************************************************************
1343 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1345 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1347 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1349 switch( HIWORD(wParam) >> 8 )
1351 case (EN_SETFOCUS >> 8):
1353 TRACE("[%04x]: edit [%04x] got focus\n",
1354 CB_HWND(lphc), lphc->hWndEdit );
1356 COMBO_SetFocus( lphc );
1359 case (EN_KILLFOCUS >> 8):
1361 TRACE("[%04x]: edit [%04x] lost focus\n",
1362 CB_HWND(lphc), lphc->hWndEdit );
1364 /* NOTE: it seems that Windows' edit control sends an
1365 * undocumented message WM_USER + 0x1B instead of this
1366 * notification (only when it happens to be a part of
1367 * the combo). ?? - AK.
1370 COMBO_KillFocus( lphc );
1374 case (EN_CHANGE >> 8):
1376 * In some circumstances (when the selection of the combobox
1377 * is changed for example) we don't wans the EN_CHANGE notification
1378 * to be forwarded to the parent of the combobox. This code
1379 * checks a flag that is set in these occasions and ignores the
1382 if (lphc->wState & CBF_NOLBSELECT)
1384 lphc->wState &= ~CBF_NOLBSELECT;
1388 CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1391 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1392 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1395 case (EN_UPDATE >> 8):
1396 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1397 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1400 case (EN_ERRSPACE >> 8):
1401 CB_NOTIFY( lphc, CBN_ERRSPACE );
1404 else if( lphc->hWndLBox == hWnd )
1406 switch( HIWORD(wParam) )
1409 CB_NOTIFY( lphc, CBN_ERRSPACE );
1413 CB_NOTIFY( lphc, CBN_DBLCLK );
1419 TRACE("[%04x]: lbox selection change [%04x]\n",
1420 CB_HWND(lphc), lphc->wState );
1422 if( HIWORD(wParam) == LBN_SELCHANGE)
1424 if( lphc->wState & CBF_EDIT )
1426 INT index = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1427 lphc->wState |= CBF_NOLBSELECT;
1428 CBUpdateEdit( lphc, index );
1429 /* select text in edit, as Windows does */
1430 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1433 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1436 /* do not roll up if selection is being tracked
1437 * by arrowkeys in the dropdown listbox */
1438 if( ((lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP)) )
1440 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1442 else lphc->wState &= ~CBF_NOROLLUP;
1444 CB_NOTIFY( lphc, CBN_SELCHANGE );
1450 /* nothing to do here since ComboLBox always resets the focus to its
1451 * combo/edit counterpart */
1458 /***********************************************************************
1461 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1463 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1465 HWND hWnd = lphc->self->hwndSelf;
1467 TRACE("[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg );
1469 #define lpIS ((LPDELETEITEMSTRUCT)lParam)
1471 /* two first items are the same in all 4 structs */
1472 lpIS->CtlType = ODT_COMBOBOX;
1473 lpIS->CtlID = lphc->self->wIDmenu;
1475 switch( msg ) /* patch window handle */
1478 lpIS->hwndItem = hWnd;
1482 #define lpIS ((LPDRAWITEMSTRUCT)lParam)
1483 lpIS->hwndItem = hWnd;
1486 case WM_COMPAREITEM:
1487 #define lpIS ((LPCOMPAREITEMSTRUCT)lParam)
1488 lpIS->hwndItem = hWnd;
1493 return SendMessageW(lphc->owner, msg, lphc->self->wIDmenu, lParam);
1496 /***********************************************************************
1499 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1500 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1502 static LRESULT COMBO_GetText( LPHEADCOMBO lphc, INT N, LPARAM lParam, BOOL unicode)
1504 if( lphc->wState & CBF_EDIT )
1505 return unicode ? SendMessageW(lphc->hWndEdit, WM_GETTEXT, (WPARAM)N, lParam) :
1506 SendMessageA(lphc->hWndEdit, WM_GETTEXT, (WPARAM)N, lParam);
1508 /* get it from the listbox */
1510 if( lphc->hWndLBox )
1512 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1516 INT length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN,
1521 LPWSTR lpBuffer, lpText = (LPWSTR)lParam;
1523 /* 'length' is without the terminating character */
1525 lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1531 n = SendMessageW(lphc->hWndLBox, LB_GETTEXT, (WPARAM)idx, (LPARAM)lpBuffer);
1533 /* truncate if buffer is too short */
1539 strncpyW(lpText, lpBuffer, (N > n) ? n+1 : N-1);
1540 lpText[N - 1] = '\0';
1542 HeapFree( GetProcessHeap(), 0, lpBuffer );
1548 LPSTR lpBuffer, lpText = (LPSTR)lParam;
1550 /* 'length' is without the terminating character */
1552 lpBuffer = HeapAlloc(GetProcessHeap(), 0, length + 1);
1558 n = SendMessageA(lphc->hWndLBox, LB_GETTEXT, (WPARAM)idx, (LPARAM)lpBuffer);
1560 /* truncate if buffer is too short */
1566 strncpy(lpText, lpBuffer, (N > n) ? n+1 : N-1);
1567 lpText[N - 1] = '\0';
1569 HeapFree( GetProcessHeap(), 0, lpBuffer );
1584 /***********************************************************************
1587 * This function sets window positions according to the updated
1588 * component placement struct.
1590 static void CBResetPos(
1596 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1598 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1599 * sizing messages */
1601 if( lphc->wState & CBF_EDIT )
1602 SetWindowPos( lphc->hWndEdit, 0,
1603 rectEdit->left, rectEdit->top,
1604 rectEdit->right - rectEdit->left,
1605 rectEdit->bottom - rectEdit->top,
1606 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1608 SetWindowPos( lphc->hWndLBox, 0,
1609 rectLB->left, rectLB->top,
1610 rectLB->right - rectLB->left,
1611 rectLB->bottom - rectLB->top,
1612 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1616 if( lphc->wState & CBF_DROPPED )
1618 lphc->wState &= ~CBF_DROPPED;
1619 ShowWindow( lphc->hWndLBox, SW_HIDE );
1622 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1623 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
1624 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1629 /***********************************************************************
1632 static void COMBO_Size( LPHEADCOMBO lphc )
1634 CBCalcPlacement(lphc->self->hwndSelf,
1638 &lphc->droppedRect);
1640 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1644 /***********************************************************************
1647 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1652 lphc->hFont = hFont;
1655 * Propagate to owned windows.
1657 if( lphc->wState & CBF_EDIT )
1658 SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1659 SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1662 * Redo the layout of the control.
1664 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1666 CBCalcPlacement(lphc->self->hwndSelf,
1670 &lphc->droppedRect);
1672 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1676 CBForceDummyResize(lphc);
1681 /***********************************************************************
1682 * COMBO_SetItemHeight
1684 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1686 LRESULT lRet = CB_ERR;
1688 if( index == -1 ) /* set text field height */
1690 if( height < 32768 )
1692 lphc->editHeight = height;
1695 * Redo the layout of the control.
1697 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1699 CBCalcPlacement(lphc->self->hwndSelf,
1703 &lphc->droppedRect);
1705 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1709 CBForceDummyResize(lphc);
1715 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1716 lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT,
1717 (WPARAM)index, (LPARAM)height );
1721 /***********************************************************************
1722 * COMBO_SelectString
1724 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
1726 INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText) :
1727 SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText);
1730 if( lphc->wState & CBF_EDIT )
1731 CBUpdateEdit( lphc, index );
1734 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1737 return (LRESULT)index;
1740 /***********************************************************************
1743 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1747 HWND hWnd = lphc->self->hwndSelf;
1749 pt.x = LOWORD(lParam);
1750 pt.y = HIWORD(lParam);
1751 bButton = PtInRect(&lphc->buttonRect, pt);
1753 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1754 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1756 lphc->wState |= CBF_BUTTONDOWN;
1757 if( lphc->wState & CBF_DROPPED )
1759 /* got a click to cancel selection */
1761 lphc->wState &= ~CBF_BUTTONDOWN;
1762 CBRollUp( lphc, TRUE, FALSE );
1763 if( !IsWindow( hWnd ) ) return;
1765 if( lphc->wState & CBF_CAPTURE )
1767 lphc->wState &= ~CBF_CAPTURE;
1773 /* drop down the listbox and start tracking */
1775 lphc->wState |= CBF_CAPTURE;
1779 if( bButton ) CBRepaintButton( lphc );
1783 /***********************************************************************
1786 * Release capture and stop tracking if needed.
1788 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1790 if( lphc->wState & CBF_CAPTURE )
1792 lphc->wState &= ~CBF_CAPTURE;
1793 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1795 INT index = CBUpdateLBox( lphc, TRUE );
1796 /* Update edit only if item is in the list */
1799 lphc->wState |= CBF_NOLBSELECT;
1800 CBUpdateEdit( lphc, index );
1801 lphc->wState &= ~CBF_NOLBSELECT;
1805 SetCapture(lphc->hWndLBox);
1808 if( lphc->wState & CBF_BUTTONDOWN )
1810 lphc->wState &= ~CBF_BUTTONDOWN;
1811 CBRepaintButton( lphc );
1815 /***********************************************************************
1818 * Two things to do - track combo button and release capture when
1819 * pointer goes into the listbox.
1821 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1826 pt.x = LOWORD(lParam);
1827 pt.y = HIWORD(lParam);
1829 if( lphc->wState & CBF_BUTTONDOWN )
1833 bButton = PtInRect(&lphc->buttonRect, pt);
1837 lphc->wState &= ~CBF_BUTTONDOWN;
1838 CBRepaintButton( lphc );
1842 GetClientRect( lphc->hWndLBox, &lbRect );
1843 MapWindowPoints( lphc->self->hwndSelf, lphc->hWndLBox, &pt, 1 );
1844 if( PtInRect(&lbRect, pt) )
1846 lphc->wState &= ~CBF_CAPTURE;
1848 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1850 /* hand over pointer tracking */
1851 SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1856 /***********************************************************************
1857 * ComboWndProc_locked
1859 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1861 static LRESULT ComboWndProc_locked( WND* pWnd, UINT message,
1862 WPARAM wParam, LPARAM lParam, BOOL unicode )
1865 LPHEADCOMBO lphc = CB_GETPTR(pWnd);
1866 HWND hwnd = pWnd->hwndSelf;
1868 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
1869 pWnd->hwndSelf, SPY_GetMsgName(message), wParam, lParam );
1871 if( lphc || message == WM_NCCREATE )
1875 /* System messages */
1879 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1880 ((LPCREATESTRUCTA)lParam)->style;
1881 return COMBO_NCCreate(pWnd, style);
1884 COMBO_NCDestroy(lphc);
1885 break;/* -> DefWindowProc */
1893 hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1894 style = ((LPCREATESTRUCTW)lParam)->style;
1898 hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1899 style = ((LPCREATESTRUCTA)lParam)->style;
1901 return COMBO_Create(lphc, pWnd, hwndParent, style);
1904 case WM_PRINTCLIENT:
1905 if (lParam & PRF_ERASEBKGND)
1906 COMBO_EraseBackground(hwnd, lphc, wParam);
1910 /* wParam may contain a valid HDC! */
1911 return COMBO_Paint(lphc, wParam);
1913 return COMBO_EraseBackground(hwnd, lphc, wParam);
1916 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1917 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1919 int vk = (int)((LPMSG)lParam)->wParam;
1921 if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1922 result |= DLGC_WANTMESSAGE;
1926 case WM_WINDOWPOSCHANGING:
1927 return COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1928 case WM_WINDOWPOSCHANGED:
1929 /* SetWindowPos can be called on a Combobox to resize its Listbox.
1930 * In that case, the Combobox itself will not be resized, so we won't
1931 * get a WM_SIZE. Since we still want to update the Listbox, we have to
1936 if( lphc->hWndLBox &&
1937 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1940 COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
1943 return (LRESULT)lphc->hFont;
1945 if( lphc->wState & CBF_EDIT )
1946 SetFocus( lphc->hWndEdit );
1948 COMBO_SetFocus( lphc );
1951 #define hwndFocus ((HWND16)wParam)
1953 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1954 COMBO_KillFocus( lphc );
1958 return COMBO_Command( lphc, wParam, (HWND)lParam );
1960 return COMBO_GetText( lphc, (INT)wParam, lParam, unicode );
1962 case WM_GETTEXTLENGTH:
1964 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1966 int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1967 if (j == -1) return 0;
1968 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1970 else if( lphc->wState & CBF_EDIT )
1973 lphc->wState |= CBF_NOEDITNOTIFY;
1974 ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1975 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1976 lphc->wState &= ~CBF_NOEDITNOTIFY;
1983 if( lphc->wState & CBF_EDIT )
1985 return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1986 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1992 case WM_COMPAREITEM:
1993 case WM_MEASUREITEM:
1994 return COMBO_ItemOp(lphc, message, lParam);
1996 if( lphc->wState & CBF_EDIT )
1997 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1998 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
2000 /* Force the control to repaint when the enabled state changes. */
2001 InvalidateRect(CB_HWND(lphc), NULL, TRUE);
2005 lphc->wState &= ~CBF_NOREDRAW;
2007 lphc->wState |= CBF_NOREDRAW;
2009 if( lphc->wState & CBF_EDIT )
2010 SendMessageW(lphc->hWndEdit, message, wParam, lParam);
2011 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
2014 if( KEYDATA_ALT & HIWORD(lParam) )
2015 if( wParam == VK_UP || wParam == VK_DOWN )
2016 COMBO_FlipListbox( lphc, FALSE, FALSE );
2024 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
2025 (lphc->wState & CBF_DROPPED))
2027 CBRollUp( lphc, wParam == VK_RETURN, FALSE );
2031 if( lphc->wState & CBF_EDIT )
2032 hwndTarget = lphc->hWndEdit;
2034 hwndTarget = lphc->hWndLBox;
2036 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
2037 SendMessageA(hwndTarget, message, wParam, lParam);
2039 case WM_LBUTTONDOWN:
2040 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self->hwndSelf );
2041 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
2044 COMBO_LButtonUp( lphc );
2047 if( lphc->wState & CBF_CAPTURE )
2048 COMBO_MouseMove( lphc, wParam, lParam );
2052 if (wParam & (MK_SHIFT | MK_CONTROL))
2053 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2054 DefWindowProcA(hwnd, message, wParam, lParam);
2055 if (SHIWORD(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
2056 if (SHIWORD(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
2059 /* Combo messages */
2061 case CB_ADDSTRING16:
2062 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2065 return unicode ? SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam) :
2066 SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
2067 case CB_INSERTSTRING16:
2068 wParam = (INT)(INT16)wParam;
2069 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2071 case CB_INSERTSTRING:
2072 return unicode ? SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam) :
2073 SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2074 case CB_DELETESTRING16:
2075 case CB_DELETESTRING:
2076 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
2077 SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
2078 case CB_SELECTSTRING16:
2079 wParam = (INT)(INT16)wParam;
2080 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2082 case CB_SELECTSTRING:
2083 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
2084 case CB_FINDSTRING16:
2085 wParam = (INT)(INT16)wParam;
2086 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2089 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
2090 SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
2091 case CB_FINDSTRINGEXACT16:
2092 wParam = (INT)(INT16)wParam;
2093 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2095 case CB_FINDSTRINGEXACT:
2096 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
2097 SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
2098 case CB_SETITEMHEIGHT16:
2099 wParam = (INT)(INT16)wParam; /* signed integer */
2101 case CB_SETITEMHEIGHT:
2102 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2103 case CB_GETITEMHEIGHT16:
2104 wParam = (INT)(INT16)wParam;
2106 case CB_GETITEMHEIGHT:
2107 if( (INT)wParam >= 0 ) /* listbox item */
2108 return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2109 return CBGetTextAreaHeight(hwnd, lphc);
2110 case CB_RESETCONTENT16:
2111 case CB_RESETCONTENT:
2112 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2113 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2115 static const WCHAR empty_stringW[] = { 0 };
2116 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2119 InvalidateRect(CB_HWND(lphc), NULL, TRUE);
2121 case CB_INITSTORAGE:
2122 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2123 case CB_GETHORIZONTALEXTENT:
2124 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2125 case CB_SETHORIZONTALEXTENT:
2126 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2127 case CB_GETTOPINDEX:
2128 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2130 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2132 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2133 case CB_GETDROPPEDWIDTH:
2134 if( lphc->droppedWidth )
2135 return lphc->droppedWidth;
2136 return lphc->droppedRect.right - lphc->droppedRect.left;
2137 case CB_SETDROPPEDWIDTH:
2138 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2139 (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2141 case CB_GETDROPPEDCONTROLRECT16:
2142 lParam = (LPARAM)MapSL(lParam);
2146 CBGetDroppedControlRect( lphc, &r );
2147 CONV_RECT32TO16( &r, (LPRECT16)lParam );
2150 case CB_GETDROPPEDCONTROLRECT:
2151 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2153 case CB_GETDROPPEDSTATE16:
2154 case CB_GETDROPPEDSTATE:
2155 return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2157 lParam = (LPARAM)MapSL(lParam);
2161 if(message == CB_DIR) message = LB_DIR;
2162 return unicode ? SendMessageW(lphc->hWndLBox, message, wParam, lParam) :
2163 SendMessageA(lphc->hWndLBox, message, wParam, lParam);
2165 case CB_SHOWDROPDOWN16:
2166 case CB_SHOWDROPDOWN:
2167 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2171 if( !(lphc->wState & CBF_DROPPED) )
2175 if( lphc->wState & CBF_DROPPED )
2176 CBRollUp( lphc, FALSE, TRUE );
2181 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2182 case CB_GETCURSEL16:
2184 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2185 case CB_SETCURSEL16:
2186 wParam = (INT)(INT16)wParam;
2189 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2191 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2193 /* no LBN_SELCHANGE in this case, update manually */
2194 if( lphc->wState & CBF_EDIT )
2195 CBUpdateEdit( lphc, (INT)wParam );
2197 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
2198 lphc->wState &= ~CBF_SELCHANGE;
2200 case CB_GETLBTEXT16:
2201 wParam = (INT)(INT16)wParam;
2202 lParam = (LPARAM)MapSL(lParam);
2205 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2206 SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2207 case CB_GETLBTEXTLEN16:
2208 wParam = (INT)(INT16)wParam;
2210 case CB_GETLBTEXTLEN:
2211 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2212 case CB_GETITEMDATA16:
2213 wParam = (INT)(INT16)wParam;
2215 case CB_GETITEMDATA:
2216 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2217 case CB_SETITEMDATA16:
2218 wParam = (INT)(INT16)wParam;
2220 case CB_SETITEMDATA:
2221 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2222 case CB_GETEDITSEL16:
2223 wParam = lParam = 0; /* just in case */
2226 /* Edit checks passed parameters itself */
2227 if( lphc->wState & CBF_EDIT )
2228 return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2230 case CB_SETEDITSEL16:
2232 if( lphc->wState & CBF_EDIT )
2233 return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2234 (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2236 case CB_SETEXTENDEDUI16:
2237 case CB_SETEXTENDEDUI:
2238 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2241 lphc->wState |= CBF_EUI;
2242 else lphc->wState &= ~CBF_EUI;
2244 case CB_GETEXTENDEDUI16:
2245 case CB_GETEXTENDEDUI:
2246 return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2249 if (message >= WM_USER)
2250 WARN("unknown msg WM_USER+%04x wp=%04x lp=%08lx\n",
2251 message - WM_USER, wParam, lParam );
2254 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2255 DefWindowProcA(hwnd, message, wParam, lParam);
2260 /***********************************************************************
2263 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2266 static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2268 WND* pWnd = WIN_FindWndPtr(hwnd);
2269 LRESULT retvalue = ComboWndProc_locked(pWnd, message, wParam, lParam, FALSE);
2271 WIN_ReleaseWndPtr(pWnd);
2275 /***********************************************************************
2278 static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2280 WND* pWnd = WIN_FindWndPtr(hwnd);
2281 LRESULT retvalue = ComboWndProc_locked(pWnd, message, wParam, lParam, TRUE);
2283 WIN_ReleaseWndPtr(pWnd);