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(
666 COLORREF oldTextColor
, oldBkColor
;
668 if( lphc
->wState
& CBF_NOREDRAW
)
671 hPrevBrush
= SelectObject(hdc
, GetSysColorBrush(COLOR_BTNFACE
));
674 * Draw the button background
679 rectButton
.right
-rectButton
.left
,
680 rectButton
.bottom
-rectButton
.top
,
683 if( (bBool
= lphc
->wState
& CBF_BUTTONDOWN
) )
685 DrawEdge( hdc
, &rectButton
, EDGE_SUNKEN
, BF_RECT
);
689 DrawEdge( hdc
, &rectButton
, EDGE_RAISED
, BF_RECT
);
693 * Remove the edge of the button from the rectangle
694 * and calculate the position of the bitmap.
696 InflateRect( &rectButton
, -2, -2);
698 x
= (rectButton
.left
+ rectButton
.right
- CBitWidth
) >> 1;
699 y
= (rectButton
.top
+ rectButton
.bottom
- CBitHeight
) >> 1;
702 hMemDC
= CreateCompatibleDC( hdc
);
703 SelectObject( hMemDC
, hComboBmp
);
704 oldTextColor
= SetTextColor( hdc
, GetSysColor(COLOR_BTNFACE
) );
705 oldBkColor
= SetBkColor( hdc
, CB_DISABLED(lphc
) ? RGB(128,128,128) :
707 BitBlt( hdc
, x
, y
, CBitWidth
, CBitHeight
, hMemDC
, 0, 0, SRCCOPY
);
708 SetBkColor( hdc
, oldBkColor
);
709 SetTextColor( hdc
, oldTextColor
);
711 SelectObject( hdc
, hPrevBrush
);
714 /***********************************************************************
717 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
719 static void CBPaintText(
727 if( lphc
->wState
& CBF_NOREDRAW
) return;
729 /* follow Windows combobox that sends a bunch of text
730 * inquiries to its listbox while processing WM_PAINT. */
732 if( (id
= SendMessageA(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0) ) != LB_ERR
)
734 size
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
, id
, 0);
735 if( (pText
= HeapAlloc( GetProcessHeap(), 0, size
+ 1)) )
737 SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
, (WPARAM
)id
, (LPARAM
)pText
);
738 pText
[size
] = '\0'; /* just in case */
742 if( lphc
->wState
& CBF_EDIT
)
744 if( CB_HASSTRINGS(lphc
) ) SetWindowTextA( lphc
->hWndEdit
, pText
? pText
: "" );
745 if( lphc
->wState
& CBF_FOCUSED
)
746 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
748 else /* paint text field ourselves */
750 HBRUSH hPrevBrush
= 0;
755 if ((hDC
= GetDC(lphc
->self
->hwndSelf
)))
757 HBRUSH hBrush
= SendMessageA( lphc
->owner
,
759 hDC
, lphc
->self
->hwndSelf
);
760 hPrevBrush
= SelectObject( hDC
,
761 (hBrush
) ? hBrush
: GetStockObject(WHITE_BRUSH
) );
767 HFONT hPrevFont
= (lphc
->hFont
) ? SelectObject(hDC
, lphc
->hFont
) : 0;
770 * Give ourselves some space.
772 InflateRect( &rectEdit
, -1, -1 );
774 if ( (lphc
->wState
& CBF_FOCUSED
) &&
775 !(lphc
->wState
& CBF_DROPPED
) )
779 FillRect( hDC
, &rectEdit
, GetSysColorBrush(COLOR_HIGHLIGHT
) );
780 SetBkColor( hDC
, GetSysColor( COLOR_HIGHLIGHT
) );
781 SetTextColor( hDC
, GetSysColor( COLOR_HIGHLIGHTTEXT
) );
782 itemState
= ODS_SELECTED
| ODS_FOCUS
;
787 if( CB_OWNERDRAWN(lphc
) )
793 * Save the current clip region.
794 * To retrieve the clip region, we need to create one "dummy"
797 clipRegion
= CreateRectRgnIndirect(&rectEdit
);
799 if (GetClipRgn(hDC
, clipRegion
)!=1)
801 DeleteObject(clipRegion
);
802 clipRegion
=(HRGN
)NULL
;
805 if ( lphc
->self
->dwStyle
& WS_DISABLED
)
806 itemState
|= ODS_DISABLED
;
808 dis
.CtlType
= ODT_COMBOBOX
;
809 dis
.CtlID
= lphc
->self
->wIDmenu
;
810 dis
.hwndItem
= lphc
->self
->hwndSelf
;
811 dis
.itemAction
= ODA_DRAWENTIRE
;
813 dis
.itemState
= itemState
;
815 dis
.rcItem
= rectEdit
;
816 dis
.itemData
= SendMessageA( lphc
->hWndLBox
, LB_GETITEMDATA
,
820 * Clip the DC and have the parent draw the item.
822 IntersectClipRect(hDC
,
823 rectEdit
.left
, rectEdit
.top
,
824 rectEdit
.right
, rectEdit
.bottom
);
826 SendMessageA(lphc
->owner
, WM_DRAWITEM
,
827 lphc
->self
->wIDmenu
, (LPARAM
)&dis
);
830 * Reset the clipping region.
832 SelectClipRgn(hDC
, clipRegion
);
839 ETO_OPAQUE
| ETO_CLIPPED
,
841 pText
? pText
: "" , size
, NULL
);
843 if(lphc
->wState
& CBF_FOCUSED
&& !(lphc
->wState
& CBF_DROPPED
))
844 DrawFocusRect( hDC
, &rectEdit
);
848 SelectObject(hDC
, hPrevFont
);
853 SelectObject( hDC
, hPrevBrush
);
855 ReleaseDC( lphc
->self
->hwndSelf
, hDC
);
860 HeapFree( GetProcessHeap(), 0, pText
);
863 /***********************************************************************
866 static void CBPaintBorder(
873 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
875 GetClientRect(hwnd
, &clientRect
);
879 CopyRect(&clientRect
, &lphc
->textRect
);
881 InflateRect(&clientRect
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
882 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
885 DrawEdge(hdc
, &clientRect
, EDGE_SUNKEN
, BF_RECT
);
888 /***********************************************************************
889 * COMBO_EraseBackground
891 static LRESULT
COMBO_EraseBackground(
900 hDC
= (hParamDC
) ? hParamDC
904 * Calculate the area that we want to erase.
906 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
908 GetClientRect(hwnd
, &clientRect
);
912 CopyRect(&clientRect
, &lphc
->textRect
);
914 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
917 hBkgBrush
= SendMessageA( lphc
->owner
, WM_CTLCOLORLISTBOX
,
921 hBkgBrush
= GetStockObject(WHITE_BRUSH
);
923 FillRect(hDC
, &clientRect
, hBkgBrush
);
926 ReleaseDC(hwnd
, hDC
);
931 /***********************************************************************
934 static LRESULT
COMBO_Paint(LPHEADCOMBO lphc
, HDC hParamDC
)
939 hDC
= (hParamDC
) ? hParamDC
940 : BeginPaint( lphc
->self
->hwndSelf
, &ps
);
943 if( hDC
&& !(lphc
->wState
& CBF_NOREDRAW
) )
945 HBRUSH hPrevBrush
, hBkgBrush
;
947 hBkgBrush
= SendMessageA( lphc
->owner
, WM_CTLCOLORLISTBOX
,
948 hDC
, lphc
->self
->hwndSelf
);
951 hBkgBrush
= GetStockObject(WHITE_BRUSH
);
953 hPrevBrush
= SelectObject( hDC
, hBkgBrush
);
956 * In non 3.1 look, there is a sunken border on the combobox
958 if (TWEAK_WineLook
!= WIN31_LOOK
)
960 CBPaintBorder(CB_HWND(lphc
), lphc
, hDC
);
963 if( !IsRectEmpty(&lphc
->buttonRect
) )
965 CBPaintButton(lphc
, hDC
, lphc
->buttonRect
);
968 if( !(lphc
->wState
& CBF_EDIT
) )
971 * The text area has a border only in Win 3.1 look.
973 if (TWEAK_WineLook
== WIN31_LOOK
)
975 HPEN hPrevPen
= SelectObject( hDC
, GetSysColorPen(COLOR_WINDOWFRAME
) );
978 lphc
->textRect
.left
, lphc
->textRect
.top
,
979 lphc
->textRect
.right
- 1, lphc
->textRect
.bottom
- 1);
981 SelectObject( hDC
, hPrevPen
);
984 CBPaintText( lphc
, hDC
, lphc
->textRect
);
988 SelectObject( hDC
, hPrevBrush
);
992 EndPaint(lphc
->self
->hwndSelf
, &ps
);
997 /***********************************************************************
1000 * Select listbox entry according to the contents of the edit control.
1002 static INT
CBUpdateLBox( LPHEADCOMBO lphc
)
1004 INT length
, idx
, ret
;
1008 length
= CB_GETEDITTEXTLENGTH( lphc
);
1011 pText
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1);
1013 TRACE("\t edit text length %i\n", length
);
1017 if( length
) GetWindowTextA( lphc
->hWndEdit
, pText
, length
+ 1);
1018 else pText
[0] = '\0';
1019 idx
= SendMessageA( lphc
->hWndLBox
, LB_FINDSTRING
,
1020 (WPARAM
)(-1), (LPARAM
)pText
);
1021 if( idx
== LB_ERR
) idx
= 0; /* select first item */
1023 HeapFree( GetProcessHeap(), 0, pText
);
1028 SendMessageA( lphc
->hWndLBox
, LB_SETCURSEL
, (WPARAM
)idx
, 0 );
1032 SendMessageA( lphc
->hWndLBox
, LB_SETTOPINDEX
, (WPARAM
)idx
, 0 );
1033 /* probably superfluous but Windows sends this too */
1034 SendMessageA( lphc
->hWndLBox
, LB_SETCARETINDEX
, (WPARAM
)idx
, 0 );
1039 /***********************************************************************
1042 * Copy a listbox entry to the edit control.
1044 static void CBUpdateEdit( LPHEADCOMBO lphc
, INT index
)
1049 TRACE("\t %i\n", index
);
1053 length
= CB_GETEDITTEXTLENGTH( lphc
);
1056 if( (pText
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1)) )
1058 GetWindowTextA( lphc
->hWndEdit
, pText
, length
+ 1 );
1059 index
= SendMessageA( lphc
->hWndLBox
, LB_FINDSTRING
,
1060 (WPARAM
)(-1), (LPARAM
)pText
);
1061 HeapFree( GetProcessHeap(), 0, pText
);
1066 if( index
>= 0 ) /* got an entry */
1068 length
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
, (WPARAM
)index
, 0);
1071 if( (pText
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1)) )
1073 SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
,
1074 (WPARAM
)index
, (LPARAM
)pText
);
1076 lphc
->wState
|= CBF_NOEDITNOTIFY
;
1078 SendMessageA( lphc
->hWndEdit
, WM_SETTEXT
, 0, (LPARAM
)pText
);
1079 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1) );
1080 HeapFree( GetProcessHeap(), 0, pText
);
1086 /***********************************************************************
1089 * Show listbox popup.
1091 static void CBDropDown( LPHEADCOMBO lphc
)
1099 TRACE("[%04x]: drop down\n", CB_HWND(lphc
));
1101 CB_NOTIFY( lphc
, CBN_DROPDOWN
);
1105 lphc
->wState
|= CBF_DROPPED
;
1106 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1108 lphc
->droppedIndex
= CBUpdateLBox( lphc
);
1110 if( !(lphc
->wState
& CBF_CAPTURE
) )
1111 CBUpdateEdit( lphc
, lphc
->droppedIndex
);
1115 lphc
->droppedIndex
= SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0 );
1117 if( lphc
->droppedIndex
== LB_ERR
)
1118 lphc
->droppedIndex
= 0;
1120 SendMessageA( lphc
->hWndLBox
, LB_SETTOPINDEX
, (WPARAM
)lphc
->droppedIndex
, 0 );
1121 SendMessageA( lphc
->hWndLBox
, LB_CARETON
, 0, 0 );
1124 /* now set popup position */
1125 GetWindowRect( lphc
->self
->hwndSelf
, &rect
);
1128 * If it's a dropdown, the listbox is offset
1130 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1131 rect
.left
+= COMBO_EDITBUTTONSPACE();
1133 /* if the dropped height is greater than the total height of the dropped
1134 items list, then force the drop down list height to be the total height
1135 of the items in the dropped list */
1137 nDroppedHeight
= lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
1138 nItems
= (int)SendMessageA (lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
1139 nHeight
= COMBO_YBORDERGAP
;
1140 for (i
= 0; i
< nItems
; i
++)
1142 nHeight
+= (int)SendMessageA (lphc
->hWndLBox
, LB_GETITEMHEIGHT
, i
, 0);
1144 if (nHeight
>= nDroppedHeight
)
1148 if (nHeight
< nDroppedHeight
)
1149 nDroppedHeight
= nHeight
;
1151 SetWindowPos( lphc
->hWndLBox
, HWND_TOP
, rect
.left
, rect
.bottom
,
1152 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
1154 SWP_NOACTIVATE
| SWP_NOREDRAW
);
1156 if( !(lphc
->wState
& CBF_NOREDRAW
) )
1157 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0, RDW_INVALIDATE
|
1158 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1160 ShowWindow( lphc
->hWndLBox
, SW_SHOWNA
);
1163 /***********************************************************************
1166 * Hide listbox popup.
1168 static void CBRollUp( LPHEADCOMBO lphc
, BOOL ok
, BOOL bButton
)
1170 HWND hWnd
= lphc
->self
->hwndSelf
;
1172 CB_NOTIFY( lphc
, (ok
) ? CBN_SELENDOK
: CBN_SELENDCANCEL
);
1174 if( IsWindow( hWnd
) && CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1177 TRACE("[%04x]: roll up [%i]\n", CB_HWND(lphc
), (INT
)ok
);
1179 if( lphc
->wState
& CBF_DROPPED
)
1184 * It seems useful to send the WM_LBUTTONUP with (-1,-1) when cancelling
1185 * and with (0,0) (anywhere in the listbox) when Oking.
1187 SendMessageA( lphc
->hWndLBox
, WM_LBUTTONUP
, 0, ok
? (LPARAM
)0 : (LPARAM
)(-1) );
1189 lphc
->wState
&= ~CBF_DROPPED
;
1190 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1192 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1194 INT index
= SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0 );
1195 CBUpdateEdit( lphc
, index
);
1196 rect
= lphc
->buttonRect
;
1207 rect
= lphc
->textRect
;
1212 if( bButton
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1213 RedrawWindow( hWnd
, &rect
, 0, RDW_INVALIDATE
|
1214 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1215 CB_NOTIFY( lphc
, CBN_CLOSEUP
);
1220 /***********************************************************************
1223 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1225 BOOL
COMBO_FlipListbox( LPHEADCOMBO lphc
, BOOL bRedrawButton
)
1227 if( lphc
->wState
& CBF_DROPPED
)
1229 CBRollUp( lphc
, TRUE
, bRedrawButton
);
1237 /***********************************************************************
1240 * Edit control helper.
1242 HWND
COMBO_GetLBWindow( WND
* pWnd
)
1244 LPHEADCOMBO lphc
= CB_GETPTR(pWnd
);
1245 if( lphc
) return lphc
->hWndLBox
;
1250 /***********************************************************************
1253 static void CBRepaintButton( LPHEADCOMBO lphc
)
1255 InvalidateRect(CB_HWND(lphc
), &lphc
->buttonRect
, TRUE
);
1256 UpdateWindow(CB_HWND(lphc
));
1259 /***********************************************************************
1262 static void COMBO_SetFocus( LPHEADCOMBO lphc
)
1264 if( !(lphc
->wState
& CBF_FOCUSED
) )
1266 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1267 SendMessageA( lphc
->hWndLBox
, LB_CARETON
, 0, 0 );
1269 if( lphc
->wState
& CBF_EDIT
)
1270 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1) );
1271 lphc
->wState
|= CBF_FOCUSED
;
1272 if( !(lphc
->wState
& CBF_EDIT
) )
1274 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1277 CB_NOTIFY( lphc
, CBN_SETFOCUS
);
1281 /***********************************************************************
1284 static void COMBO_KillFocus( LPHEADCOMBO lphc
)
1286 HWND hWnd
= lphc
->self
->hwndSelf
;
1288 if( lphc
->wState
& CBF_FOCUSED
)
1290 SendMessageA( hWnd
, WM_LBUTTONUP
, 0, (LPARAM
)(-1) );
1292 CBRollUp( lphc
, FALSE
, TRUE
);
1293 if( IsWindow( hWnd
) )
1295 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1296 SendMessageA( lphc
->hWndLBox
, LB_CARETOFF
, 0, 0 );
1298 lphc
->wState
&= ~CBF_FOCUSED
;
1301 if( lphc
->wState
& CBF_EDIT
)
1302 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, (WPARAM
)(-1), 0 );
1305 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1308 CB_NOTIFY( lphc
, CBN_KILLFOCUS
);
1313 /***********************************************************************
1316 static LRESULT
COMBO_Command( LPHEADCOMBO lphc
, WPARAM wParam
, HWND hWnd
)
1318 if ( lphc
->wState
& CBF_EDIT
&& lphc
->hWndEdit
== hWnd
)
1320 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1322 switch( HIWORD(wParam
) >> 8 )
1324 case (EN_SETFOCUS
>> 8):
1326 TRACE("[%04x]: edit [%04x] got focus\n",
1327 CB_HWND(lphc
), lphc
->hWndEdit
);
1329 if( !(lphc
->wState
& CBF_FOCUSED
) ) COMBO_SetFocus( lphc
);
1332 case (EN_KILLFOCUS
>> 8):
1334 TRACE("[%04x]: edit [%04x] lost focus\n",
1335 CB_HWND(lphc
), lphc
->hWndEdit
);
1337 /* NOTE: it seems that Windows' edit control sends an
1338 * undocumented message WM_USER + 0x1B instead of this
1339 * notification (only when it happens to be a part of
1340 * the combo). ?? - AK.
1343 COMBO_KillFocus( lphc
);
1347 case (EN_CHANGE
>> 8):
1349 * In some circumstances (when the selection of the combobox
1350 * is changed for example) we don't wans the EN_CHANGE notification
1351 * to be forwarded to the parent of the combobox. This code
1352 * checks a flag that is set in these occasions and ignores the
1355 if (lphc
->wState
& CBF_NOEDITNOTIFY
)
1357 lphc
->wState
&= ~CBF_NOEDITNOTIFY
;
1361 CB_NOTIFY( lphc
, CBN_EDITCHANGE
);
1364 CBUpdateLBox( lphc
);
1367 case (EN_UPDATE
>> 8):
1368 CB_NOTIFY( lphc
, CBN_EDITUPDATE
);
1371 case (EN_ERRSPACE
>> 8):
1372 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1375 else if( lphc
->hWndLBox
== hWnd
)
1377 switch( HIWORD(wParam
) )
1380 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1384 CB_NOTIFY( lphc
, CBN_DBLCLK
);
1390 TRACE("[%04x]: lbox selection change [%04x]\n",
1391 CB_HWND(lphc
), lphc
->wState
);
1393 /* do not roll up if selection is being tracked
1394 * by arrowkeys in the dropdown listbox */
1396 if( (lphc
->wState
& CBF_DROPPED
) && !(lphc
->wState
& CBF_NOROLLUP
) )
1397 CBRollUp( lphc
, (HIWORD(wParam
) == LBN_SELCHANGE
), TRUE
);
1398 else lphc
->wState
&= ~CBF_NOROLLUP
;
1400 CB_NOTIFY( lphc
, CBN_SELCHANGE
);
1401 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1406 /* nothing to do here since ComboLBox always resets the focus to its
1407 * combo/edit counterpart */
1414 /***********************************************************************
1417 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1419 static LRESULT
COMBO_ItemOp( LPHEADCOMBO lphc
, UINT msg
,
1420 WPARAM wParam
, LPARAM lParam
)
1422 HWND hWnd
= lphc
->self
->hwndSelf
;
1424 TRACE("[%04x]: ownerdraw op %04x\n", CB_HWND(lphc
), msg
);
1426 #define lpIS ((LPDELETEITEMSTRUCT)lParam)
1428 /* two first items are the same in all 4 structs */
1429 lpIS
->CtlType
= ODT_COMBOBOX
;
1430 lpIS
->CtlID
= lphc
->self
->wIDmenu
;
1432 switch( msg
) /* patch window handle */
1435 lpIS
->hwndItem
= hWnd
;
1439 #define lpIS ((LPDRAWITEMSTRUCT)lParam)
1440 lpIS
->hwndItem
= hWnd
;
1443 case WM_COMPAREITEM
:
1444 #define lpIS ((LPCOMPAREITEMSTRUCT)lParam)
1445 lpIS
->hwndItem
= hWnd
;
1450 return SendMessageA( lphc
->owner
, msg
, lphc
->self
->wIDmenu
, lParam
);
1453 /***********************************************************************
1456 static LRESULT
COMBO_GetText( LPHEADCOMBO lphc
, UINT N
, LPSTR lpText
)
1458 if( lphc
->wState
& CBF_EDIT
)
1459 return SendMessageA( lphc
->hWndEdit
, WM_GETTEXT
,
1460 (WPARAM
)N
, (LPARAM
)lpText
);
1462 /* get it from the listbox */
1464 if( lphc
->hWndLBox
)
1466 INT idx
= SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0 );
1470 INT length
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
,
1473 /* 'length' is without the terminating character */
1475 lpBuffer
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1 );
1481 INT n
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
,
1482 (WPARAM
)idx
, (LPARAM
)lpBuffer
);
1484 /* truncate if buffer is too short */
1489 if( n
!= LB_ERR
) memcpy( lpText
, lpBuffer
, (N
>n
) ? n
+1 : N
-1 );
1490 lpText
[N
- 1] = '\0';
1492 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1502 /***********************************************************************
1505 * This function sets window positions according to the updated
1506 * component placement struct.
1508 static void CBResetPos(
1514 BOOL bDrop
= (CB_GETTYPE(lphc
) != CBS_SIMPLE
);
1516 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1517 * sizing messages */
1519 if( lphc
->wState
& CBF_EDIT
)
1520 SetWindowPos( lphc
->hWndEdit
, 0,
1521 rectEdit
->left
, rectEdit
->top
,
1522 rectEdit
->right
- rectEdit
->left
,
1523 rectEdit
->bottom
- rectEdit
->top
,
1524 SWP_NOZORDER
| SWP_NOACTIVATE
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1526 SetWindowPos( lphc
->hWndLBox
, 0,
1527 rectLB
->left
, rectLB
->top
,
1528 rectLB
->right
- rectLB
->left
,
1529 rectLB
->bottom
- rectLB
->top
,
1530 SWP_NOACTIVATE
| SWP_NOZORDER
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1534 if( lphc
->wState
& CBF_DROPPED
)
1536 lphc
->wState
&= ~CBF_DROPPED
;
1537 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1540 if( bRedraw
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1541 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0,
1542 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
1547 /***********************************************************************
1550 static void COMBO_Size( LPHEADCOMBO lphc
)
1552 CBCalcPlacement(lphc
->self
->hwndSelf
,
1556 &lphc
->droppedRect
);
1558 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1562 /***********************************************************************
1565 static void COMBO_Font( LPHEADCOMBO lphc
, HFONT hFont
, BOOL bRedraw
)
1570 lphc
->hFont
= hFont
;
1573 * Propagate to owned windows.
1575 if( lphc
->wState
& CBF_EDIT
)
1576 SendMessageA( lphc
->hWndEdit
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1577 SendMessageA( lphc
->hWndLBox
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1580 * Redo the layout of the control.
1582 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1584 CBCalcPlacement(lphc
->self
->hwndSelf
,
1588 &lphc
->droppedRect
);
1590 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1594 CBForceDummyResize(lphc
);
1599 /***********************************************************************
1600 * COMBO_SetItemHeight
1602 static LRESULT
COMBO_SetItemHeight( LPHEADCOMBO lphc
, INT index
, INT height
)
1604 LRESULT lRet
= CB_ERR
;
1606 if( index
== -1 ) /* set text field height */
1608 if( height
< 32768 )
1610 lphc
->editHeight
= height
;
1613 * Redo the layout of the control.
1615 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1617 CBCalcPlacement(lphc
->self
->hwndSelf
,
1621 &lphc
->droppedRect
);
1623 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1627 CBForceDummyResize(lphc
);
1633 else if ( CB_OWNERDRAWN(lphc
) ) /* set listbox item height */
1634 lRet
= SendMessageA( lphc
->hWndLBox
, LB_SETITEMHEIGHT
,
1635 (WPARAM
)index
, (LPARAM
)height
);
1639 /***********************************************************************
1640 * COMBO_SelectString
1642 static LRESULT
COMBO_SelectString( LPHEADCOMBO lphc
, INT start
, LPCSTR pText
)
1644 INT index
= SendMessageA( lphc
->hWndLBox
, LB_SELECTSTRING
,
1645 (WPARAM
)start
, (LPARAM
)pText
);
1648 if( lphc
->wState
& CBF_EDIT
)
1649 CBUpdateEdit( lphc
, index
);
1652 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1655 return (LRESULT
)index
;
1658 /***********************************************************************
1661 static void COMBO_LButtonDown( LPHEADCOMBO lphc
, LPARAM lParam
)
1665 HWND hWnd
= lphc
->self
->hwndSelf
;
1667 pt
.x
= LOWORD(lParam
);
1668 pt
.y
= HIWORD(lParam
);
1669 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1671 if( (CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
) ||
1672 (bButton
&& (CB_GETTYPE(lphc
) == CBS_DROPDOWN
)) )
1674 lphc
->wState
|= CBF_BUTTONDOWN
;
1675 if( lphc
->wState
& CBF_DROPPED
)
1677 /* got a click to cancel selection */
1679 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1680 CBRollUp( lphc
, TRUE
, FALSE
);
1681 if( !IsWindow( hWnd
) ) return;
1683 if( lphc
->wState
& CBF_CAPTURE
)
1685 lphc
->wState
&= ~CBF_CAPTURE
;
1691 /* drop down the listbox and start tracking */
1693 lphc
->wState
|= CBF_CAPTURE
;
1697 if( bButton
) CBRepaintButton( lphc
);
1701 /***********************************************************************
1704 * Release capture and stop tracking if needed.
1706 static void COMBO_LButtonUp( LPHEADCOMBO lphc
, LPARAM lParam
)
1708 if( lphc
->wState
& CBF_CAPTURE
)
1710 lphc
->wState
&= ~CBF_CAPTURE
;
1711 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1713 INT index
= CBUpdateLBox( lphc
);
1714 CBUpdateEdit( lphc
, index
);
1719 if( lphc
->wState
& CBF_BUTTONDOWN
)
1721 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1722 CBRepaintButton( lphc
);
1726 /***********************************************************************
1729 * Two things to do - track combo button and release capture when
1730 * pointer goes into the listbox.
1732 static void COMBO_MouseMove( LPHEADCOMBO lphc
, WPARAM wParam
, LPARAM lParam
)
1737 pt
.x
= LOWORD(lParam
);
1738 pt
.y
= HIWORD(lParam
);
1740 if( lphc
->wState
& CBF_BUTTONDOWN
)
1744 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1748 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1749 CBRepaintButton( lphc
);
1753 GetClientRect( lphc
->hWndLBox
, &lbRect
);
1754 MapWindowPoints( lphc
->self
->hwndSelf
, lphc
->hWndLBox
, &pt
, 1 );
1755 if( PtInRect(&lbRect
, pt
) )
1757 lphc
->wState
&= ~CBF_CAPTURE
;
1759 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
) CBUpdateLBox( lphc
);
1761 /* hand over pointer tracking */
1762 SendMessageA( lphc
->hWndLBox
, WM_LBUTTONDOWN
, wParam
, lParam
);
1767 /***********************************************************************
1768 * ComboWndProc_locked
1770 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1772 static inline LRESULT WINAPI
ComboWndProc_locked( WND
* pWnd
, UINT message
,
1773 WPARAM wParam
, LPARAM lParam
)
1776 LPHEADCOMBO lphc
= CB_GETPTR(pWnd
);
1777 HWND hwnd
= pWnd
->hwndSelf
;
1779 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
1780 pWnd
->hwndSelf
, SPY_GetMsgName(message
), wParam
, lParam
);
1782 if( lphc
|| message
== WM_NCCREATE
)
1786 /* System messages */
1789 return COMBO_NCCreate(pWnd
, lParam
);
1791 COMBO_NCDestroy(lphc
);
1792 break;/* -> DefWindowProc */
1795 return COMBO_Create(lphc
, pWnd
, lParam
);
1797 case WM_PRINTCLIENT
:
1798 if (lParam
& PRF_ERASEBKGND
)
1799 COMBO_EraseBackground(hwnd
, lphc
, wParam
);
1803 /* wParam may contain a valid HDC! */
1804 return COMBO_Paint(lphc
, wParam
);
1806 return COMBO_EraseBackground(hwnd
, lphc
, wParam
);
1808 return (LRESULT
)(DLGC_WANTARROWS
| DLGC_WANTCHARS
);
1809 case WM_WINDOWPOSCHANGING
:
1810 return COMBO_WindowPosChanging(hwnd
, lphc
, (LPWINDOWPOS
)lParam
);
1812 if( lphc
->hWndLBox
&&
1813 !(lphc
->wState
& CBF_NORESIZE
) ) COMBO_Size( lphc
);
1816 COMBO_Font( lphc
, (HFONT16
)wParam
, (BOOL
)lParam
);
1819 return (LRESULT
)lphc
->hFont
;
1821 if( lphc
->wState
& CBF_EDIT
)
1822 SetFocus( lphc
->hWndEdit
);
1824 COMBO_SetFocus( lphc
);
1827 #define hwndFocus ((HWND16)wParam)
1829 (hwndFocus
!= lphc
->hWndEdit
&& hwndFocus
!= lphc
->hWndLBox
))
1830 COMBO_KillFocus( lphc
);
1834 return COMBO_Command( lphc
, wParam
, (HWND
)lParam
);
1836 return COMBO_GetText( lphc
, (UINT
)wParam
, (LPSTR
)lParam
);
1838 case WM_GETTEXTLENGTH
:
1843 if( lphc
->wState
& CBF_EDIT
)
1845 lphc
->wState
|= CBF_NOEDITNOTIFY
;
1847 return SendMessageA( lphc
->hWndEdit
, message
, wParam
, lParam
);
1852 case WM_COMPAREITEM
:
1853 case WM_MEASUREITEM
:
1854 return COMBO_ItemOp( lphc
, message
, wParam
, lParam
);
1856 if( lphc
->wState
& CBF_EDIT
)
1857 EnableWindow( lphc
->hWndEdit
, (BOOL
)wParam
);
1858 EnableWindow( lphc
->hWndLBox
, (BOOL
)wParam
);
1862 lphc
->wState
&= ~CBF_NOREDRAW
;
1864 lphc
->wState
|= CBF_NOREDRAW
;
1866 if( lphc
->wState
& CBF_EDIT
)
1867 SendMessageA( lphc
->hWndEdit
, message
, wParam
, lParam
);
1868 SendMessageA( lphc
->hWndLBox
, message
, wParam
, lParam
);
1871 if( KEYDATA_ALT
& HIWORD(lParam
) )
1872 if( wParam
== VK_UP
|| wParam
== VK_DOWN
)
1873 COMBO_FlipListbox( lphc
, TRUE
);
1874 break;/* -> DefWindowProc */
1878 if( lphc
->wState
& CBF_EDIT
)
1879 return SendMessageA( lphc
->hWndEdit
, message
, wParam
, lParam
);
1881 return SendMessageA( lphc
->hWndLBox
, message
, wParam
, lParam
);
1882 case WM_LBUTTONDOWN
:
1883 if( !(lphc
->wState
& CBF_FOCUSED
) ) SetFocus( lphc
->self
->hwndSelf
);
1884 if( lphc
->wState
& CBF_FOCUSED
) COMBO_LButtonDown( lphc
, lParam
);
1887 COMBO_LButtonUp( lphc
, lParam
);
1890 if( lphc
->wState
& CBF_CAPTURE
)
1891 COMBO_MouseMove( lphc
, wParam
, lParam
);
1893 /* Combo messages */
1895 case CB_ADDSTRING16
:
1896 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1898 return SendMessageA( lphc
->hWndLBox
, LB_ADDSTRING
, 0, lParam
);
1899 case CB_INSERTSTRING16
:
1900 wParam
= (INT
)(INT16
)wParam
;
1901 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1902 case CB_INSERTSTRING
:
1903 return SendMessageA( lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
1904 case CB_DELETESTRING16
:
1905 case CB_DELETESTRING
:
1906 return SendMessageA( lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0);
1907 case CB_SELECTSTRING16
:
1908 wParam
= (INT
)(INT16
)wParam
;
1909 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1910 case CB_SELECTSTRING
:
1911 return COMBO_SelectString( lphc
, (INT
)wParam
, (LPSTR
)lParam
);
1912 case CB_FINDSTRING16
:
1913 wParam
= (INT
)(INT16
)wParam
;
1914 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1916 return SendMessageA( lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
);
1917 case CB_FINDSTRINGEXACT16
:
1918 wParam
= (INT
)(INT16
)wParam
;
1919 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1920 case CB_FINDSTRINGEXACT
:
1921 return SendMessageA( lphc
->hWndLBox
, LB_FINDSTRINGEXACT
,
1923 case CB_SETITEMHEIGHT16
:
1924 wParam
= (INT
)(INT16
)wParam
; /* signed integer */
1925 case CB_SETITEMHEIGHT
:
1926 return COMBO_SetItemHeight( lphc
, (INT
)wParam
, (INT
)lParam
);
1927 case CB_GETITEMHEIGHT16
:
1928 wParam
= (INT
)(INT16
)wParam
;
1929 case CB_GETITEMHEIGHT
:
1930 if( (INT
)wParam
>= 0 ) /* listbox item */
1931 return SendMessageA( lphc
->hWndLBox
, LB_GETITEMHEIGHT
, wParam
, 0);
1932 return CBGetTextAreaHeight(hwnd
, lphc
);
1933 case CB_RESETCONTENT16
:
1934 case CB_RESETCONTENT
:
1935 SendMessageA( lphc
->hWndLBox
, LB_RESETCONTENT
, 0, 0 );
1936 InvalidateRect(CB_HWND(lphc
), NULL
, TRUE
);
1938 case CB_INITSTORAGE
:
1939 return SendMessageA( lphc
->hWndLBox
, LB_INITSTORAGE
, wParam
, lParam
);
1940 case CB_GETHORIZONTALEXTENT
:
1941 return SendMessageA( lphc
->hWndLBox
, LB_GETHORIZONTALEXTENT
, 0, 0);
1942 case CB_SETHORIZONTALEXTENT
:
1943 return SendMessageA( lphc
->hWndLBox
, LB_SETHORIZONTALEXTENT
, wParam
, 0);
1944 case CB_GETTOPINDEX
:
1945 return SendMessageA( lphc
->hWndLBox
, LB_GETTOPINDEX
, 0, 0);
1947 return SendMessageA( lphc
->hWndLBox
, LB_GETLOCALE
, 0, 0);
1949 return SendMessageA( lphc
->hWndLBox
, LB_SETLOCALE
, wParam
, 0);
1950 case CB_GETDROPPEDWIDTH
:
1951 if( lphc
->droppedWidth
)
1952 return lphc
->droppedWidth
;
1953 return lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
1954 case CB_SETDROPPEDWIDTH
:
1955 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) &&
1956 (INT
)wParam
< 32768 ) lphc
->droppedWidth
= (INT
)wParam
;
1958 case CB_GETDROPPEDCONTROLRECT16
:
1959 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1963 CBGetDroppedControlRect( lphc
, &r
);
1964 CONV_RECT32TO16( &r
, (LPRECT16
)lParam
);
1967 case CB_GETDROPPEDCONTROLRECT
:
1968 if( lParam
) CBGetDroppedControlRect(lphc
, (LPRECT
)lParam
);
1970 case CB_GETDROPPEDSTATE16
:
1971 case CB_GETDROPPEDSTATE
:
1972 return (lphc
->wState
& CBF_DROPPED
) ? TRUE
: FALSE
;
1974 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1977 return COMBO_Directory( lphc
, (UINT
)wParam
,
1978 (LPSTR
)lParam
, (message
== CB_DIR
));
1979 case CB_SHOWDROPDOWN16
:
1980 case CB_SHOWDROPDOWN
:
1981 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1985 if( !(lphc
->wState
& CBF_DROPPED
) )
1989 if( lphc
->wState
& CBF_DROPPED
)
1990 CBRollUp( lphc
, FALSE
, TRUE
);
1995 return SendMessageA( lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
1996 case CB_GETCURSEL16
:
1998 return SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1999 case CB_SETCURSEL16
:
2000 wParam
= (INT
)(INT16
)wParam
;
2002 lParam
= SendMessageA( lphc
->hWndLBox
, LB_SETCURSEL
, wParam
, 0);
2003 if( lphc
->wState
& CBF_SELCHANGE
)
2005 /* no LBN_SELCHANGE in this case, update manually */
2006 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
2007 lphc
->wState
&= ~CBF_SELCHANGE
;
2010 case CB_GETLBTEXT16
:
2011 wParam
= (INT
)(INT16
)wParam
;
2012 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2014 return SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
);
2015 case CB_GETLBTEXTLEN16
:
2016 wParam
= (INT
)(INT16
)wParam
;
2017 case CB_GETLBTEXTLEN
:
2018 return SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0);
2019 case CB_GETITEMDATA16
:
2020 wParam
= (INT
)(INT16
)wParam
;
2021 case CB_GETITEMDATA
:
2022 return SendMessageA( lphc
->hWndLBox
, LB_GETITEMDATA
, wParam
, 0);
2023 case CB_SETITEMDATA16
:
2024 wParam
= (INT
)(INT16
)wParam
;
2025 case CB_SETITEMDATA
:
2026 return SendMessageA( lphc
->hWndLBox
, LB_SETITEMDATA
, wParam
, lParam
);
2027 case CB_GETEDITSEL16
:
2028 wParam
= lParam
= 0; /* just in case */
2030 if( lphc
->wState
& CBF_EDIT
)
2034 return SendMessageA( lphc
->hWndEdit
, EM_GETSEL
,
2035 (wParam
) ? wParam
: (WPARAM
)&a
,
2036 (lParam
) ? lParam
: (LPARAM
)&b
);
2039 case CB_SETEDITSEL16
:
2041 if( lphc
->wState
& CBF_EDIT
)
2042 return SendMessageA( lphc
->hWndEdit
, EM_SETSEL
,
2043 (INT
)(INT16
)LOWORD(lParam
), (INT
)(INT16
)HIWORD(lParam
) );
2045 case CB_SETEXTENDEDUI16
:
2046 case CB_SETEXTENDEDUI
:
2047 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
2050 lphc
->wState
|= CBF_EUI
;
2051 else lphc
->wState
&= ~CBF_EUI
;
2053 case CB_GETEXTENDEDUI16
:
2054 case CB_GETEXTENDEDUI
:
2055 return (lphc
->wState
& CBF_EUI
) ? TRUE
: FALSE
;
2056 case (WM_USER
+ 0x1B):
2057 WARN("[%04x]: undocumented msg!\n", hwnd
);
2059 return DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2064 /***********************************************************************
2067 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2070 LRESULT WINAPI
ComboWndProc( HWND hwnd
, UINT message
,
2071 WPARAM wParam
, LPARAM lParam
)
2073 WND
* pWnd
= WIN_FindWndPtr(hwnd
);
2074 LRESULT retvalue
= ComboWndProc_locked(pWnd
,message
,wParam
,lParam
);
2077 WIN_ReleaseWndPtr(pWnd
);