1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/combo.cpp
3 // Purpose: wxMSW wxComboCtrl
4 // Author: Jaakko Salli
6 // Created: Apr-30-2006
8 // Copyright: (c) 2005 Jaakko Salli
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 #include "wx/wxprec.h"
30 #include "wx/combobox.h"
31 #include "wx/dcclient.h"
32 #include "wx/settings.h"
33 #include "wx/dialog.h"
36 #include "wx/dcbuffer.h"
41 #include "wx/msw/uxtheme.h"
43 // Change to #if 1 to include tmschema.h for easier testing of theme
48 //----------------------------------
52 #define ETS_SELECTED 3
53 #define ETS_DISABLED 4
55 #define ETS_READONLY 6
57 #define TMT_FILLCOLOR 3802
58 #define TMT_TEXTCOLOR 3803
59 #define TMT_BORDERCOLOR 3801
60 #define TMT_EDGEFILLCOLOR 3808
61 //----------------------------------
65 #define NATIVE_TEXT_INDENT_XP 4
66 #define NATIVE_TEXT_INDENT_CLASSIC 2
68 #define TEXTCTRLXADJUST_XP 1
69 #define TEXTCTRLYADJUST_XP 3
70 #define TEXTCTRLXADJUST_CLASSIC 1
71 #define TEXTCTRLYADJUST_CLASSIC 2
74 // ============================================================================
76 // ============================================================================
79 BEGIN_EVENT_TABLE(wxComboCtrl
, wxComboCtrlBase
)
80 EVT_PAINT(wxComboCtrl::OnPaintEvent
)
81 EVT_MOUSE_EVENTS(wxComboCtrl::OnMouseEvent
)
85 IMPLEMENT_DYNAMIC_CLASS(wxComboCtrl
, wxComboCtrlBase
)
87 void wxComboCtrl::Init()
91 bool wxComboCtrl::Create(wxWindow
*parent
,
93 const wxString
& value
,
97 const wxValidator
& validator
,
102 long border
= style
& wxBORDER_MASK
;
104 wxUxThemeEngine
* theme
= wxUxThemeEngine::GetIfActive();
108 // For XP, have 1-width custom border, for older version use sunken
111 border
= wxBORDER_NONE
;
112 m_widthCustomBorder
= 1;
115 border
= wxBORDER_SUNKEN
;
117 style
= (style
& ~(wxBORDER_MASK
)) | border
;
120 // create main window
121 if ( !wxComboCtrlBase::Create(parent
,
126 style
| wxFULL_REPAINT_ON_RESIZE
,
131 if ( style
& wxCC_STD_BUTTON
)
132 m_iFlags
|= wxCC_POPUP_ON_MOUSE_UP
;
134 // Create textctrl, if necessary
135 CreateTextCtrl( wxNO_BORDER
, validator
);
137 // Add keyboard input handlers for main control and textctrl
138 InstallInputHandlers();
140 // Prepare background for double-buffering
141 SetBackgroundStyle( wxBG_STYLE_CUSTOM
);
143 // SetBestSize should be called last
149 wxComboCtrl::~wxComboCtrl()
153 void wxComboCtrl::OnThemeChange()
155 wxUxThemeEngine
* theme
= wxUxThemeEngine::GetIfActive();
158 wxUxThemeHandle
hTheme(this, L
"COMBOBOX");
161 theme
->GetThemeColor(hTheme
,EP_EDITTEXT
,ETS_NORMAL
,TMT_FILLCOLOR
,&col
);
162 SetBackgroundColour(wxRGBToColour(col
));
163 theme
->GetThemeColor(hTheme
,EP_EDITTEXT
,ETS_NORMAL
,TMT_TEXTCOLOR
,&col
);
164 SetForegroundColour(wxRGBToColour(col
));
168 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW
));
169 SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT
));
173 void wxComboCtrl::OnResize()
176 // Recalculates button and textctrl areas
181 if ( wxUxThemeEngine::GetIfActive() )
183 textCtrlXAdjust
= TEXTCTRLXADJUST_XP
;
184 textCtrlYAdjust
= TEXTCTRLYADJUST_XP
;
188 textCtrlXAdjust
= TEXTCTRLXADJUST_CLASSIC
;
189 textCtrlYAdjust
= TEXTCTRLYADJUST_CLASSIC
;
192 // Technically Classic Windows style combo has more narrow button,
193 // but the native renderer doesn't paint it well like that.
195 CalculateAreas(btnWidth
);
197 // Position textctrl using standard routine
198 PositionTextCtrl(textCtrlXAdjust
,textCtrlYAdjust
);
201 // Draws non-XP GUI dotted line around the focus area
202 static void wxMSWDrawFocusRect( wxDC
& dc
, const wxRect
& rect
)
204 #if !defined(__WXWINCE__)
207 mswRect.left = rect.x;
208 mswRect.top = rect.y;
209 mswRect.right = rect.x + rect.width;
210 mswRect.bottom = rect.y + rect.height;
211 HDC hdc = (HDC) dc.GetHDC();
212 SetMapMode(hdc,MM_TEXT); // Just in case...
213 DrawFocusRect(hdc,&mswRect);
215 // FIXME: Use DrawFocusRect code above (currently it draws solid line
216 // for caption focus but works ok for other stuff).
217 // Also, this code below may not work in future wx versions, since
218 // it employs wxCAP_BUTT hack to have line of width 1.
219 dc
.SetLogicalFunction(wxINVERT
);
221 wxPen
pen(*wxBLACK
,1,wxDOT
);
222 pen
.SetCap(wxCAP_BUTT
);
224 dc
.SetBrush(*wxTRANSPARENT_BRUSH
);
226 dc
.DrawRectangle(rect
);
228 dc
.SetLogicalFunction(wxCOPY
);
230 dc
.SetLogicalFunction(wxINVERT
);
232 dc
.SetPen(wxPen(*wxBLACK
,1,wxDOT
));
233 dc
.SetBrush(*wxTRANSPARENT_BRUSH
);
235 dc
.DrawRectangle(rect
);
237 dc
.SetLogicalFunction(wxCOPY
);
241 // draw focus background on area in a way typical on platform
242 void wxComboCtrl::PrepareBackground( wxDC
& dc
, const wxRect
& rect
, int flags
) const
244 wxUxThemeEngine
* theme
= (wxUxThemeEngine
*) NULL
;
246 // Constructor only calls GetHWND() const, so it should be safe
247 // to cast "this" to const.
248 wxUxThemeHandle
hTheme(this, L
"COMBOBOX");
251 wxSize sz
= GetClientSize();
253 bool isFocused
; // also selected
255 // For smaller size control (and for disabled background) use less spacing
259 if ( !(flags
& wxCONTROL_ISSUBMENU
) )
262 isEnabled
= IsEnabled();
263 isFocused
= ShouldDrawFocus();
265 // Windows-style: for smaller size control (and for disabled background) use less spacing
269 focusSpacingX
= isEnabled
? 2 : 1;
270 focusSpacingY
= sz
.y
> (GetCharHeight()+2) && isEnabled
? 2 : 1;
289 // Drawing a list item
290 isEnabled
= true; // they are never disabled
291 isFocused
= flags
& wxCONTROL_SELECTED
? true : false;
297 // Set the background sub-rectangle for selection, disabled etc
298 wxRect
selRect(rect
);
299 selRect
.y
+= focusSpacingY
;
300 selRect
.height
-= (focusSpacingY
*2);
304 if ( !(flags
& wxCONTROL_ISSUBMENU
) )
305 wcp
+= m_widthCustomPaint
;
307 selRect
.x
+= wcp
+ focusSpacingX
;
308 selRect
.width
-= wcp
+ (focusSpacingX
*2);
311 theme
= wxUxThemeEngine::GetIfActive();
314 bool drawDottedEdge
= false;
318 // If popup is hidden and this control is focused,
319 // then draw the focus-indicator (selbgcolor background etc.).
323 // TODO: Proper theme color getting (JMS: I don't know which parts/colors to use,
324 // those below don't work)
327 theme
->GetThemeColor(hTheme
,EP_EDITTEXT
,ETS_SELECTED
,TMT_TEXTCOLOR
,&cref
);
328 dc
.SetTextForeground( wxRGBToColour(cref
) );
329 theme
->GetThemeColor(hTheme
,EP_EDITTEXT
,ETS_SELECTED
,TMT_FILLCOLOR
,&cref
);
330 bgCol
= wxRGBToColour(cref
);
335 dc
.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
) );
336 bgCol
= wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
);
337 if ( m_windowStyle
& wxCB_READONLY
)
338 drawDottedEdge
= true;
345 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_TEXTCOLOR,&cref);
346 dc.SetTextForeground( wxRGBToColour(cref) );
347 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_FILLCOLOR,&cref);
348 bgCol = wxRGBToColour(cref);
352 dc
.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT
) );
353 bgCol
= GetBackgroundColour();
361 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_DISABLED,TMT_TEXTCOLOR,&cref);
362 dc.SetTextForeground( wxRGBToColour(cref) );
363 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_DISABLED,TMT_EDGEFILLCOLOR,&cref);
364 bgCol = wxRGBToColour(cref);
368 dc
.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT
) );
369 bgCol
= wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE
);
375 dc
.DrawRectangle(selRect
);
376 if ( drawDottedEdge
)
377 wxMSWDrawFocusRect(dc
,selRect
);
379 // Don't clip exactly to the selection rectangle so we can draw
380 // to the non-selected area in front of it.
381 wxRect
clipRect(rect
.x
,rect
.y
,
382 (selRect
.x
+selRect
.width
)-rect
.x
-1,rect
.height
);
383 dc
.SetClippingRegion(clipRect
);
386 void wxComboCtrl::OnPaintEvent( wxPaintEvent
& WXUNUSED(event
) )
388 // TODO: Convert drawing in this function to Windows API Code
390 wxSize sz
= GetClientSize();
391 wxBufferedPaintDC
dc(this,GetBufferBitmap(sz
));
393 const wxRect
& rectb
= m_btnArea
;
394 wxRect rect
= m_tcArea
;
395 bool isEnabled
= IsEnabled();
396 wxColour bgCol
= GetBackgroundColour();
399 wxUxThemeEngine
* theme
= NULL
;
400 wxUxThemeHandle
hTheme(this, L
"COMBOBOX");
403 // area around both controls
404 wxRect
rect2(0,0,sz
.x
,sz
.y
);
405 if ( m_iFlags
& wxCC_IFLAG_BUTTON_OUTSIDE
)
411 // Use theme to draw border on XP
414 theme
= wxUxThemeEngine::GetIfActive();
417 // Select correct border colour
419 etsState
= ETS_DISABLED
;
421 etsState
= ETS_NORMAL
;
423 if ( m_widthCustomBorder
)
425 theme
->GetThemeColor(hTheme
,EP_EDITTEXT
,etsState
,TMT_BORDERCOLOR
,&cref
);
428 dc
.SetPen( wxRGBToColour(cref
) );
430 dc
.SetBrush( *wxTRANSPARENT_BRUSH
);
431 dc
.DrawRectangle(rect2
);
434 theme
->GetThemeColor(hTheme
,EP_EDITTEXT
,etsState
,TMT_TEXTCOLOR
,&cref
);
435 fgCol
= wxRGBToColour(cref
);
439 // draw regular background
440 fgCol
= GetForegroundColour();
443 rect2
.Deflate(m_widthCustomBorder
);
448 // clear main background
449 dc
.DrawRectangle(rect
);
451 // Button background with theme?
452 bool drawButBg
= true;
453 if ( hTheme
&& m_blankButtonBg
)
456 wxCopyRectToRECT(rectb
, r
);
458 // Draw parent background if needed (since button looks like its out of
459 // the combo, this is preferred).
460 theme
->DrawThemeParentBackground(GetHwndOf(this),
467 // Standard button rendering
468 DrawButton(dc
,rectb
,drawButBg
);
470 // paint required portion on the control
471 if ( (!m_text
|| m_widthCustomPaint
) )
473 wxASSERT( m_widthCustomPaint
>= 0 );
475 // this is intentionally here to allow drawed rectangle's
476 // right edge to be hidden
478 rect
.width
= m_widthCustomPaint
;
480 dc
.SetFont( GetFont() );
482 dc
.SetClippingRegion(rect
);
483 if ( m_popupInterface
)
484 m_popupInterface
->PaintComboControl(dc
,rect
);
486 wxComboPopup::DefaultPaintComboControl(this,dc
,rect
);
490 void wxComboCtrl::OnMouseEvent( wxMouseEvent
& event
)
493 bool isOnButtonArea
= m_btnArea
.Contains(mx
,event
.m_y
);
494 int handlerFlags
= isOnButtonArea
? wxCC_MF_ON_BUTTON
: 0;
496 if ( PreprocessMouseEvent(event
,isOnButtonArea
) )
499 if ( (m_windowStyle
& (wxCC_SPECIAL_DCLICK
|wxCB_READONLY
)) == wxCB_READONLY
)
501 // if no textctrl and no special double-click, then the entire control acts
503 handlerFlags
|= wxCC_MF_ON_BUTTON
;
504 if ( HandleButtonMouseEvent(event
,handlerFlags
) )
509 if ( isOnButtonArea
|| HasCapture() ||
510 (m_widthCustomPaint
&& mx
< (m_tcArea
.x
+m_widthCustomPaint
)) )
512 handlerFlags
|= wxCC_MF_ON_CLICK_AREA
;
514 if ( HandleButtonMouseEvent(event
,handlerFlags
) )
517 else if ( m_btnState
)
519 // otherwise need to clear the hover status
521 RefreshRect(m_btnArea
);
526 // This will handle left_down and left_dclick events outside button in a Windows-like manner.
527 // See header file for further information on this method.
528 HandleNormalMouseEvent(event
);
532 wxCoord
wxComboCtrl::GetNativeTextIndent() const
534 if ( wxUxThemeEngine::GetIfActive() )
535 return NATIVE_TEXT_INDENT_XP
;
536 return NATIVE_TEXT_INDENT_CLASSIC
;
539 bool wxComboCtrl::IsKeyPopupToggle(const wxKeyEvent
& event
) const
541 const bool isPopupShown
= IsPopupShown();
543 switch ( event
.GetKeyCode() )
546 // F4 toggles the popup in the native comboboxes, so emulate them
547 if ( !event
.AltDown() )
558 // On XP or with writable combo in Classic, arrows don't open the
559 // popup but Alt-arrow does
560 if ( event
.AltDown() ||
562 HasFlag(wxCB_READONLY
) &&
563 !wxUxThemeEngine::GetIfActive()
574 #endif // wxUSE_COMBOCTRL