1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/renderer.cpp
3 // Purpose: implementation of wxRendererNative for Windows
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // for compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
28 #include "wx/string.h"
29 #include "wx/window.h"
31 #include "wx/settings.h"
34 #include "wx/dcgraph.h"
35 #include "wx/scopeguard.h"
36 #include "wx/splitter.h"
37 #include "wx/renderer.h"
38 #include "wx/msw/private.h"
39 #include "wx/msw/uxtheme.h"
41 // tmschema.h is in Win32 Platform SDK and might not be available with earlier
43 #ifndef CP_DROPDOWNBUTTON
44 #define BP_PUSHBUTTON 1
45 #define BP_RADIOBUTTON 2
47 #define RBS_UNCHECKEDNORMAL 1
48 #define RBS_CHECKEDNORMAL (RBS_UNCHECKEDNORMAL + 4)
49 #define RBS_MIXEDNORMAL (RBS_CHECKEDNORMAL + 4)
50 #define CBS_UNCHECKEDNORMAL 1
51 #define CBS_CHECKEDNORMAL (CBS_UNCHECKEDNORMAL + 4)
52 #define CBS_MIXEDNORMAL (CBS_CHECKEDNORMAL + 4)
57 #define PBS_DISABLED 4
58 #define PBS_DEFAULTED 5
60 #define CP_DROPDOWNBUTTON 1
64 #define CBXS_PRESSED 3
65 #define CBXS_DISABLED 4
72 #define HP_HEADERITEM 1
78 #define TMT_HEIGHT 2417
80 #define HP_HEADERSORTARROW 4
81 #define HSAS_SORTEDUP 1
82 #define HSAS_SORTEDDOWN 2
87 #define ETS_SELECTED 3
88 #define ETS_DISABLED 4
90 #define ETS_READONLY 6
92 #define TMT_FILLCOLOR 3802
93 #define TMT_TEXTCOLOR 3803
94 #define TMT_BORDERCOLOR 3801
95 #define TMT_EDGEFILLCOLOR 3808
97 #define WP_MINBUTTON 15
98 #define WP_MAXBUTTON 17
99 #define WP_CLOSEBUTTON 18
100 #define WP_RESTOREBUTTON 21
101 #define WP_HELPBUTTON 23
104 #if defined(__WXWINCE__)
114 #define DFCS_HOT 0x1000
117 // When we're using GDI+, the DC might have transforms applied to it,
118 // but the renderer APIs don't respect them. So we need to apply
119 // the transforms to the rect ourselves.
121 wxRect
applyGDIPlusTransformsToRect(wxDC
& dc
, const wxRect
& r
)
124 #if wxUSE_GRAPHICS_CONTEXT
125 wxGCDC
* gcdc
= dynamic_cast<wxGCDC
*>(&dc
);
130 wxGraphicsContext
* gc
= gcdc
->GetGraphicsContext();
131 gc
->GetTransform().TransformPoint(&xtrans
, &ytrans
);
132 rect
.x
= rect
.x
+ (int)xtrans
;
133 rect
.y
= rect
.y
+ (int)ytrans
;
139 // ----------------------------------------------------------------------------
140 // methods common to wxRendererMSW and wxRendererXP
141 // ----------------------------------------------------------------------------
143 class wxRendererMSWBase
: public wxDelegateRendererNative
146 wxRendererMSWBase() { }
147 wxRendererMSWBase(wxRendererNative
& rendererNative
)
148 : wxDelegateRendererNative(rendererNative
) { }
150 void DrawFocusRect(wxWindow
* win
,
155 void DrawItemSelectionRect(wxWindow
*win
,
161 // ----------------------------------------------------------------------------
162 // wxRendererMSW: wxRendererNative implementation for "old" Win32 systems
163 // ----------------------------------------------------------------------------
165 class wxRendererMSW
: public wxRendererMSWBase
170 static wxRendererNative
& Get();
172 virtual void DrawComboBoxDropButton(wxWindow
*win
,
177 virtual void DrawCheckBox(wxWindow
*win
,
182 DoDrawButton(DFCS_BUTTONCHECK
, win
, dc
, rect
, flags
);
185 virtual void DrawPushButton(wxWindow
*win
,
190 virtual void DrawChoice(wxWindow
* win
,
195 virtual void DrawComboBox(wxWindow
* win
,
200 virtual void DrawTextCtrl(wxWindow
* win
,
205 virtual void DrawRadioBitmap(wxWindow
* win
,
210 DoDrawButton(DFCS_BUTTONRADIO
, win
, dc
, rect
, flags
);
213 virtual void DrawTitleBarBitmap(wxWindow
*win
,
216 wxTitleBarButton button
,
219 virtual wxSize
GetCheckBoxSize(wxWindow
*win
);
221 virtual int GetHeaderButtonHeight(wxWindow
*win
);
223 virtual int GetHeaderButtonMargin(wxWindow
*win
);
226 // wrapper of DrawFrameControl()
227 void DoDrawFrameControl(UINT type
,
234 // common part of Draw{PushButton,CheckBox,RadioBitmap}(): wraps
235 // DrawFrameControl(DFC_BUTTON)
236 void DoDrawButton(UINT kind
,
242 DoDrawFrameControl(DFC_BUTTON
, kind
, win
, dc
, rect
, flags
);
245 wxDECLARE_NO_COPY_CLASS(wxRendererMSW
);
248 // ----------------------------------------------------------------------------
249 // wxRendererXP: wxRendererNative implementation for Windows XP and later
250 // ----------------------------------------------------------------------------
254 class wxRendererXP
: public wxRendererMSWBase
257 wxRendererXP() : wxRendererMSWBase(wxRendererMSW::Get()) { }
259 static wxRendererNative
& Get();
261 virtual int DrawHeaderButton(wxWindow
*win
,
265 wxHeaderSortIconType sortArrow
= wxHDR_SORT_ICON_NONE
,
266 wxHeaderButtonParams
* params
= NULL
);
268 virtual void DrawTreeItemButton(wxWindow
*win
,
272 virtual void DrawSplitterBorder(wxWindow
*win
,
276 virtual void DrawSplitterSash(wxWindow
*win
,
280 wxOrientation orient
,
282 virtual void DrawComboBoxDropButton(wxWindow
*win
,
286 virtual void DrawCheckBox(wxWindow
*win
,
291 if ( !DoDrawXPButton(BP_CHECKBOX
, win
, dc
, rect
, flags
) )
292 m_rendererNative
.DrawCheckBox(win
, dc
, rect
, flags
);
295 virtual void DrawPushButton(wxWindow
*win
,
300 if ( !DoDrawXPButton(BP_PUSHBUTTON
, win
, dc
, rect
, flags
) )
301 m_rendererNative
.DrawPushButton(win
, dc
, rect
, flags
);
304 virtual void DrawRadioBitmap(wxWindow
*win
,
309 if ( !DoDrawXPButton(BP_RADIOBUTTON
, win
, dc
, rect
, flags
) )
310 m_rendererNative
.DrawRadioBitmap(win
, dc
, rect
, flags
);
313 virtual void DrawTitleBarBitmap(wxWindow
*win
,
316 wxTitleBarButton button
,
319 virtual wxSplitterRenderParams
GetSplitterParams(const wxWindow
*win
);
322 // wrapper around DrawThemeBackground() translating flags to NORMAL/HOT/
323 // PUSHED/DISABLED states (and so suitable for drawing anything
325 void DoDrawButtonLike(HTHEME htheme
,
331 // common part of DrawCheckBox(), DrawPushButton() and DrawRadioBitmap()
332 bool DoDrawXPButton(int kind
,
338 wxDECLARE_NO_COPY_CLASS(wxRendererXP
);
341 #endif // wxUSE_UXTHEME
344 // ============================================================================
345 // wxRendererMSWBase implementation
346 // ============================================================================
348 void wxRendererMSWBase::DrawFocusRect(wxWindow
* WXUNUSED(win
),
354 wxCopyRectToRECT(rect
, rc
);
356 ::DrawFocusRect(GetHdcOf(dc
.GetTempHDC()), &rc
);
359 void wxRendererMSWBase::DrawItemSelectionRect(wxWindow
*win
,
365 if ( flags
& wxCONTROL_SELECTED
)
367 if ( flags
& wxCONTROL_FOCUSED
)
369 brush
= wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
373 brush
= wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE
));
378 brush
= *wxTRANSPARENT_BRUSH
;
382 dc
.SetPen(*wxTRANSPARENT_PEN
);
383 dc
.DrawRectangle( rect
);
385 if ((flags
& wxCONTROL_FOCUSED
) && (flags
& wxCONTROL_CURRENT
))
386 DrawFocusRect( win
, dc
, rect
, flags
);
390 // ============================================================================
391 // wxRendererNative and wxRendererMSW implementation
392 // ============================================================================
395 wxRendererNative
& wxRendererNative::GetDefault()
398 wxUxThemeEngine
*themeEngine
= wxUxThemeEngine::Get();
399 if ( themeEngine
&& themeEngine
->IsAppThemed() )
400 return wxRendererXP::Get();
401 #endif // wxUSE_UXTHEME
403 return wxRendererMSW::Get();
407 wxRendererNative
& wxRendererMSW::Get()
409 static wxRendererMSW s_rendererMSW
;
411 return s_rendererMSW
;
415 wxRendererMSW::DrawComboBoxDropButton(wxWindow
* WXUNUSED(win
),
420 wxRect adjustedRect
= applyGDIPlusTransformsToRect(dc
, rect
);
423 wxCopyRectToRECT(adjustedRect
, r
);
425 int style
= DFCS_SCROLLCOMBOBOX
;
426 if ( flags
& wxCONTROL_DISABLED
)
427 style
|= DFCS_INACTIVE
;
428 if ( flags
& wxCONTROL_PRESSED
)
429 style
|= DFCS_PUSHED
| DFCS_FLAT
;
431 ::DrawFrameControl(GetHdcOf(dc
.GetTempHDC()), &r
, DFC_SCROLL
, style
);
435 wxRendererMSW::DoDrawFrameControl(UINT type
,
437 wxWindow
* WXUNUSED(win
),
442 wxRect adjustedRect
= applyGDIPlusTransformsToRect(dc
, rect
);
445 wxCopyRectToRECT(adjustedRect
, r
);
448 if ( flags
& wxCONTROL_CHECKED
)
449 style
|= DFCS_CHECKED
;
450 if ( flags
& wxCONTROL_DISABLED
)
451 style
|= DFCS_INACTIVE
;
452 if ( flags
& wxCONTROL_FLAT
)
454 if ( flags
& wxCONTROL_PRESSED
)
455 style
|= DFCS_PUSHED
;
456 if ( flags
& wxCONTROL_CURRENT
)
459 ::DrawFrameControl(GetHdcOf(dc
.GetTempHDC()), &r
, type
, style
);
463 wxRendererMSW::DrawPushButton(wxWindow
*win
,
465 const wxRect
& rectOrig
,
468 wxRect
rect(rectOrig
);
469 if ( flags
& wxCONTROL_ISDEFAULT
)
471 // DrawFrameControl() doesn't seem to support default buttons so we
472 // have to draw the border ourselves
473 wxDCPenChanger
pen(dc
, *wxBLACK_PEN
);
474 wxDCBrushChanger
brush(dc
, *wxTRANSPARENT_BRUSH
);
475 dc
.DrawRectangle(rect
);
479 DoDrawButton(DFCS_BUTTONPUSH
, win
, dc
, rect
, flags
);
483 wxRendererMSW::DrawTitleBarBitmap(wxWindow
*win
,
486 wxTitleBarButton button
,
492 case wxTITLEBAR_BUTTON_CLOSE
:
493 kind
= DFCS_CAPTIONCLOSE
;
496 case wxTITLEBAR_BUTTON_MAXIMIZE
:
497 kind
= DFCS_CAPTIONMAX
;
500 case wxTITLEBAR_BUTTON_ICONIZE
:
501 kind
= DFCS_CAPTIONMIN
;
504 case wxTITLEBAR_BUTTON_RESTORE
:
505 kind
= DFCS_CAPTIONRESTORE
;
508 case wxTITLEBAR_BUTTON_HELP
:
509 kind
= DFCS_CAPTIONHELP
;
513 wxFAIL_MSG( "unsupported title bar button" );
517 DoDrawFrameControl(DFC_CAPTION
, kind
, win
, dc
, rect
, flags
);
520 wxSize
wxRendererMSW::GetCheckBoxSize(wxWindow
* WXUNUSED(win
))
522 return wxSize(::GetSystemMetrics(SM_CXMENUCHECK
),
523 ::GetSystemMetrics(SM_CYMENUCHECK
));
526 int wxRendererMSW::GetHeaderButtonHeight(wxWindow
* WXUNUSED(win
))
528 // some "reasonable" value returned in case of error, it doesn't really
529 // correspond to anything but it's better than returning 0
530 static const int DEFAULT_HEIGHT
= 20;
533 // create a temporary header window just to get its geometry
534 HWND hwndHeader
= ::CreateWindow(WC_HEADER
, NULL
, 0,
535 0, 0, 0, 0, NULL
, NULL
, NULL
, NULL
);
537 return DEFAULT_HEIGHT
;
539 wxON_BLOCK_EXIT1( ::DestroyWindow
, hwndHeader
);
541 // initialize the struct filled with the values by Header_Layout()
542 RECT parentRect
= { 0, 0, 100, 100 };
543 WINDOWPOS wp
= { 0, 0, 0, 0, 0, 0, 0 };
544 HDLAYOUT hdl
= { &parentRect
, &wp
};
546 return Header_Layout(hwndHeader
, &hdl
) ? wp
.cy
: DEFAULT_HEIGHT
;
549 int wxRendererMSW::GetHeaderButtonMargin(wxWindow
*WXUNUSED(win
))
554 // Uses the theme to draw the border and fill for something like a wxTextCtrl
555 void wxRendererMSW::DrawTextCtrl(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
562 wxUxThemeHandle
hTheme(win
, L
"EDIT");
565 wxUxThemeEngine::Get()->GetThemeColor(hTheme
, EP_EDITTEXT
,
566 ETS_NORMAL
, TMT_FILLCOLOR
, &cref
);
567 fill
= wxRGBToColour(cref
);
570 if ( flags
& wxCONTROL_DISABLED
)
571 etsState
= ETS_DISABLED
;
573 etsState
= ETS_NORMAL
;
575 wxUxThemeEngine::Get()->GetThemeColor(hTheme
, EP_EDITTEXT
,
576 etsState
, TMT_BORDERCOLOR
, &cref
);
577 bdr
= wxRGBToColour(cref
);
582 fill
= wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW
);
588 dc
.DrawRectangle(rect
);
592 // Draw the equivalent of a wxComboBox
593 void wxRendererMSW::DrawComboBox(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
595 // Draw the main part of the control same as TextCtrl
596 DrawTextCtrl(win
, dc
, rect
, flags
);
598 // Draw the button inside the border, on the right side
601 br
.x
+= br
.width
- br
.height
- 1;
602 br
.width
= br
.height
;
605 DrawComboBoxDropButton(win
, dc
, br
, flags
);
609 void wxRendererMSW::DrawChoice(wxWindow
* win
, wxDC
& dc
,
610 const wxRect
& rect
, int flags
)
612 DrawComboBox(win
, dc
, rect
, flags
);
615 // ============================================================================
616 // wxRendererXP implementation
617 // ============================================================================
622 wxRendererNative
& wxRendererXP::Get()
624 static wxRendererXP s_rendererXP
;
629 // NOTE: There is no guarantee that the button drawn fills the entire rect (XP
630 // default theme, for example), so the caller should have cleared button's
631 // background before this call. This is quite likely a wxMSW-specific thing.
633 wxRendererXP::DrawComboBoxDropButton(wxWindow
* win
,
638 wxUxThemeHandle
hTheme(win
, L
"COMBOBOX");
641 m_rendererNative
.DrawComboBoxDropButton(win
, dc
, rect
, flags
);
645 wxRect adjustedRect
= applyGDIPlusTransformsToRect(dc
, rect
);
648 wxCopyRectToRECT(adjustedRect
, r
);
651 if ( flags
& wxCONTROL_PRESSED
)
652 state
= CBXS_PRESSED
;
653 else if ( flags
& wxCONTROL_CURRENT
)
655 else if ( flags
& wxCONTROL_DISABLED
)
656 state
= CBXS_DISABLED
;
660 wxUxThemeEngine::Get()->DrawThemeBackground
663 GetHdcOf(dc
.GetTempHDC()),
673 wxRendererXP::DrawHeaderButton(wxWindow
*win
,
677 wxHeaderSortIconType sortArrow
,
678 wxHeaderButtonParams
* params
)
680 wxUxThemeHandle
hTheme(win
, L
"HEADER");
683 return m_rendererNative
.DrawHeaderButton(win
, dc
, rect
, flags
, sortArrow
, params
);
686 wxRect adjustedRect
= applyGDIPlusTransformsToRect(dc
, rect
);
689 wxCopyRectToRECT(adjustedRect
, r
);
692 if ( flags
& wxCONTROL_PRESSED
)
694 else if ( flags
& wxCONTROL_CURRENT
)
698 wxUxThemeEngine::Get()->DrawThemeBackground
701 GetHdcOf(dc
.GetTempHDC()),
708 // NOTE: Using the theme to draw HP_HEADERSORTARROW doesn't do anything.
709 // Why? If this can be fixed then draw the sort arrows using the theme
710 // and then clear those flags before calling DrawHeaderButtonContents.
712 // Add any extras that are specified in flags and params
713 return DrawHeaderButtonContents(win
, dc
, rect
, flags
, sortArrow
, params
);
718 wxRendererXP::DrawTreeItemButton(wxWindow
*win
,
723 wxUxThemeHandle
hTheme(win
, L
"TREEVIEW");
726 m_rendererNative
.DrawTreeItemButton(win
, dc
, rect
, flags
);
730 wxRect adjustedRect
= applyGDIPlusTransformsToRect(dc
, rect
);
733 wxCopyRectToRECT(adjustedRect
, r
);
735 int state
= flags
& wxCONTROL_EXPANDED
? GLPS_OPENED
: GLPS_CLOSED
;
736 wxUxThemeEngine::Get()->DrawThemeBackground
739 GetHdcOf(dc
.GetTempHDC()),
748 wxRendererXP::DoDrawXPButton(int kind
,
754 wxUxThemeHandle
hTheme(win
, L
"BUTTON");
758 DoDrawButtonLike(hTheme
, kind
, dc
, rect
, flags
);
764 wxRendererXP::DoDrawButtonLike(HTHEME htheme
,
770 wxRect adjustedRect
= applyGDIPlusTransformsToRect(dc
, rect
);
773 wxCopyRectToRECT(adjustedRect
, r
);
775 // the base state is always 1, whether it is PBS_NORMAL,
776 // {CBS,RBS}_UNCHECKEDNORMAL or CBS_NORMAL
779 // XBS_XXX is followed by XBX_XXXHOT, then XBS_XXXPRESSED and DISABLED
789 // in both RBS_ and CBS_ enums CHECKED elements are offset by 4 from base
790 // (UNCHECKED) ones and MIXED are offset by 4 again as there are all states
791 // from the above enum in between them
792 if ( flags
& wxCONTROL_CHECKED
)
793 state
+= STATES_COUNT
;
794 else if ( flags
& wxCONTROL_UNDETERMINED
)
795 state
+= 2*STATES_COUNT
;
797 if ( flags
& wxCONTROL_DISABLED
)
798 state
+= DISABLED_OFFSET
;
799 else if ( flags
& wxCONTROL_PRESSED
)
800 state
+= PRESSED_OFFSET
;
801 else if ( flags
& wxCONTROL_CURRENT
)
803 // wxCONTROL_ISDEFAULT flag is only valid for push buttons
804 else if ( part
== BP_PUSHBUTTON
&& (flags
& wxCONTROL_ISDEFAULT
) )
805 state
= PBS_DEFAULTED
;
807 wxUxThemeEngine::Get()->DrawThemeBackground
810 GetHdcOf(dc
.GetTempHDC()),
819 wxRendererXP::DrawTitleBarBitmap(wxWindow
*win
,
822 wxTitleBarButton button
,
825 wxUxThemeHandle
hTheme(win
, L
"WINDOW");
828 m_rendererNative
.DrawTitleBarBitmap(win
, dc
, rect
, button
, flags
);
835 case wxTITLEBAR_BUTTON_CLOSE
:
836 part
= WP_CLOSEBUTTON
;
839 case wxTITLEBAR_BUTTON_MAXIMIZE
:
843 case wxTITLEBAR_BUTTON_ICONIZE
:
847 case wxTITLEBAR_BUTTON_RESTORE
:
848 part
= WP_RESTOREBUTTON
;
851 case wxTITLEBAR_BUTTON_HELP
:
852 part
= WP_HELPBUTTON
;
856 wxFAIL_MSG( "unsupported title bar button" );
860 DoDrawButtonLike(hTheme
, part
, dc
, rect
, flags
);
863 // ----------------------------------------------------------------------------
865 // ----------------------------------------------------------------------------
867 // the width of the sash: this is the same as used by Explorer...
868 static const wxCoord SASH_WIDTH
= 4;
870 wxSplitterRenderParams
871 wxRendererXP::GetSplitterParams(const wxWindow
* win
)
873 if ( win
->HasFlag(wxSP_NO_XP_THEME
) )
874 return m_rendererNative
.GetSplitterParams(win
);
876 return wxSplitterRenderParams(SASH_WIDTH
, 0, false);
880 wxRendererXP::DrawSplitterBorder(wxWindow
* win
,
885 if ( win
->HasFlag(wxSP_NO_XP_THEME
) )
887 m_rendererNative
.DrawSplitterBorder(win
, dc
, rect
, flags
);
892 wxRendererXP::DrawSplitterSash(wxWindow
*win
,
896 wxOrientation orient
,
899 if ( !win
->HasFlag(wxSP_NO_XP_THEME
) )
901 dc
.SetPen(*wxTRANSPARENT_PEN
);
902 dc
.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE
)));
903 if ( orient
== wxVERTICAL
)
905 dc
.DrawRectangle(position
, 0, SASH_WIDTH
, size
.y
);
909 dc
.DrawRectangle(0, position
, size
.x
, SASH_WIDTH
);
915 m_rendererNative
.DrawSplitterSash(win
, dc
, size
, position
, orient
, flags
);
918 #endif // wxUSE_UXTHEME