4 * Copyright 1997 Alex Korobka
6 * FIXME: roll up in Netscape 3.01.
14 #include "wine/winuser16.h"
21 #include "debugtools.h"
24 DEFAULT_DEBUG_CHANNEL(combo)
26 /* bits in the dwKeyData */
27 #define KEYDATA_ALT 0x2000
28 #define KEYDATA_PREVSTATE 0x4000
31 * Additional combo box definitions
34 #define CB_GETPTR( wnd ) (*(LPHEADCOMBO*)((wnd)->wExtra))
35 #define CB_NOTIFY( lphc, code ) \
36 (SendMessageA( (lphc)->owner, WM_COMMAND, \
37 MAKEWPARAM((lphc)->self->wIDmenu, (code)), (lphc)->self->hwndSelf))
38 #define CB_GETEDITTEXTLENGTH( lphc ) \
39 (SendMessageA( (lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ))
44 static HBITMAP hComboBmp = 0;
45 static UINT CBitHeight, CBitWidth;
48 * Look and feel dependant "constants"
51 #define COMBO_YBORDERGAP 5
52 #define COMBO_XBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
53 #define COMBO_YBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
54 #define COMBO_EDITBUTTONSPACE() ( (TWEAK_WineLook == WIN31_LOOK) ? 8 : 0 )
55 #define EDIT_CONTROL_PADDING() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 1 )
57 /***********************************************************************
60 * Load combo button bitmap.
62 static BOOL COMBO_Init()
66 if( hComboBmp ) return TRUE;
67 if( (hDC = CreateCompatibleDC(0)) )
70 if( (hComboBmp = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_COMBO))) )
76 GetObjectA( hComboBmp, sizeof(bm), &bm );
77 CBitHeight = bm.bmHeight;
78 CBitWidth = bm.bmWidth;
80 TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
82 hPrevB = SelectObject16( hDC, hComboBmp);
83 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
84 InvertRect( hDC, &r );
85 SelectObject( hDC, hPrevB );
94 /***********************************************************************
97 static LRESULT COMBO_NCCreate(WND* wnd, LPARAM lParam)
101 if ( wnd && COMBO_Init() &&
102 (lphc = HeapAlloc(GetProcessHeap(), 0, sizeof(HEADCOMBO))) )
104 LPCREATESTRUCTA lpcs = (CREATESTRUCTA*)lParam;
106 memset( lphc, 0, sizeof(HEADCOMBO) );
107 *(LPHEADCOMBO*)wnd->wExtra = lphc;
109 /* some braindead apps do try to use scrollbar/border flags */
111 lphc->dwStyle = (lpcs->style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL));
112 wnd->dwStyle &= ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
115 * We also have to remove the client edge style to make sure
116 * we don't end-up with a non client area.
118 wnd->dwExStyle &= ~(WS_EX_CLIENTEDGE);
120 if( !(lpcs->style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
121 lphc->dwStyle |= CBS_HASSTRINGS;
122 if( !(wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) )
123 lphc->wState |= CBF_NOTIFY;
125 TRACE("[0x%08x], style = %08x\n",
126 (UINT)lphc, lphc->dwStyle );
128 return (LRESULT)(UINT)wnd->hwndSelf;
130 return (LRESULT)FALSE;
133 /***********************************************************************
136 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
141 WND* wnd = lphc->self;
143 TRACE("[%04x]: freeing storage\n", CB_HWND(lphc));
145 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
146 DestroyWindow( lphc->hWndLBox );
148 HeapFree( GetProcessHeap(), 0, lphc );
154 /***********************************************************************
157 * The dummy resize is used for listboxes that have a popup to trigger
158 * a re-arranging of the contents of the combobox and the recalculation
159 * of the size of the "real" control window.
161 static void CBForceDummyResize(
166 GetWindowRect(CB_HWND(lphc), &windowRect);
169 * We have to be careful, resizing a combobox also has the meaning that the
170 * dropped rect will be resized. In this case, we want to trigger a resize
171 * to recalculate layout but we don't want to change the dropped rectangle
172 * So, we add the size of the dropped rectangle to the size of the control.
173 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
176 SetWindowPos( CB_HWND(lphc),
179 windowRect.right - windowRect.left,
180 windowRect.bottom - windowRect.top +
181 lphc->droppedRect.bottom - lphc->droppedRect.top,
182 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
185 /***********************************************************************
186 * CBGetTextAreaHeight
188 * This method will calculate the height of the text area of the
190 * The height of the text area is set in two ways.
191 * It can be set explicitely through a combobox message of through a
192 * WM_MEASUREITEM callback.
193 * If this is not the case, the height is set to 13 dialog units.
194 * This height was determined through experimentation.
196 static INT CBGetTextAreaHeight(
202 if( lphc->editHeight ) /* explicitly set height */
204 iTextItemHeight = lphc->editHeight;
209 HDC hDC = GetDC(hwnd);
214 hPrevFont = SelectObject( hDC, lphc->hFont );
216 GetTextMetricsA(hDC, &tm);
218 baseUnitY = tm.tmHeight;
221 SelectObject( hDC, hPrevFont );
223 ReleaseDC(hwnd, hDC);
225 iTextItemHeight = ((13 * baseUnitY) / 8);
228 * This "formula" calculates the height of the complete control.
229 * To calculate the height of the text area, we have to remove the
232 iTextItemHeight -= 2*COMBO_YBORDERSIZE();
236 * Check the ownerdraw case if we haven't asked the parent the size
239 if ( CB_OWNERDRAWN(lphc) &&
240 (lphc->wState & CBF_MEASUREITEM) )
242 MEASUREITEMSTRUCT measureItem;
244 INT originalItemHeight = iTextItemHeight;
247 * We use the client rect for the width of the item.
249 GetClientRect(hwnd, &clientRect);
251 lphc->wState &= ~CBF_MEASUREITEM;
254 * Send a first one to measure the size of the text area
256 measureItem.CtlType = ODT_COMBOBOX;
257 measureItem.CtlID = lphc->self->wIDmenu;
258 measureItem.itemID = -1;
259 measureItem.itemWidth = clientRect.right;
260 measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
261 measureItem.itemData = 0;
262 SendMessageA(lphc->owner, WM_MEASUREITEM,
263 (WPARAM)measureItem.CtlID, (LPARAM)&measureItem);
264 iTextItemHeight = 6 + measureItem.itemHeight;
267 * Send a second one in the case of a fixed ownerdraw list to calculate the
268 * size of the list items. (we basically do this on behalf of the listbox)
270 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
272 measureItem.CtlType = ODT_COMBOBOX;
273 measureItem.CtlID = lphc->self->wIDmenu;
274 measureItem.itemID = 0;
275 measureItem.itemWidth = clientRect.right;
276 measureItem.itemHeight = originalItemHeight;
277 measureItem.itemData = 0;
278 SendMessageA(lphc->owner, WM_MEASUREITEM,
279 (WPARAM)measureItem.CtlID, (LPARAM)&measureItem);
280 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
284 * Keep the size for the next time
286 lphc->editHeight = iTextItemHeight;
289 return iTextItemHeight;
293 /***********************************************************************
296 * Set up component coordinates given valid lphc->RectCombo.
298 static void CBCalcPlacement(
306 * Again, start with the client rectangle.
308 GetClientRect(hwnd, lprEdit);
313 InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
316 * Chop off the bottom part to fit with the height of the text area.
318 lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
321 * The button starts the same vertical position as the text area.
323 CopyRect(lprButton, lprEdit);
326 * If the combobox is "simple" there is no button.
328 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
329 lprButton->left = lprButton->right = lprButton->bottom = 0;
333 * Let's assume the combobox button is the same width as the
335 * size the button horizontally and cut-off the text area.
337 lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
338 lprEdit->right = lprButton->left;
342 * In the case of a dropdown, there is an additional spacing between the
343 * text area and the button.
345 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
347 lprEdit->right -= COMBO_EDITBUTTONSPACE();
351 * If we have an edit control, we space it away from the borders slightly.
353 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
355 InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
359 * Adjust the size of the listbox popup.
361 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
364 * Use the client rectangle to initialize the listbox rectangle
366 GetClientRect(hwnd, lprLB);
369 * Then, chop-off the top part.
371 lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
376 * Make sure the dropped width is as large as the combobox itself.
378 if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
380 lprLB->right = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
383 * In the case of a dropdown, the popup listbox is offset to the right.
384 * so, we want to make sure it's flush with the right side of the
387 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
388 lprLB->right -= COMBO_EDITBUTTONSPACE();
391 lprLB->right = lprLB->left + lphc->droppedWidth;
394 TRACE("\ttext\t= (%i,%i-%i,%i)\n",
395 lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom);
397 TRACE("\tbutton\t= (%i,%i-%i,%i)\n",
398 lprButton->left, lprButton->top, lprButton->right, lprButton->bottom);
400 TRACE("\tlbox\t= (%i,%i-%i,%i)\n",
401 lprLB->left, lprLB->top, lprLB->right, lprLB->bottom );
404 /***********************************************************************
405 * CBGetDroppedControlRect
407 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
409 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
410 of the combo box and the lower right corner of the listbox */
412 GetWindowRect(lphc->self->hwndSelf, lpRect);
414 lpRect->right = lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
415 lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
419 /***********************************************************************
420 * COMBO_WindowPosChanging
422 static LRESULT COMBO_WindowPosChanging(
425 WINDOWPOS* posChanging)
428 * We need to override the WM_WINDOWPOSCHANGING method to handle all
429 * the non-simple comboboxes. The problem is that those controls are
430 * always the same height. We have to make sure they are not resized
433 if ( ( CB_GETTYPE(lphc) != CBS_SIMPLE ) &&
434 ((posChanging->flags & SWP_NOSIZE) == 0) )
438 newComboHeight = CBGetTextAreaHeight(hwnd,lphc) +
439 2*COMBO_YBORDERSIZE();
442 * Resizing a combobox has another side effect, it resizes the dropped
443 * rectangle as well. However, it does it only if the new height for the
444 * combobox is different than the height it should have. In other words,
445 * if the application resizing the combobox only had the intention to resize
446 * the actual control, for example, to do the layout of a dialog that is
447 * resized, the height of the dropdown is not changed.
449 if (posChanging->cy != newComboHeight)
451 lphc->droppedRect.bottom = lphc->droppedRect.top + posChanging->cy - newComboHeight;
453 posChanging->cy = newComboHeight;
460 /***********************************************************************
463 static LRESULT COMBO_Create( LPHEADCOMBO lphc, WND* wnd, LPARAM lParam)
465 static char clbName[] = "ComboLBox";
466 static char editName[] = "Edit";
468 LPCREATESTRUCTA lpcs = (CREATESTRUCTA*)lParam;
470 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
471 else if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
474 lphc->owner = lpcs->hwndParent;
477 * The item height and dropped width are not set when the control
480 lphc->droppedWidth = lphc->editHeight = 0;
483 * The first time we go through, we want to measure the ownerdraw item
485 lphc->wState |= CBF_MEASUREITEM;
487 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
489 if( lphc->owner || !(lpcs->style & WS_VISIBLE) )
495 * Initialize the dropped rect to the size of the client area of the
496 * control and then, force all the areas of the combobox to be
499 GetClientRect( wnd->hwndSelf, &lphc->droppedRect );
501 CBCalcPlacement(wnd->hwndSelf,
505 &lphc->droppedRect );
508 * Adjust the position of the popup listbox if it's necessary
510 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
512 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
515 * If it's a dropdown, the listbox is offset
517 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
518 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
520 ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect);
521 ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect.right);
524 /* create listbox popup */
526 lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS) |
527 (lpcs->style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
529 if( lphc->dwStyle & CBS_SORT )
530 lbeStyle |= LBS_SORT;
531 if( lphc->dwStyle & CBS_HASSTRINGS )
532 lbeStyle |= LBS_HASSTRINGS;
533 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
534 lbeStyle |= LBS_NOINTEGRALHEIGHT;
535 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
536 lbeStyle |= LBS_DISABLENOSCROLL;
538 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
540 lbeStyle |= WS_CHILD | WS_VISIBLE;
543 * In win 95 look n feel, the listbox in the simple combobox has
544 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
546 if (TWEAK_WineLook > WIN31_LOOK)
548 lbeStyle &= ~WS_BORDER;
549 lbeExStyle |= WS_EX_CLIENTEDGE;
552 else /* popup listbox */
553 lbeStyle |= WS_POPUP;
555 /* Dropdown ComboLBox is not a child window and we cannot pass
556 * ID_CB_LISTBOX directly because it will be treated as a menu handle.
558 lphc->hWndLBox = CreateWindowExA(lbeExStyle,
562 lphc->droppedRect.left,
563 lphc->droppedRect.top,
564 lphc->droppedRect.right - lphc->droppedRect.left,
565 lphc->droppedRect.bottom - lphc->droppedRect.top,
566 lphc->self->hwndSelf,
567 (lphc->dwStyle & CBS_DROPDOWN)? (HMENU)0 : (HMENU)ID_CB_LISTBOX,
568 lphc->self->hInstance,
572 * The ComboLBox is a strange little beast (when it's not a CBS_SIMPLE)...
573 * It's a popup window but, when you get the window style, you get WS_CHILD.
574 * When created, it's parent is the combobox but, when you ask for it's parent
575 * after that, you're supposed to get the desktop. (see MFC code function
577 * To achieve this in Wine, we have to create it as a popup and change
578 * it's style to child after the creation.
580 if ( (lphc->hWndLBox!= 0) &&
581 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
583 SetWindowLongA(lphc->hWndLBox,
585 (GetWindowLongA(lphc->hWndLBox, GWL_STYLE) | WS_CHILD) & ~WS_POPUP);
591 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT;
594 * In Win95 look, the border fo the edit control is
595 * provided by the combobox
597 if (TWEAK_WineLook == WIN31_LOOK)
598 lbeStyle |= WS_BORDER;
600 if( lphc->wState & CBF_EDIT )
602 if( lphc->dwStyle & CBS_OEMCONVERT )
603 lbeStyle |= ES_OEMCONVERT;
604 if( lphc->dwStyle & CBS_AUTOHSCROLL )
605 lbeStyle |= ES_AUTOHSCROLL;
606 if( lphc->dwStyle & CBS_LOWERCASE )
607 lbeStyle |= ES_LOWERCASE;
608 else if( lphc->dwStyle & CBS_UPPERCASE )
609 lbeStyle |= ES_UPPERCASE;
611 lphc->hWndEdit = CreateWindowExA(0,
615 lphc->textRect.left, lphc->textRect.top,
616 lphc->textRect.right - lphc->textRect.left,
617 lphc->textRect.bottom - lphc->textRect.top,
618 lphc->self->hwndSelf,
620 lphc->self->hInstance,
623 if( !lphc->hWndEdit )
630 * If the combo is a dropdown, we must resize the control to fit only
631 * the text area and button. To do this, we send a dummy resize and the
632 * WM_WINDOWPOSCHANGING message will take care of setting the height for
635 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
637 CBForceDummyResize(lphc);
640 TRACE("init done\n");
641 return wnd->hwndSelf;
643 ERR("edit control failure.\n");
644 } else ERR("listbox failure.\n");
645 } else ERR("no owner for visible combo.\n");
647 /* CreateWindow() will send WM_NCDESTROY to cleanup */
652 /***********************************************************************
655 * Paint combo button (normal, pressed, and disabled states).
657 static void CBPaintButton(
662 if( lphc->wState & CBF_NOREDRAW )
665 if (TWEAK_WineLook == WIN31_LOOK)
671 COLORREF oldTextColor, oldBkColor;
674 hPrevBrush = SelectObject(hdc, GetSysColorBrush(COLOR_BTNFACE));
677 * Draw the button background
682 rectButton.right-rectButton.left,
683 rectButton.bottom-rectButton.top,
686 if( (bBool = lphc->wState & CBF_BUTTONDOWN) )
688 DrawEdge( hdc, &rectButton, EDGE_SUNKEN, BF_RECT );
692 DrawEdge( hdc, &rectButton, EDGE_RAISED, BF_RECT );
696 * Remove the edge of the button from the rectangle
697 * and calculate the position of the bitmap.
699 InflateRect( &rectButton, -2, -2);
701 x = (rectButton.left + rectButton.right - CBitWidth) >> 1;
702 y = (rectButton.top + rectButton.bottom - CBitHeight) >> 1;
705 hMemDC = CreateCompatibleDC( hdc );
706 SelectObject( hMemDC, hComboBmp );
707 oldTextColor = SetTextColor( hdc, GetSysColor(COLOR_BTNFACE) );
708 oldBkColor = SetBkColor( hdc, CB_DISABLED(lphc) ? RGB(128,128,128) :
710 BitBlt( hdc, x, y, CBitWidth, CBitHeight, hMemDC, 0, 0, SRCCOPY );
711 SetBkColor( hdc, oldBkColor );
712 SetTextColor( hdc, oldTextColor );
714 SelectObject( hdc, hPrevBrush );
718 UINT buttonState = DFCS_SCROLLCOMBOBOX;
720 if (lphc->wState & CBF_BUTTONDOWN)
722 buttonState |= DFCS_PUSHED;
725 DrawFrameControl(hdc,
732 /***********************************************************************
735 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
737 static void CBPaintText(
745 if( lphc->wState & CBF_NOREDRAW ) return;
747 /* follow Windows combobox that sends a bunch of text
748 * inquiries to its listbox while processing WM_PAINT. */
750 if( (id = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
752 size = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
753 if( (pText = HeapAlloc( GetProcessHeap(), 0, size + 1)) )
755 SendMessageA( lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText );
756 pText[size] = '\0'; /* just in case */
760 if( lphc->wState & CBF_EDIT )
762 if( CB_HASSTRINGS(lphc) ) SetWindowTextA( lphc->hWndEdit, pText ? pText : "" );
763 if( lphc->wState & CBF_FOCUSED )
764 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
766 else /* paint text field ourselves */
768 HBRUSH hPrevBrush = 0;
773 if ((hDC = GetDC(lphc->self->hwndSelf)))
775 HBRUSH hBrush = SendMessageA( lphc->owner,
777 hDC, lphc->self->hwndSelf );
778 hPrevBrush = SelectObject( hDC,
779 (hBrush) ? hBrush : GetStockObject(WHITE_BRUSH) );
785 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hDC, lphc->hFont) : 0;
788 * Give ourselves some space.
790 InflateRect( &rectEdit, -1, -1 );
792 if ( (lphc->wState & CBF_FOCUSED) &&
793 !(lphc->wState & CBF_DROPPED) )
797 FillRect( hDC, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
798 SetBkColor( hDC, GetSysColor( COLOR_HIGHLIGHT ) );
799 SetTextColor( hDC, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
800 itemState = ODS_SELECTED | ODS_FOCUS;
805 if( CB_OWNERDRAWN(lphc) )
811 * Save the current clip region.
812 * To retrieve the clip region, we need to create one "dummy"
815 clipRegion = CreateRectRgnIndirect(&rectEdit);
817 if (GetClipRgn(hDC, clipRegion)!=1)
819 DeleteObject(clipRegion);
820 clipRegion=(HRGN)NULL;
823 if ( lphc->self->dwStyle & WS_DISABLED )
824 itemState |= ODS_DISABLED;
826 dis.CtlType = ODT_COMBOBOX;
827 dis.CtlID = lphc->self->wIDmenu;
828 dis.hwndItem = lphc->self->hwndSelf;
829 dis.itemAction = ODA_DRAWENTIRE;
831 dis.itemState = itemState;
833 dis.rcItem = rectEdit;
834 dis.itemData = SendMessageA( lphc->hWndLBox, LB_GETITEMDATA,
838 * Clip the DC and have the parent draw the item.
840 IntersectClipRect(hDC,
841 rectEdit.left, rectEdit.top,
842 rectEdit.right, rectEdit.bottom);
844 SendMessageA(lphc->owner, WM_DRAWITEM,
845 lphc->self->wIDmenu, (LPARAM)&dis );
848 * Reset the clipping region.
850 SelectClipRgn(hDC, clipRegion);
857 ETO_OPAQUE | ETO_CLIPPED,
859 pText ? pText : "" , size, NULL );
861 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
862 DrawFocusRect( hDC, &rectEdit );
866 SelectObject(hDC, hPrevFont );
871 SelectObject( hDC, hPrevBrush );
873 ReleaseDC( lphc->self->hwndSelf, hDC );
878 HeapFree( GetProcessHeap(), 0, pText );
881 /***********************************************************************
884 static void CBPaintBorder(
891 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
893 GetClientRect(hwnd, &clientRect);
897 CopyRect(&clientRect, &lphc->textRect);
899 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
900 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
903 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
906 /***********************************************************************
907 * COMBO_EraseBackground
909 static LRESULT COMBO_EraseBackground(
918 hDC = (hParamDC) ? hParamDC
922 * Calculate the area that we want to erase.
924 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
926 GetClientRect(hwnd, &clientRect);
930 CopyRect(&clientRect, &lphc->textRect);
932 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
935 hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
939 hBkgBrush = GetStockObject(WHITE_BRUSH);
941 FillRect(hDC, &clientRect, hBkgBrush);
944 ReleaseDC(hwnd, hDC);
949 /***********************************************************************
952 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
957 hDC = (hParamDC) ? hParamDC
958 : BeginPaint( lphc->self->hwndSelf, &ps);
961 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
963 HBRUSH hPrevBrush, hBkgBrush;
965 hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
966 hDC, lphc->self->hwndSelf );
969 hBkgBrush = GetStockObject(WHITE_BRUSH);
971 hPrevBrush = SelectObject( hDC, hBkgBrush );
974 * In non 3.1 look, there is a sunken border on the combobox
976 if (TWEAK_WineLook != WIN31_LOOK)
978 CBPaintBorder(CB_HWND(lphc), lphc, hDC);
981 if( !IsRectEmpty(&lphc->buttonRect) )
983 CBPaintButton(lphc, hDC, lphc->buttonRect);
986 if( !(lphc->wState & CBF_EDIT) )
989 * The text area has a border only in Win 3.1 look.
991 if (TWEAK_WineLook == WIN31_LOOK)
993 HPEN hPrevPen = SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
996 lphc->textRect.left, lphc->textRect.top,
997 lphc->textRect.right - 1, lphc->textRect.bottom - 1);
999 SelectObject( hDC, hPrevPen );
1002 CBPaintText( lphc, hDC, lphc->textRect);
1006 SelectObject( hDC, hPrevBrush );
1010 EndPaint(lphc->self->hwndSelf, &ps);
1015 /***********************************************************************
1018 * Select listbox entry according to the contents of the edit control.
1020 static INT CBUpdateLBox( LPHEADCOMBO lphc )
1022 INT length, idx, ret;
1026 length = CB_GETEDITTEXTLENGTH( lphc );
1029 pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1);
1031 TRACE("\t edit text length %i\n", length );
1035 if( length ) GetWindowTextA( lphc->hWndEdit, pText, length + 1);
1036 else pText[0] = '\0';
1037 idx = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
1038 (WPARAM)(-1), (LPARAM)pText );
1039 if( idx == LB_ERR ) idx = 0; /* select first item */
1041 HeapFree( GetProcessHeap(), 0, pText );
1046 SendMessageA( lphc->hWndLBox, LB_SETCURSEL, (WPARAM)idx, 0 );
1050 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)idx, 0 );
1051 /* probably superfluous but Windows sends this too */
1052 SendMessageA( lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)idx, 0 );
1057 /***********************************************************************
1060 * Copy a listbox entry to the edit control.
1062 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
1067 TRACE("\t %i\n", index );
1071 length = CB_GETEDITTEXTLENGTH( lphc );
1074 if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
1076 GetWindowTextA( lphc->hWndEdit, pText, length + 1 );
1077 index = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
1078 (WPARAM)(-1), (LPARAM)pText );
1079 HeapFree( GetProcessHeap(), 0, pText );
1084 if( index >= 0 ) /* got an entry */
1086 length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
1089 if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
1091 SendMessageA( lphc->hWndLBox, LB_GETTEXT,
1092 (WPARAM)index, (LPARAM)pText );
1094 lphc->wState |= CBF_NOEDITNOTIFY;
1096 SendMessageA( lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)pText );
1097 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
1098 HeapFree( GetProcessHeap(), 0, pText );
1104 /***********************************************************************
1107 * Show listbox popup.
1109 static void CBDropDown( LPHEADCOMBO lphc )
1117 TRACE("[%04x]: drop down\n", CB_HWND(lphc));
1119 CB_NOTIFY( lphc, CBN_DROPDOWN );
1123 lphc->wState |= CBF_DROPPED;
1124 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1126 lphc->droppedIndex = CBUpdateLBox( lphc );
1128 if( !(lphc->wState & CBF_CAPTURE) )
1129 CBUpdateEdit( lphc, lphc->droppedIndex );
1133 lphc->droppedIndex = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1135 if( lphc->droppedIndex == LB_ERR )
1136 lphc->droppedIndex = 0;
1138 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)lphc->droppedIndex, 0 );
1139 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1142 /* now set popup position */
1143 GetWindowRect( lphc->self->hwndSelf, &rect );
1146 * If it's a dropdown, the listbox is offset
1148 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1149 rect.left += COMBO_EDITBUTTONSPACE();
1151 /* if the dropped height is greater than the total height of the dropped
1152 items list, then force the drop down list height to be the total height
1153 of the items in the dropped list */
1155 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1156 nItems = (int)SendMessageA (lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1157 nHeight = COMBO_YBORDERGAP;
1158 for (i = 0; i < nItems; i++)
1160 nHeight += (int)SendMessageA (lphc->hWndLBox, LB_GETITEMHEIGHT, i, 0);
1162 if (nHeight >= nDroppedHeight)
1166 if (nHeight < nDroppedHeight)
1167 nDroppedHeight = nHeight;
1169 SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1170 lphc->droppedRect.right - lphc->droppedRect.left,
1172 SWP_NOACTIVATE | SWP_NOREDRAW);
1174 if( !(lphc->wState & CBF_NOREDRAW) )
1175 RedrawWindow( lphc->self->hwndSelf, NULL, 0, RDW_INVALIDATE |
1176 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1178 ShowWindow( lphc->hWndLBox, SW_SHOWNA );
1181 /***********************************************************************
1184 * Hide listbox popup.
1186 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1188 HWND hWnd = lphc->self->hwndSelf;
1190 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1192 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1195 TRACE("[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT)ok );
1197 if( lphc->wState & CBF_DROPPED )
1202 * It seems useful to send the WM_LBUTTONUP with (-1,-1) when cancelling
1203 * and with (0,0) (anywhere in the listbox) when Oking.
1205 SendMessageA( lphc->hWndLBox, WM_LBUTTONUP, 0, ok ? (LPARAM)0 : (LPARAM)(-1) );
1207 lphc->wState &= ~CBF_DROPPED;
1208 ShowWindow( lphc->hWndLBox, SW_HIDE );
1210 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1212 INT index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1213 CBUpdateEdit( lphc, index );
1214 rect = lphc->buttonRect;
1225 rect = lphc->textRect;
1230 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1231 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1232 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1233 CB_NOTIFY( lphc, CBN_CLOSEUP );
1238 /***********************************************************************
1241 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1243 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL bRedrawButton )
1245 if( lphc->wState & CBF_DROPPED )
1247 CBRollUp( lphc, TRUE, bRedrawButton );
1255 /***********************************************************************
1258 * Edit control helper.
1260 HWND COMBO_GetLBWindow( WND* pWnd )
1262 LPHEADCOMBO lphc = CB_GETPTR(pWnd);
1263 if( lphc ) return lphc->hWndLBox;
1268 /***********************************************************************
1271 static void CBRepaintButton( LPHEADCOMBO lphc )
1273 InvalidateRect(CB_HWND(lphc), &lphc->buttonRect, TRUE);
1274 UpdateWindow(CB_HWND(lphc));
1277 /***********************************************************************
1280 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1282 if( !(lphc->wState & CBF_FOCUSED) )
1284 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1285 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1287 if( lphc->wState & CBF_EDIT )
1288 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
1289 lphc->wState |= CBF_FOCUSED;
1290 if( !(lphc->wState & CBF_EDIT) )
1292 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1295 CB_NOTIFY( lphc, CBN_SETFOCUS );
1299 /***********************************************************************
1302 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1304 HWND hWnd = lphc->self->hwndSelf;
1306 if( lphc->wState & CBF_FOCUSED )
1308 SendMessageA( hWnd, WM_LBUTTONUP, 0, (LPARAM)(-1) );
1310 CBRollUp( lphc, FALSE, TRUE );
1311 if( IsWindow( hWnd ) )
1313 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1314 SendMessageA( lphc->hWndLBox, LB_CARETOFF, 0, 0 );
1316 lphc->wState &= ~CBF_FOCUSED;
1319 if( lphc->wState & CBF_EDIT )
1320 SendMessageA( lphc->hWndEdit, EM_SETSEL, (WPARAM)(-1), 0 );
1323 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1326 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1331 /***********************************************************************
1334 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1336 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1338 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1340 switch( HIWORD(wParam) >> 8 )
1342 case (EN_SETFOCUS >> 8):
1344 TRACE("[%04x]: edit [%04x] got focus\n",
1345 CB_HWND(lphc), lphc->hWndEdit );
1347 if( !(lphc->wState & CBF_FOCUSED) ) COMBO_SetFocus( lphc );
1350 case (EN_KILLFOCUS >> 8):
1352 TRACE("[%04x]: edit [%04x] lost focus\n",
1353 CB_HWND(lphc), lphc->hWndEdit );
1355 /* NOTE: it seems that Windows' edit control sends an
1356 * undocumented message WM_USER + 0x1B instead of this
1357 * notification (only when it happens to be a part of
1358 * the combo). ?? - AK.
1361 COMBO_KillFocus( lphc );
1365 case (EN_CHANGE >> 8):
1367 * In some circumstances (when the selection of the combobox
1368 * is changed for example) we don't wans the EN_CHANGE notification
1369 * to be forwarded to the parent of the combobox. This code
1370 * checks a flag that is set in these occasions and ignores the
1373 if (lphc->wState & CBF_NOEDITNOTIFY)
1375 lphc->wState &= ~CBF_NOEDITNOTIFY;
1379 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1382 CBUpdateLBox( lphc );
1385 case (EN_UPDATE >> 8):
1386 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1389 case (EN_ERRSPACE >> 8):
1390 CB_NOTIFY( lphc, CBN_ERRSPACE );
1393 else if( lphc->hWndLBox == hWnd )
1395 switch( HIWORD(wParam) )
1398 CB_NOTIFY( lphc, CBN_ERRSPACE );
1402 CB_NOTIFY( lphc, CBN_DBLCLK );
1408 TRACE("[%04x]: lbox selection change [%04x]\n",
1409 CB_HWND(lphc), lphc->wState );
1411 /* do not roll up if selection is being tracked
1412 * by arrowkeys in the dropdown listbox */
1414 if( (lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP) )
1415 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1416 else lphc->wState &= ~CBF_NOROLLUP;
1418 CB_NOTIFY( lphc, CBN_SELCHANGE );
1419 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1424 /* nothing to do here since ComboLBox always resets the focus to its
1425 * combo/edit counterpart */
1432 /***********************************************************************
1435 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1437 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg,
1438 WPARAM wParam, LPARAM lParam )
1440 HWND hWnd = lphc->self->hwndSelf;
1442 TRACE("[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg );
1444 #define lpIS ((LPDELETEITEMSTRUCT)lParam)
1446 /* two first items are the same in all 4 structs */
1447 lpIS->CtlType = ODT_COMBOBOX;
1448 lpIS->CtlID = lphc->self->wIDmenu;
1450 switch( msg ) /* patch window handle */
1453 lpIS->hwndItem = hWnd;
1457 #define lpIS ((LPDRAWITEMSTRUCT)lParam)
1458 lpIS->hwndItem = hWnd;
1461 case WM_COMPAREITEM:
1462 #define lpIS ((LPCOMPAREITEMSTRUCT)lParam)
1463 lpIS->hwndItem = hWnd;
1468 return SendMessageA( lphc->owner, msg, lphc->self->wIDmenu, lParam );
1471 /***********************************************************************
1474 static LRESULT COMBO_GetText( LPHEADCOMBO lphc, UINT N, LPSTR lpText)
1476 if( lphc->wState & CBF_EDIT )
1477 return SendMessageA( lphc->hWndEdit, WM_GETTEXT,
1478 (WPARAM)N, (LPARAM)lpText );
1480 /* get it from the listbox */
1482 if( lphc->hWndLBox )
1484 INT idx = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1488 INT length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN,
1491 /* 'length' is without the terminating character */
1493 lpBuffer = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1 );
1499 INT n = SendMessageA( lphc->hWndLBox, LB_GETTEXT,
1500 (WPARAM)idx, (LPARAM)lpBuffer );
1502 /* truncate if buffer is too short */
1507 if( n != LB_ERR ) memcpy( lpText, lpBuffer, (N>n) ? n+1 : N-1 );
1508 lpText[N - 1] = '\0';
1510 HeapFree( GetProcessHeap(), 0, lpBuffer );
1520 /***********************************************************************
1523 * This function sets window positions according to the updated
1524 * component placement struct.
1526 static void CBResetPos(
1532 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1534 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1535 * sizing messages */
1537 if( lphc->wState & CBF_EDIT )
1538 SetWindowPos( lphc->hWndEdit, 0,
1539 rectEdit->left, rectEdit->top,
1540 rectEdit->right - rectEdit->left,
1541 rectEdit->bottom - rectEdit->top,
1542 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1544 SetWindowPos( lphc->hWndLBox, 0,
1545 rectLB->left, rectLB->top,
1546 rectLB->right - rectLB->left,
1547 rectLB->bottom - rectLB->top,
1548 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1552 if( lphc->wState & CBF_DROPPED )
1554 lphc->wState &= ~CBF_DROPPED;
1555 ShowWindow( lphc->hWndLBox, SW_HIDE );
1558 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1559 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
1560 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1565 /***********************************************************************
1568 static void COMBO_Size( LPHEADCOMBO lphc )
1570 CBCalcPlacement(lphc->self->hwndSelf,
1574 &lphc->droppedRect);
1576 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1580 /***********************************************************************
1583 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1588 lphc->hFont = hFont;
1591 * Propagate to owned windows.
1593 if( lphc->wState & CBF_EDIT )
1594 SendMessageA( lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw );
1595 SendMessageA( lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw );
1598 * Redo the layout of the control.
1600 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1602 CBCalcPlacement(lphc->self->hwndSelf,
1606 &lphc->droppedRect);
1608 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1612 CBForceDummyResize(lphc);
1617 /***********************************************************************
1618 * COMBO_SetItemHeight
1620 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1622 LRESULT lRet = CB_ERR;
1624 if( index == -1 ) /* set text field height */
1626 if( height < 32768 )
1628 lphc->editHeight = height;
1631 * Redo the layout of the control.
1633 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1635 CBCalcPlacement(lphc->self->hwndSelf,
1639 &lphc->droppedRect);
1641 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1645 CBForceDummyResize(lphc);
1651 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1652 lRet = SendMessageA( lphc->hWndLBox, LB_SETITEMHEIGHT,
1653 (WPARAM)index, (LPARAM)height );
1657 /***********************************************************************
1658 * COMBO_SelectString
1660 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPCSTR pText )
1662 INT index = SendMessageA( lphc->hWndLBox, LB_SELECTSTRING,
1663 (WPARAM)start, (LPARAM)pText );
1666 if( lphc->wState & CBF_EDIT )
1667 CBUpdateEdit( lphc, index );
1670 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1673 return (LRESULT)index;
1676 /***********************************************************************
1679 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1683 HWND hWnd = lphc->self->hwndSelf;
1685 pt.x = LOWORD(lParam);
1686 pt.y = HIWORD(lParam);
1687 bButton = PtInRect(&lphc->buttonRect, pt);
1689 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1690 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1692 lphc->wState |= CBF_BUTTONDOWN;
1693 if( lphc->wState & CBF_DROPPED )
1695 /* got a click to cancel selection */
1697 lphc->wState &= ~CBF_BUTTONDOWN;
1698 CBRollUp( lphc, TRUE, FALSE );
1699 if( !IsWindow( hWnd ) ) return;
1701 if( lphc->wState & CBF_CAPTURE )
1703 lphc->wState &= ~CBF_CAPTURE;
1709 /* drop down the listbox and start tracking */
1711 lphc->wState |= CBF_CAPTURE;
1715 if( bButton ) CBRepaintButton( lphc );
1719 /***********************************************************************
1722 * Release capture and stop tracking if needed.
1724 static void COMBO_LButtonUp( LPHEADCOMBO lphc, LPARAM lParam )
1726 if( lphc->wState & CBF_CAPTURE )
1728 lphc->wState &= ~CBF_CAPTURE;
1729 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1731 INT index = CBUpdateLBox( lphc );
1732 CBUpdateEdit( lphc, index );
1737 if( lphc->wState & CBF_BUTTONDOWN )
1739 lphc->wState &= ~CBF_BUTTONDOWN;
1740 CBRepaintButton( lphc );
1744 /***********************************************************************
1747 * Two things to do - track combo button and release capture when
1748 * pointer goes into the listbox.
1750 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1755 pt.x = LOWORD(lParam);
1756 pt.y = HIWORD(lParam);
1758 if( lphc->wState & CBF_BUTTONDOWN )
1762 bButton = PtInRect(&lphc->buttonRect, pt);
1766 lphc->wState &= ~CBF_BUTTONDOWN;
1767 CBRepaintButton( lphc );
1771 GetClientRect( lphc->hWndLBox, &lbRect );
1772 MapWindowPoints( lphc->self->hwndSelf, lphc->hWndLBox, &pt, 1 );
1773 if( PtInRect(&lbRect, pt) )
1775 lphc->wState &= ~CBF_CAPTURE;
1777 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc );
1779 /* hand over pointer tracking */
1780 SendMessageA( lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam );
1785 /***********************************************************************
1786 * ComboWndProc_locked
1788 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1790 static inline LRESULT WINAPI ComboWndProc_locked( WND* pWnd, UINT message,
1791 WPARAM wParam, LPARAM lParam )
1794 LPHEADCOMBO lphc = CB_GETPTR(pWnd);
1795 HWND hwnd = pWnd->hwndSelf;
1797 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
1798 pWnd->hwndSelf, SPY_GetMsgName(message), wParam, lParam );
1800 if( lphc || message == WM_NCCREATE )
1804 /* System messages */
1807 return COMBO_NCCreate(pWnd, lParam);
1809 COMBO_NCDestroy(lphc);
1810 break;/* -> DefWindowProc */
1813 return COMBO_Create(lphc, pWnd, lParam);
1815 case WM_PRINTCLIENT:
1816 if (lParam & PRF_ERASEBKGND)
1817 COMBO_EraseBackground(hwnd, lphc, wParam);
1821 /* wParam may contain a valid HDC! */
1822 return COMBO_Paint(lphc, wParam);
1824 return COMBO_EraseBackground(hwnd, lphc, wParam);
1826 return (LRESULT)(DLGC_WANTARROWS | DLGC_WANTCHARS);
1827 case WM_WINDOWPOSCHANGING:
1828 return COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1830 if( lphc->hWndLBox &&
1831 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1834 COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
1837 return (LRESULT)lphc->hFont;
1839 if( lphc->wState & CBF_EDIT )
1840 SetFocus( lphc->hWndEdit );
1842 COMBO_SetFocus( lphc );
1845 #define hwndFocus ((HWND16)wParam)
1847 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1848 COMBO_KillFocus( lphc );
1852 return COMBO_Command( lphc, wParam, (HWND)lParam );
1854 return COMBO_GetText( lphc, (UINT)wParam, (LPSTR)lParam );
1856 case WM_GETTEXTLENGTH:
1861 if( lphc->wState & CBF_EDIT )
1863 lphc->wState |= CBF_NOEDITNOTIFY;
1865 return SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1870 case WM_COMPAREITEM:
1871 case WM_MEASUREITEM:
1872 return COMBO_ItemOp( lphc, message, wParam, lParam );
1874 if( lphc->wState & CBF_EDIT )
1875 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1876 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1880 lphc->wState &= ~CBF_NOREDRAW;
1882 lphc->wState |= CBF_NOREDRAW;
1884 if( lphc->wState & CBF_EDIT )
1885 SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1886 SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1889 if( KEYDATA_ALT & HIWORD(lParam) )
1890 if( wParam == VK_UP || wParam == VK_DOWN )
1891 COMBO_FlipListbox( lphc, TRUE );
1892 break;/* -> DefWindowProc */
1896 if( lphc->wState & CBF_EDIT )
1897 return SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1899 return SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1900 case WM_LBUTTONDOWN:
1901 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self->hwndSelf );
1902 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1905 COMBO_LButtonUp( lphc, lParam );
1908 if( lphc->wState & CBF_CAPTURE )
1909 COMBO_MouseMove( lphc, wParam, lParam );
1911 /* Combo messages */
1913 case CB_ADDSTRING16:
1914 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1916 return SendMessageA( lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
1917 case CB_INSERTSTRING16:
1918 wParam = (INT)(INT16)wParam;
1919 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1920 case CB_INSERTSTRING:
1921 return SendMessageA( lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1922 case CB_DELETESTRING16:
1923 case CB_DELETESTRING:
1924 return SendMessageA( lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1925 case CB_SELECTSTRING16:
1926 wParam = (INT)(INT16)wParam;
1927 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1928 case CB_SELECTSTRING:
1929 return COMBO_SelectString( lphc, (INT)wParam, (LPSTR)lParam );
1930 case CB_FINDSTRING16:
1931 wParam = (INT)(INT16)wParam;
1932 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1934 return SendMessageA( lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1935 case CB_FINDSTRINGEXACT16:
1936 wParam = (INT)(INT16)wParam;
1937 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1938 case CB_FINDSTRINGEXACT:
1939 return SendMessageA( lphc->hWndLBox, LB_FINDSTRINGEXACT,
1941 case CB_SETITEMHEIGHT16:
1942 wParam = (INT)(INT16)wParam; /* signed integer */
1943 case CB_SETITEMHEIGHT:
1944 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
1945 case CB_GETITEMHEIGHT16:
1946 wParam = (INT)(INT16)wParam;
1947 case CB_GETITEMHEIGHT:
1948 if( (INT)wParam >= 0 ) /* listbox item */
1949 return SendMessageA( lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
1950 return CBGetTextAreaHeight(hwnd, lphc);
1951 case CB_RESETCONTENT16:
1952 case CB_RESETCONTENT:
1953 SendMessageA( lphc->hWndLBox, LB_RESETCONTENT, 0, 0 );
1954 InvalidateRect(CB_HWND(lphc), NULL, TRUE);
1956 case CB_INITSTORAGE:
1957 return SendMessageA( lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
1958 case CB_GETHORIZONTALEXTENT:
1959 return SendMessageA( lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
1960 case CB_SETHORIZONTALEXTENT:
1961 return SendMessageA( lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
1962 case CB_GETTOPINDEX:
1963 return SendMessageA( lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
1965 return SendMessageA( lphc->hWndLBox, LB_GETLOCALE, 0, 0);
1967 return SendMessageA( lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
1968 case CB_GETDROPPEDWIDTH:
1969 if( lphc->droppedWidth )
1970 return lphc->droppedWidth;
1971 return lphc->droppedRect.right - lphc->droppedRect.left;
1972 case CB_SETDROPPEDWIDTH:
1973 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
1974 (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
1976 case CB_GETDROPPEDCONTROLRECT16:
1977 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1981 CBGetDroppedControlRect( lphc, &r );
1982 CONV_RECT32TO16( &r, (LPRECT16)lParam );
1985 case CB_GETDROPPEDCONTROLRECT:
1986 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
1988 case CB_GETDROPPEDSTATE16:
1989 case CB_GETDROPPEDSTATE:
1990 return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
1992 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1995 return COMBO_Directory( lphc, (UINT)wParam,
1996 (LPSTR)lParam, (message == CB_DIR));
1997 case CB_SHOWDROPDOWN16:
1998 case CB_SHOWDROPDOWN:
1999 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2003 if( !(lphc->wState & CBF_DROPPED) )
2007 if( lphc->wState & CBF_DROPPED )
2008 CBRollUp( lphc, FALSE, TRUE );
2013 return SendMessageA( lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2014 case CB_GETCURSEL16:
2016 return SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2017 case CB_SETCURSEL16:
2018 wParam = (INT)(INT16)wParam;
2020 lParam = SendMessageA( lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2021 if( lphc->wState & CBF_SELCHANGE )
2023 /* no LBN_SELCHANGE in this case, update manually */
2024 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
2025 lphc->wState &= ~CBF_SELCHANGE;
2028 case CB_GETLBTEXT16:
2029 wParam = (INT)(INT16)wParam;
2030 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2032 return SendMessageA( lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2033 case CB_GETLBTEXTLEN16:
2034 wParam = (INT)(INT16)wParam;
2035 case CB_GETLBTEXTLEN:
2036 return SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2037 case CB_GETITEMDATA16:
2038 wParam = (INT)(INT16)wParam;
2039 case CB_GETITEMDATA:
2040 return SendMessageA( lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2041 case CB_SETITEMDATA16:
2042 wParam = (INT)(INT16)wParam;
2043 case CB_SETITEMDATA:
2044 return SendMessageA( lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2045 case CB_GETEDITSEL16:
2046 wParam = lParam = 0; /* just in case */
2048 if( lphc->wState & CBF_EDIT )
2052 return SendMessageA( lphc->hWndEdit, EM_GETSEL,
2053 (wParam) ? wParam : (WPARAM)&a,
2054 (lParam) ? lParam : (LPARAM)&b );
2057 case CB_SETEDITSEL16:
2059 if( lphc->wState & CBF_EDIT )
2060 return SendMessageA( lphc->hWndEdit, EM_SETSEL,
2061 (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2063 case CB_SETEXTENDEDUI16:
2064 case CB_SETEXTENDEDUI:
2065 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2068 lphc->wState |= CBF_EUI;
2069 else lphc->wState &= ~CBF_EUI;
2071 case CB_GETEXTENDEDUI16:
2072 case CB_GETEXTENDEDUI:
2073 return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2074 case (WM_USER + 0x1B):
2075 WARN("[%04x]: undocumented msg!\n", hwnd );
2077 return DefWindowProcA(hwnd, message, wParam, lParam);
2082 /***********************************************************************
2085 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2088 LRESULT WINAPI ComboWndProc( HWND hwnd, UINT message,
2089 WPARAM wParam, LPARAM lParam )
2091 WND* pWnd = WIN_FindWndPtr(hwnd);
2092 LRESULT retvalue = ComboWndProc_locked(pWnd,message,wParam,lParam);
2095 WIN_ReleaseWndPtr(pWnd);