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) )
494 * Initialize the dropped rect to the size of the client area of the
495 * control and then, force all the areas of the combobox to be
498 GetClientRect( wnd->hwndSelf, &lphc->droppedRect );
500 CBCalcPlacement(wnd->hwndSelf,
504 &lphc->droppedRect );
507 * Adjust the position of the popup listbox if it's necessary
509 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
511 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
514 * If it's a dropdown, the listbox is offset
516 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
517 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
519 ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect);
520 ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect.right);
523 /* create listbox popup */
525 lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS) |
526 (lpcs->style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
528 if( lphc->dwStyle & CBS_SORT )
529 lbeStyle |= LBS_SORT;
530 if( lphc->dwStyle & CBS_HASSTRINGS )
531 lbeStyle |= LBS_HASSTRINGS;
532 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
533 lbeStyle |= LBS_NOINTEGRALHEIGHT;
534 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
535 lbeStyle |= LBS_DISABLENOSCROLL;
537 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
538 lbeStyle |= WS_CHILD | WS_VISIBLE;
539 else /* popup listbox */
540 lbeStyle |= WS_POPUP;
542 /* Dropdown ComboLBox is not a child window and we cannot pass
543 * ID_CB_LISTBOX directly because it will be treated as a menu handle.
545 lphc->hWndLBox = CreateWindowExA(0,
549 lphc->droppedRect.left,
550 lphc->droppedRect.top,
551 lphc->droppedRect.right - lphc->droppedRect.left,
552 lphc->droppedRect.bottom - lphc->droppedRect.top,
553 lphc->self->hwndSelf,
554 (lphc->dwStyle & CBS_DROPDOWN)? (HMENU)0 : (HMENU)ID_CB_LISTBOX,
555 lphc->self->hInstance,
559 * The ComboLBox is a strange little beast (when it's not a CBS_SIMPLE)...
560 * It's a popup window but, when you get the window style, you get WS_CHILD.
561 * When created, it's parent is the combobox but, when you ask for it's parent
562 * after that, you're supposed to get the desktop. (see MFC code function
564 * To achieve this in Wine, we have to create it as a popup and change
565 * it's style to child after the creation.
567 if ( (lphc->hWndLBox!= 0) &&
568 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
570 SetWindowLongA(lphc->hWndLBox,
572 (GetWindowLongA(lphc->hWndLBox, GWL_STYLE) | WS_CHILD) & ~WS_POPUP);
578 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT;
581 * In Win95 look, the border fo the edit control is
582 * provided by the combobox
584 if (TWEAK_WineLook == WIN31_LOOK)
585 lbeStyle |= WS_BORDER;
587 if( lphc->wState & CBF_EDIT )
589 if( lphc->dwStyle & CBS_OEMCONVERT )
590 lbeStyle |= ES_OEMCONVERT;
591 if( lphc->dwStyle & CBS_AUTOHSCROLL )
592 lbeStyle |= ES_AUTOHSCROLL;
593 if( lphc->dwStyle & CBS_LOWERCASE )
594 lbeStyle |= ES_LOWERCASE;
595 else if( lphc->dwStyle & CBS_UPPERCASE )
596 lbeStyle |= ES_UPPERCASE;
598 lphc->hWndEdit = CreateWindowExA(0,
602 lphc->textRect.left, lphc->textRect.top,
603 lphc->textRect.right - lphc->textRect.left,
604 lphc->textRect.bottom - lphc->textRect.top,
605 lphc->self->hwndSelf,
607 lphc->self->hInstance,
610 if( !lphc->hWndEdit )
617 * If the combo is a dropdown, we must resize the control to fit only
618 * the text area and button. To do this, we send a dummy resize and the
619 * WM_WINDOWPOSCHANGING message will take care of setting the height for
622 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
624 CBForceDummyResize(lphc);
627 TRACE("init done\n");
628 return wnd->hwndSelf;
630 ERR("edit control failure.\n");
631 } else ERR("listbox failure.\n");
632 } else ERR("no owner for visible combo.\n");
634 /* CreateWindow() will send WM_NCDESTROY to cleanup */
639 /***********************************************************************
642 * Paint combo button (normal, pressed, and disabled states).
644 static void CBPaintButton(
653 COLORREF oldTextColor, oldBkColor;
655 if( lphc->wState & CBF_NOREDRAW )
658 hPrevBrush = SelectObject(hdc, GetSysColorBrush(COLOR_BTNFACE));
661 * Draw the button background
666 rectButton.right-rectButton.left,
667 rectButton.bottom-rectButton.top,
670 if( (bBool = lphc->wState & CBF_BUTTONDOWN) )
672 DrawEdge( hdc, &rectButton, EDGE_SUNKEN, BF_RECT );
676 DrawEdge( hdc, &rectButton, EDGE_RAISED, BF_RECT );
680 * Remove the edge of the button from the rectangle
681 * and calculate the position of the bitmap.
683 InflateRect( &rectButton, -2, -2);
685 x = (rectButton.left + rectButton.right - CBitWidth) >> 1;
686 y = (rectButton.top + rectButton.bottom - CBitHeight) >> 1;
689 hMemDC = CreateCompatibleDC( hdc );
690 SelectObject( hMemDC, hComboBmp );
691 oldTextColor = SetTextColor( hdc, GetSysColor(COLOR_BTNFACE) );
692 oldBkColor = SetBkColor( hdc, CB_DISABLED(lphc) ? RGB(128,128,128) :
694 BitBlt( hdc, x, y, CBitWidth, CBitHeight, hMemDC, 0, 0, SRCCOPY );
695 SetBkColor( hdc, oldBkColor );
696 SetTextColor( hdc, oldTextColor );
698 SelectObject( hdc, hPrevBrush );
701 /***********************************************************************
704 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
706 static void CBPaintText(
714 if( lphc->wState & CBF_NOREDRAW ) return;
716 /* follow Windows combobox that sends a bunch of text
717 * inquiries to its listbox while processing WM_PAINT. */
719 if( (id = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
721 size = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
722 if( (pText = HeapAlloc( GetProcessHeap(), 0, size + 1)) )
724 SendMessageA( lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText );
725 pText[size] = '\0'; /* just in case */
729 if( lphc->wState & CBF_EDIT )
731 if( CB_HASSTRINGS(lphc) ) SetWindowTextA( lphc->hWndEdit, pText ? pText : "" );
732 if( lphc->wState & CBF_FOCUSED )
733 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
735 else /* paint text field ourselves */
737 HBRUSH hPrevBrush = 0;
742 if ((hDC = GetDC(lphc->self->hwndSelf)))
744 HBRUSH hBrush = SendMessageA( lphc->owner,
746 hDC, lphc->self->hwndSelf );
747 hPrevBrush = SelectObject( hDC,
748 (hBrush) ? hBrush : GetStockObject(WHITE_BRUSH) );
754 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hDC, lphc->hFont) : 0;
757 * Give ourselves some space.
759 InflateRect( &rectEdit, -1, -1 );
761 if ( (lphc->wState & CBF_FOCUSED) &&
762 !(lphc->wState & CBF_DROPPED) )
766 FillRect( hDC, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
767 SetBkColor( hDC, GetSysColor( COLOR_HIGHLIGHT ) );
768 SetTextColor( hDC, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
769 itemState = ODS_SELECTED | ODS_FOCUS;
774 if( CB_OWNERDRAWN(lphc) )
780 * Save the current clip region.
781 * To retrieve the clip region, we need to create one "dummy"
784 clipRegion = CreateRectRgnIndirect(&rectEdit);
786 if (GetClipRgn(hDC, clipRegion)!=1)
788 DeleteObject(clipRegion);
789 clipRegion=(HRGN)NULL;
792 if ( lphc->self->dwStyle & WS_DISABLED )
793 itemState |= ODS_DISABLED;
795 dis.CtlType = ODT_COMBOBOX;
796 dis.CtlID = lphc->self->wIDmenu;
797 dis.hwndItem = lphc->self->hwndSelf;
798 dis.itemAction = ODA_DRAWENTIRE;
800 dis.itemState = itemState;
802 dis.rcItem = rectEdit;
803 dis.itemData = SendMessageA( lphc->hWndLBox, LB_GETITEMDATA,
807 * Clip the DC and have the parent draw the item.
809 IntersectClipRect(hDC,
810 rectEdit.left, rectEdit.top,
811 rectEdit.right, rectEdit.bottom);
813 SendMessageA(lphc->owner, WM_DRAWITEM,
814 lphc->self->wIDmenu, (LPARAM)&dis );
817 * Reset the clipping region.
819 SelectClipRgn(hDC, clipRegion);
826 ETO_OPAQUE | ETO_CLIPPED,
828 pText ? pText : "" , size, NULL );
830 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
831 DrawFocusRect( hDC, &rectEdit );
835 SelectObject(hDC, hPrevFont );
840 SelectObject( hDC, hPrevBrush );
842 ReleaseDC( lphc->self->hwndSelf, hDC );
847 HeapFree( GetProcessHeap(), 0, pText );
850 /***********************************************************************
853 static void CBPaintBorder(
860 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
862 GetClientRect(hwnd, &clientRect);
866 CopyRect(&clientRect, &lphc->textRect);
868 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
869 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
872 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
875 /***********************************************************************
876 * COMBO_EraseBackground
878 static LRESULT COMBO_EraseBackground(
887 hDC = (hParamDC) ? hParamDC
891 * Calculate the area that we want to erase.
893 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
895 GetClientRect(hwnd, &clientRect);
899 CopyRect(&clientRect, &lphc->textRect);
901 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
904 hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
908 hBkgBrush = GetStockObject(WHITE_BRUSH);
910 FillRect(hDC, &clientRect, hBkgBrush);
913 ReleaseDC(hwnd, hDC);
918 /***********************************************************************
921 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
926 hDC = (hParamDC) ? hParamDC
927 : BeginPaint( lphc->self->hwndSelf, &ps);
930 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
932 HBRUSH hPrevBrush, hBkgBrush;
934 hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
935 hDC, lphc->self->hwndSelf );
938 hBkgBrush = GetStockObject(WHITE_BRUSH);
940 hPrevBrush = SelectObject( hDC, hBkgBrush );
943 * In non 3.1 look, there is a sunken border on the combobox
945 if (TWEAK_WineLook != WIN31_LOOK)
947 CBPaintBorder(CB_HWND(lphc), lphc, hDC);
950 if( !IsRectEmpty(&lphc->buttonRect) )
952 CBPaintButton(lphc, hDC, lphc->buttonRect);
955 if( !(lphc->wState & CBF_EDIT) )
958 * The text area has a border only in Win 3.1 look.
960 if (TWEAK_WineLook == WIN31_LOOK)
962 HPEN hPrevPen = SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
965 lphc->textRect.left, lphc->textRect.top,
966 lphc->textRect.right - 1, lphc->textRect.bottom - 1);
968 SelectObject( hDC, hPrevPen );
971 CBPaintText( lphc, hDC, lphc->textRect);
975 SelectObject( hDC, hPrevBrush );
979 EndPaint(lphc->self->hwndSelf, &ps);
984 /***********************************************************************
987 * Select listbox entry according to the contents of the edit control.
989 static INT CBUpdateLBox( LPHEADCOMBO lphc )
991 INT length, idx, ret;
995 length = CB_GETEDITTEXTLENGTH( lphc );
998 pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1);
1000 TRACE("\t edit text length %i\n", length );
1004 if( length ) GetWindowTextA( lphc->hWndEdit, pText, length + 1);
1005 else pText[0] = '\0';
1006 idx = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
1007 (WPARAM)(-1), (LPARAM)pText );
1008 if( idx == LB_ERR ) idx = 0; /* select first item */
1010 HeapFree( GetProcessHeap(), 0, pText );
1015 SendMessageA( lphc->hWndLBox, LB_SETCURSEL, (WPARAM)idx, 0 );
1019 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)idx, 0 );
1020 /* probably superfluous but Windows sends this too */
1021 SendMessageA( lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)idx, 0 );
1026 /***********************************************************************
1029 * Copy a listbox entry to the edit control.
1031 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
1036 TRACE("\t %i\n", index );
1040 length = CB_GETEDITTEXTLENGTH( lphc );
1043 if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
1045 GetWindowTextA( lphc->hWndEdit, pText, length + 1 );
1046 index = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
1047 (WPARAM)(-1), (LPARAM)pText );
1048 HeapFree( GetProcessHeap(), 0, pText );
1053 if( index >= 0 ) /* got an entry */
1055 length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
1058 if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
1060 SendMessageA( lphc->hWndLBox, LB_GETTEXT,
1061 (WPARAM)index, (LPARAM)pText );
1063 lphc->wState |= CBF_NOEDITNOTIFY;
1065 SendMessageA( lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)pText );
1066 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
1067 HeapFree( GetProcessHeap(), 0, pText );
1073 /***********************************************************************
1076 * Show listbox popup.
1078 static void CBDropDown( LPHEADCOMBO lphc )
1086 TRACE("[%04x]: drop down\n", CB_HWND(lphc));
1088 CB_NOTIFY( lphc, CBN_DROPDOWN );
1092 lphc->wState |= CBF_DROPPED;
1093 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1095 lphc->droppedIndex = CBUpdateLBox( lphc );
1097 if( !(lphc->wState & CBF_CAPTURE) )
1098 CBUpdateEdit( lphc, lphc->droppedIndex );
1102 lphc->droppedIndex = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1104 if( lphc->droppedIndex == LB_ERR )
1105 lphc->droppedIndex = 0;
1107 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)lphc->droppedIndex, 0 );
1108 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1111 /* now set popup position */
1112 GetWindowRect( lphc->self->hwndSelf, &rect );
1115 * If it's a dropdown, the listbox is offset
1117 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1118 rect.left += COMBO_EDITBUTTONSPACE();
1120 /* if the dropped height is greater than the total height of the dropped
1121 items list, then force the drop down list height to be the total height
1122 of the items in the dropped list */
1124 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1125 nItems = (int)SendMessageA (lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1126 nHeight = COMBO_YBORDERGAP;
1127 for (i = 0; i < nItems; i++)
1129 nHeight += (int)SendMessageA (lphc->hWndLBox, LB_GETITEMHEIGHT, i, 0);
1131 if (nHeight >= nDroppedHeight)
1135 if (nHeight < nDroppedHeight)
1136 nDroppedHeight = nHeight;
1138 SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1139 lphc->droppedRect.right - lphc->droppedRect.left,
1141 SWP_NOACTIVATE | SWP_NOREDRAW);
1143 if( !(lphc->wState & CBF_NOREDRAW) )
1144 RedrawWindow( lphc->self->hwndSelf, NULL, 0, RDW_INVALIDATE |
1145 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1147 ShowWindow( lphc->hWndLBox, SW_SHOWNA );
1150 /***********************************************************************
1153 * Hide listbox popup.
1155 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1157 HWND hWnd = lphc->self->hwndSelf;
1159 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1161 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1164 TRACE("[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT)ok );
1166 if( lphc->wState & CBF_DROPPED )
1171 * It seems useful to send the WM_LBUTTONUP with (-1,-1) when cancelling
1172 * and with (0,0) (anywhere in the listbox) when Oking.
1174 SendMessageA( lphc->hWndLBox, WM_LBUTTONUP, 0, ok ? (LPARAM)0 : (LPARAM)(-1) );
1176 lphc->wState &= ~CBF_DROPPED;
1177 ShowWindow( lphc->hWndLBox, SW_HIDE );
1179 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1181 INT index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1182 CBUpdateEdit( lphc, index );
1183 rect = lphc->buttonRect;
1194 rect = lphc->textRect;
1199 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1200 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1201 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1202 CB_NOTIFY( lphc, CBN_CLOSEUP );
1207 /***********************************************************************
1210 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1212 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL bRedrawButton )
1214 if( lphc->wState & CBF_DROPPED )
1216 CBRollUp( lphc, TRUE, bRedrawButton );
1224 /***********************************************************************
1227 * Edit control helper.
1229 HWND COMBO_GetLBWindow( WND* pWnd )
1231 LPHEADCOMBO lphc = CB_GETPTR(pWnd);
1232 if( lphc ) return lphc->hWndLBox;
1237 /***********************************************************************
1240 static void CBRepaintButton( LPHEADCOMBO lphc )
1242 InvalidateRect(CB_HWND(lphc), &lphc->buttonRect, TRUE);
1243 UpdateWindow(CB_HWND(lphc));
1246 /***********************************************************************
1249 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1251 if( !(lphc->wState & CBF_FOCUSED) )
1253 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1254 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1256 if( lphc->wState & CBF_EDIT )
1257 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
1258 lphc->wState |= CBF_FOCUSED;
1259 if( !(lphc->wState & CBF_EDIT) )
1261 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1264 CB_NOTIFY( lphc, CBN_SETFOCUS );
1268 /***********************************************************************
1271 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1273 HWND hWnd = lphc->self->hwndSelf;
1275 if( lphc->wState & CBF_FOCUSED )
1277 SendMessageA( hWnd, WM_LBUTTONUP, 0, (LPARAM)(-1) );
1279 CBRollUp( lphc, FALSE, TRUE );
1280 if( IsWindow( hWnd ) )
1282 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1283 SendMessageA( lphc->hWndLBox, LB_CARETOFF, 0, 0 );
1285 lphc->wState &= ~CBF_FOCUSED;
1288 if( lphc->wState & CBF_EDIT )
1289 SendMessageA( lphc->hWndEdit, EM_SETSEL, (WPARAM)(-1), 0 );
1292 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1295 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1300 /***********************************************************************
1303 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1305 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1307 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1309 switch( HIWORD(wParam) >> 8 )
1311 case (EN_SETFOCUS >> 8):
1313 TRACE("[%04x]: edit [%04x] got focus\n",
1314 CB_HWND(lphc), lphc->hWndEdit );
1316 if( !(lphc->wState & CBF_FOCUSED) ) COMBO_SetFocus( lphc );
1319 case (EN_KILLFOCUS >> 8):
1321 TRACE("[%04x]: edit [%04x] lost focus\n",
1322 CB_HWND(lphc), lphc->hWndEdit );
1324 /* NOTE: it seems that Windows' edit control sends an
1325 * undocumented message WM_USER + 0x1B instead of this
1326 * notification (only when it happens to be a part of
1327 * the combo). ?? - AK.
1330 COMBO_KillFocus( lphc );
1334 case (EN_CHANGE >> 8):
1336 * In some circumstances (when the selection of the combobox
1337 * is changed for example) we don't wans the EN_CHANGE notification
1338 * to be forwarded to the parent of the combobox. This code
1339 * checks a flag that is set in these occasions and ignores the
1342 if (lphc->wState & CBF_NOEDITNOTIFY)
1344 lphc->wState &= ~CBF_NOEDITNOTIFY;
1348 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1351 CBUpdateLBox( lphc );
1354 case (EN_UPDATE >> 8):
1355 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1358 case (EN_ERRSPACE >> 8):
1359 CB_NOTIFY( lphc, CBN_ERRSPACE );
1362 else if( lphc->hWndLBox == hWnd )
1364 switch( HIWORD(wParam) )
1367 CB_NOTIFY( lphc, CBN_ERRSPACE );
1371 CB_NOTIFY( lphc, CBN_DBLCLK );
1377 TRACE("[%04x]: lbox selection change [%04x]\n",
1378 CB_HWND(lphc), lphc->wState );
1380 /* do not roll up if selection is being tracked
1381 * by arrowkeys in the dropdown listbox */
1383 if( (lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP) )
1384 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1385 else lphc->wState &= ~CBF_NOROLLUP;
1387 CB_NOTIFY( lphc, CBN_SELCHANGE );
1388 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1393 /* nothing to do here since ComboLBox always resets the focus to its
1394 * combo/edit counterpart */
1401 /***********************************************************************
1404 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1406 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg,
1407 WPARAM wParam, LPARAM lParam )
1409 HWND hWnd = lphc->self->hwndSelf;
1411 TRACE("[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg );
1413 #define lpIS ((LPDELETEITEMSTRUCT)lParam)
1415 /* two first items are the same in all 4 structs */
1416 lpIS->CtlType = ODT_COMBOBOX;
1417 lpIS->CtlID = lphc->self->wIDmenu;
1419 switch( msg ) /* patch window handle */
1422 lpIS->hwndItem = hWnd;
1426 #define lpIS ((LPDRAWITEMSTRUCT)lParam)
1427 lpIS->hwndItem = hWnd;
1430 case WM_COMPAREITEM:
1431 #define lpIS ((LPCOMPAREITEMSTRUCT)lParam)
1432 lpIS->hwndItem = hWnd;
1437 return SendMessageA( lphc->owner, msg, lphc->self->wIDmenu, lParam );
1440 /***********************************************************************
1443 static LRESULT COMBO_GetText( LPHEADCOMBO lphc, UINT N, LPSTR lpText)
1445 if( lphc->wState & CBF_EDIT )
1446 return SendMessageA( lphc->hWndEdit, WM_GETTEXT,
1447 (WPARAM)N, (LPARAM)lpText );
1449 /* get it from the listbox */
1451 if( lphc->hWndLBox )
1453 INT idx = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1457 INT length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN,
1460 /* 'length' is without the terminating character */
1462 lpBuffer = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1 );
1468 INT n = SendMessageA( lphc->hWndLBox, LB_GETTEXT,
1469 (WPARAM)idx, (LPARAM)lpBuffer );
1471 /* truncate if buffer is too short */
1476 if( n != LB_ERR ) memcpy( lpText, lpBuffer, (N>n) ? n+1 : N-1 );
1477 lpText[N - 1] = '\0';
1479 HeapFree( GetProcessHeap(), 0, lpBuffer );
1489 /***********************************************************************
1492 * This function sets window positions according to the updated
1493 * component placement struct.
1495 static void CBResetPos(
1501 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1503 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1504 * sizing messages */
1506 if( lphc->wState & CBF_EDIT )
1507 SetWindowPos( lphc->hWndEdit, 0,
1508 rectEdit->left, rectEdit->top,
1509 rectEdit->right - rectEdit->left,
1510 rectEdit->bottom - rectEdit->top,
1511 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1513 SetWindowPos( lphc->hWndLBox, 0,
1514 rectLB->left, rectLB->top,
1515 rectLB->right - rectLB->left,
1516 rectLB->bottom - rectLB->top,
1517 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1521 if( lphc->wState & CBF_DROPPED )
1523 lphc->wState &= ~CBF_DROPPED;
1524 ShowWindow( lphc->hWndLBox, SW_HIDE );
1527 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1528 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
1529 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1534 /***********************************************************************
1537 static void COMBO_Size( LPHEADCOMBO lphc )
1539 CBCalcPlacement(lphc->self->hwndSelf,
1543 &lphc->droppedRect);
1545 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1549 /***********************************************************************
1552 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1557 lphc->hFont = hFont;
1560 * Propagate to owned windows.
1562 if( lphc->wState & CBF_EDIT )
1563 SendMessageA( lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw );
1564 SendMessageA( lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw );
1567 * Redo the layout of the control.
1569 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1571 CBCalcPlacement(lphc->self->hwndSelf,
1575 &lphc->droppedRect);
1577 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1581 CBForceDummyResize(lphc);
1586 /***********************************************************************
1587 * COMBO_SetItemHeight
1589 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1591 LRESULT lRet = CB_ERR;
1593 if( index == -1 ) /* set text field height */
1595 if( height < 32768 )
1597 lphc->editHeight = height;
1600 * Redo the layout of the control.
1602 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1604 CBCalcPlacement(lphc->self->hwndSelf,
1608 &lphc->droppedRect);
1610 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1614 CBForceDummyResize(lphc);
1620 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1621 lRet = SendMessageA( lphc->hWndLBox, LB_SETITEMHEIGHT,
1622 (WPARAM)index, (LPARAM)height );
1626 /***********************************************************************
1627 * COMBO_SelectString
1629 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPCSTR pText )
1631 INT index = SendMessageA( lphc->hWndLBox, LB_SELECTSTRING,
1632 (WPARAM)start, (LPARAM)pText );
1635 if( lphc->wState & CBF_EDIT )
1636 CBUpdateEdit( lphc, index );
1639 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1642 return (LRESULT)index;
1645 /***********************************************************************
1648 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1652 HWND hWnd = lphc->self->hwndSelf;
1654 pt.x = LOWORD(lParam);
1655 pt.y = HIWORD(lParam);
1656 bButton = PtInRect(&lphc->buttonRect, pt);
1658 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1659 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1661 lphc->wState |= CBF_BUTTONDOWN;
1662 if( lphc->wState & CBF_DROPPED )
1664 /* got a click to cancel selection */
1666 lphc->wState &= ~CBF_BUTTONDOWN;
1667 CBRollUp( lphc, TRUE, FALSE );
1668 if( !IsWindow( hWnd ) ) return;
1670 if( lphc->wState & CBF_CAPTURE )
1672 lphc->wState &= ~CBF_CAPTURE;
1678 /* drop down the listbox and start tracking */
1680 lphc->wState |= CBF_CAPTURE;
1684 if( bButton ) CBRepaintButton( lphc );
1688 /***********************************************************************
1691 * Release capture and stop tracking if needed.
1693 static void COMBO_LButtonUp( LPHEADCOMBO lphc, LPARAM lParam )
1695 if( lphc->wState & CBF_CAPTURE )
1697 lphc->wState &= ~CBF_CAPTURE;
1698 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1700 INT index = CBUpdateLBox( lphc );
1701 CBUpdateEdit( lphc, index );
1706 if( lphc->wState & CBF_BUTTONDOWN )
1708 lphc->wState &= ~CBF_BUTTONDOWN;
1709 CBRepaintButton( lphc );
1713 /***********************************************************************
1716 * Two things to do - track combo button and release capture when
1717 * pointer goes into the listbox.
1719 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1724 pt.x = LOWORD(lParam);
1725 pt.y = HIWORD(lParam);
1727 if( lphc->wState & CBF_BUTTONDOWN )
1731 bButton = PtInRect(&lphc->buttonRect, pt);
1735 lphc->wState &= ~CBF_BUTTONDOWN;
1736 CBRepaintButton( lphc );
1740 GetClientRect( lphc->hWndLBox, &lbRect );
1741 MapWindowPoints( lphc->self->hwndSelf, lphc->hWndLBox, &pt, 1 );
1742 if( PtInRect(&lbRect, pt) )
1744 lphc->wState &= ~CBF_CAPTURE;
1746 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc );
1748 /* hand over pointer tracking */
1749 SendMessageA( lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam );
1754 /***********************************************************************
1755 * ComboWndProc_locked
1757 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1759 static inline LRESULT WINAPI ComboWndProc_locked( WND* pWnd, UINT message,
1760 WPARAM wParam, LPARAM lParam )
1763 LPHEADCOMBO lphc = CB_GETPTR(pWnd);
1764 HWND hwnd = pWnd->hwndSelf;
1766 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
1767 pWnd->hwndSelf, SPY_GetMsgName(message), wParam, lParam );
1769 if( lphc || message == WM_NCCREATE )
1773 /* System messages */
1776 return COMBO_NCCreate(pWnd, lParam);
1778 COMBO_NCDestroy(lphc);
1779 break;/* -> DefWindowProc */
1782 return COMBO_Create(lphc, pWnd, lParam);
1784 case WM_PRINTCLIENT:
1785 if (lParam & PRF_ERASEBKGND)
1786 COMBO_EraseBackground(hwnd, lphc, wParam);
1790 /* wParam may contain a valid HDC! */
1791 return COMBO_Paint(lphc, wParam);
1793 return COMBO_EraseBackground(hwnd, lphc, wParam);
1795 return (LRESULT)(DLGC_WANTARROWS | DLGC_WANTCHARS);
1796 case WM_WINDOWPOSCHANGING:
1797 return COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1799 if( lphc->hWndLBox &&
1800 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1803 COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
1806 return (LRESULT)lphc->hFont;
1808 if( lphc->wState & CBF_EDIT )
1809 SetFocus( lphc->hWndEdit );
1811 COMBO_SetFocus( lphc );
1814 #define hwndFocus ((HWND16)wParam)
1816 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1817 COMBO_KillFocus( lphc );
1821 return COMBO_Command( lphc, wParam, (HWND)lParam );
1823 return COMBO_GetText( lphc, (UINT)wParam, (LPSTR)lParam );
1825 case WM_GETTEXTLENGTH:
1830 if( lphc->wState & CBF_EDIT )
1832 lphc->wState |= CBF_NOEDITNOTIFY;
1834 return SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1839 case WM_COMPAREITEM:
1840 case WM_MEASUREITEM:
1841 return COMBO_ItemOp( lphc, message, wParam, lParam );
1843 if( lphc->wState & CBF_EDIT )
1844 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1845 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1849 lphc->wState &= ~CBF_NOREDRAW;
1851 lphc->wState |= CBF_NOREDRAW;
1853 if( lphc->wState & CBF_EDIT )
1854 SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1855 SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1858 if( KEYDATA_ALT & HIWORD(lParam) )
1859 if( wParam == VK_UP || wParam == VK_DOWN )
1860 COMBO_FlipListbox( lphc, TRUE );
1861 break;/* -> DefWindowProc */
1865 if( lphc->wState & CBF_EDIT )
1866 return SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1868 return SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1869 case WM_LBUTTONDOWN:
1870 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self->hwndSelf );
1871 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1874 COMBO_LButtonUp( lphc, lParam );
1877 if( lphc->wState & CBF_CAPTURE )
1878 COMBO_MouseMove( lphc, wParam, lParam );
1880 /* Combo messages */
1882 case CB_ADDSTRING16:
1883 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1885 return SendMessageA( lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
1886 case CB_INSERTSTRING16:
1887 wParam = (INT)(INT16)wParam;
1888 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1889 case CB_INSERTSTRING:
1890 return SendMessageA( lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1891 case CB_DELETESTRING16:
1892 case CB_DELETESTRING:
1893 return SendMessageA( lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1894 case CB_SELECTSTRING16:
1895 wParam = (INT)(INT16)wParam;
1896 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1897 case CB_SELECTSTRING:
1898 return COMBO_SelectString( lphc, (INT)wParam, (LPSTR)lParam );
1899 case CB_FINDSTRING16:
1900 wParam = (INT)(INT16)wParam;
1901 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1903 return SendMessageA( lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1904 case CB_FINDSTRINGEXACT16:
1905 wParam = (INT)(INT16)wParam;
1906 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1907 case CB_FINDSTRINGEXACT:
1908 return SendMessageA( lphc->hWndLBox, LB_FINDSTRINGEXACT,
1910 case CB_SETITEMHEIGHT16:
1911 wParam = (INT)(INT16)wParam; /* signed integer */
1912 case CB_SETITEMHEIGHT:
1913 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
1914 case CB_GETITEMHEIGHT16:
1915 wParam = (INT)(INT16)wParam;
1916 case CB_GETITEMHEIGHT:
1917 if( (INT)wParam >= 0 ) /* listbox item */
1918 return SendMessageA( lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
1919 return CBGetTextAreaHeight(hwnd, lphc);
1920 case CB_RESETCONTENT16:
1921 case CB_RESETCONTENT:
1922 SendMessageA( lphc->hWndLBox, LB_RESETCONTENT, 0, 0 );
1923 InvalidateRect(CB_HWND(lphc), NULL, TRUE);
1925 case CB_INITSTORAGE:
1926 return SendMessageA( lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
1927 case CB_GETHORIZONTALEXTENT:
1928 return SendMessageA( lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
1929 case CB_SETHORIZONTALEXTENT:
1930 return SendMessageA( lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
1931 case CB_GETTOPINDEX:
1932 return SendMessageA( lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
1934 return SendMessageA( lphc->hWndLBox, LB_GETLOCALE, 0, 0);
1936 return SendMessageA( lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
1937 case CB_GETDROPPEDWIDTH:
1938 if( lphc->droppedWidth )
1939 return lphc->droppedWidth;
1940 return lphc->droppedRect.right - lphc->droppedRect.left;
1941 case CB_SETDROPPEDWIDTH:
1942 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
1943 (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
1945 case CB_GETDROPPEDCONTROLRECT16:
1946 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1950 CBGetDroppedControlRect( lphc, &r );
1951 CONV_RECT32TO16( &r, (LPRECT16)lParam );
1954 case CB_GETDROPPEDCONTROLRECT:
1955 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
1957 case CB_GETDROPPEDSTATE16:
1958 case CB_GETDROPPEDSTATE:
1959 return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
1961 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1964 return COMBO_Directory( lphc, (UINT)wParam,
1965 (LPSTR)lParam, (message == CB_DIR));
1966 case CB_SHOWDROPDOWN16:
1967 case CB_SHOWDROPDOWN:
1968 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1972 if( !(lphc->wState & CBF_DROPPED) )
1976 if( lphc->wState & CBF_DROPPED )
1977 CBRollUp( lphc, FALSE, TRUE );
1982 return SendMessageA( lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1983 case CB_GETCURSEL16:
1985 return SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1986 case CB_SETCURSEL16:
1987 wParam = (INT)(INT16)wParam;
1989 lParam = SendMessageA( lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
1990 if( lphc->wState & CBF_SELCHANGE )
1992 /* no LBN_SELCHANGE in this case, update manually */
1993 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1994 lphc->wState &= ~CBF_SELCHANGE;
1997 case CB_GETLBTEXT16:
1998 wParam = (INT)(INT16)wParam;
1999 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2001 return SendMessageA( lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2002 case CB_GETLBTEXTLEN16:
2003 wParam = (INT)(INT16)wParam;
2004 case CB_GETLBTEXTLEN:
2005 return SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2006 case CB_GETITEMDATA16:
2007 wParam = (INT)(INT16)wParam;
2008 case CB_GETITEMDATA:
2009 return SendMessageA( lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2010 case CB_SETITEMDATA16:
2011 wParam = (INT)(INT16)wParam;
2012 case CB_SETITEMDATA:
2013 return SendMessageA( lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2014 case CB_GETEDITSEL16:
2015 wParam = lParam = 0; /* just in case */
2017 if( lphc->wState & CBF_EDIT )
2021 return SendMessageA( lphc->hWndEdit, EM_GETSEL,
2022 (wParam) ? wParam : (WPARAM)&a,
2023 (lParam) ? lParam : (LPARAM)&b );
2026 case CB_SETEDITSEL16:
2028 if( lphc->wState & CBF_EDIT )
2029 return SendMessageA( lphc->hWndEdit, EM_SETSEL,
2030 (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2032 case CB_SETEXTENDEDUI16:
2033 case CB_SETEXTENDEDUI:
2034 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2037 lphc->wState |= CBF_EUI;
2038 else lphc->wState &= ~CBF_EUI;
2040 case CB_GETEXTENDEDUI16:
2041 case CB_GETEXTENDEDUI:
2042 return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2043 case (WM_USER + 0x1B):
2044 WARN("[%04x]: undocumented msg!\n", hwnd );
2046 return DefWindowProcA(hwnd, message, wParam, lParam);
2051 /***********************************************************************
2054 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2057 LRESULT WINAPI ComboWndProc( HWND hwnd, UINT message,
2058 WPARAM wParam, LPARAM lParam )
2060 WND* pWnd = WIN_FindWndPtr(hwnd);
2061 LRESULT retvalue = ComboWndProc_locked(pWnd,message,wParam,lParam);
2064 WIN_ReleaseWndPtr(pWnd);