1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/renderer.cpp
3 // Purpose: implementation of wxRendererNative for wxGTK
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"
27 #include "wx/renderer.h"
30 #include "wx/window.h"
31 #include "wx/dcclient.h"
32 #include "wx/settings.h"
33 #include "wx/module.h"
36 #include "wx/dcgraph.h"
38 #include "wx/gtk/dc.h"
42 #include "wx/gtk/private.h"
43 #include "wx/gtk/private/gtk2-compat.h"
45 // ----------------------------------------------------------------------------
46 // wxRendererGTK: our wxRendererNative implementation
47 // ----------------------------------------------------------------------------
49 class WXDLLEXPORT wxRendererGTK
: public wxDelegateRendererNative
52 // draw the header control button (used by wxListCtrl)
53 virtual int DrawHeaderButton(wxWindow
*win
,
57 wxHeaderSortIconType sortArrow
= wxHDR_SORT_ICON_NONE
,
58 wxHeaderButtonParams
* params
= NULL
);
60 virtual int GetHeaderButtonHeight(wxWindow
*win
);
62 virtual int GetHeaderButtonMargin(wxWindow
*win
);
65 // draw the expanded/collapsed icon for a tree control item
66 virtual void DrawTreeItemButton(wxWindow
*win
,
71 virtual void DrawSplitterBorder(wxWindow
*win
,
75 virtual void DrawSplitterSash(wxWindow
*win
,
82 virtual void DrawComboBoxDropButton(wxWindow
*win
,
87 virtual void DrawDropArrow(wxWindow
*win
,
92 virtual void DrawCheckBox(wxWindow
*win
,
97 virtual void DrawPushButton(wxWindow
*win
,
102 virtual void DrawItemSelectionRect(wxWindow
*win
,
107 virtual void DrawChoice(wxWindow
* win
,
112 virtual void DrawComboBox(wxWindow
* win
,
117 virtual void DrawTextCtrl(wxWindow
* win
,
122 virtual void DrawRadioBitmap(wxWindow
* win
,
127 virtual void DrawFocusRect(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
= 0);
129 virtual wxSize
GetCheckBoxSize(wxWindow
*win
);
131 virtual wxSplitterRenderParams
GetSplitterParams(const wxWindow
*win
);
134 // ============================================================================
136 // ============================================================================
139 wxRendererNative
& wxRendererNative::GetDefault()
141 static wxRendererGTK s_rendererGTK
;
143 return s_rendererGTK
;
148 typedef cairo_t wxGTKDrawable
;
150 static cairo_t
* wxGetGTKDrawable(wxWindow
*, const wxDC
& dc
)
152 wxGraphicsContext
* gc
= dc
.GetGraphicsContext();
153 wxCHECK_MSG(gc
, NULL
, "cannot use wxRendererNative on wxDC of this type");
154 return static_cast<cairo_t
*>(gc
->GetNativeContext());
157 static const GtkStateFlags stateTypeToFlags
[] = {
158 GTK_STATE_FLAG_NORMAL
, GTK_STATE_FLAG_ACTIVE
, GTK_STATE_FLAG_PRELIGHT
,
159 GTK_STATE_FLAG_SELECTED
, GTK_STATE_FLAG_INSENSITIVE
, GTK_STATE_FLAG_INCONSISTENT
,
160 GTK_STATE_FLAG_FOCUSED
164 #define NULL_RECT NULL,
165 typedef GdkWindow wxGTKDrawable
;
167 static GdkWindow
* wxGetGTKDrawable(wxWindow
* win
, wxDC
& dc
)
169 GdkWindow
* gdk_window
= NULL
;
171 #if wxUSE_GRAPHICS_CONTEXT
172 if ( wxDynamicCast(&dc
, wxGCDC
) )
173 gdk_window
= win
->GTKGetDrawingWindow();
177 wxDCImpl
*impl
= dc
.GetImpl();
178 wxGTKDCImpl
*gtk_impl
= wxDynamicCast( impl
, wxGTKDCImpl
);
180 gdk_window
= gtk_impl
->GetGDKWindow();
182 wxFAIL_MSG("cannot use wxRendererNative on wxDC of this type");
185 #if !wxUSE_GRAPHICS_CONTEXT
193 // ----------------------------------------------------------------------------
194 // list/tree controls drawing
195 // ----------------------------------------------------------------------------
198 wxRendererGTK::DrawHeaderButton(wxWindow
*win
,
202 wxHeaderSortIconType sortArrow
,
203 wxHeaderButtonParams
* params
)
205 GtkWidget
*button
= wxGTKPrivate::GetHeaderButtonWidget();
206 if (flags
& wxCONTROL_SPECIAL
)
207 button
= wxGTKPrivate::GetHeaderButtonWidgetFirst();
208 if (flags
& wxCONTROL_DIRTY
)
209 button
= wxGTKPrivate::GetHeaderButtonWidgetLast();
212 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
215 GtkStateType state
= GTK_STATE_NORMAL
;
216 if (flags
& wxCONTROL_DISABLED
)
217 state
= GTK_STATE_INSENSITIVE
;
220 if (flags
& wxCONTROL_CURRENT
)
221 state
= GTK_STATE_PRELIGHT
;
225 cairo_t
* cr
= wxGetGTKDrawable(win
, dc
);
228 GtkStyleContext
* sc
= gtk_widget_get_style_context(button
);
229 gtk_style_context_save(sc
);
230 gtk_style_context_set_state(sc
, stateTypeToFlags
[state
]);
231 gtk_render_background(sc
, cr
, rect
.x
- x_diff
+4, rect
.y
+4, rect
.width
-8, rect
.height
-8);
232 gtk_render_frame(sc
, cr
, rect
.x
- x_diff
+4, rect
.y
+4, rect
.width
-8, rect
.height
-8);
233 gtk_style_context_restore(sc
);
236 GdkWindow
* gdk_window
= wxGetGTKDrawable(win
, dc
);
239 gtk_widget_get_style(button
),
246 dc
.LogicalToDeviceX(rect
.x
) - x_diff
, rect
.y
, rect
.width
, rect
.height
250 return DrawHeaderButtonContents(win
, dc
, rect
, flags
, sortArrow
, params
);
253 int wxRendererGTK::GetHeaderButtonHeight(wxWindow
*WXUNUSED(win
))
255 GtkWidget
*button
= wxGTKPrivate::GetHeaderButtonWidget();
259 gtk_widget_get_preferred_height(button
, NULL
, &req
.height
);
261 GTK_WIDGET_GET_CLASS(button
)->size_request(button
, &req
);
267 int wxRendererGTK::GetHeaderButtonMargin(wxWindow
*WXUNUSED(win
))
269 wxFAIL_MSG( "GetHeaderButtonMargin() not implemented" );
274 // draw a ">" or "v" button
276 wxRendererGTK::DrawTreeItemButton(wxWindow
* win
,
277 wxDC
& dc
, const wxRect
& rect
, int flags
)
279 GtkWidget
*tree
= wxGTKPrivate::GetTreeWidget();
282 if ( flags
& wxCONTROL_CURRENT
)
283 state
= GTK_STATE_PRELIGHT
;
285 state
= GTK_STATE_NORMAL
;
288 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
292 cairo_t
* cr
= wxGetGTKDrawable(win
, dc
);
295 gtk_widget_set_state_flags(tree
, stateTypeToFlags
[state
], true);
296 GtkStyleContext
* sc
= gtk_widget_get_style_context(tree
);
297 gtk_render_expander(sc
, cr
, rect
.x
- x_diff
, rect
.y
, rect
.width
, rect
.height
);
300 // x and y parameters specify the center of the expander
301 GdkWindow
* gdk_window
= wxGetGTKDrawable(win
, dc
);
302 if (gdk_window
== NULL
)
306 gtk_widget_get_style(tree
),
312 dc
.LogicalToDeviceX(rect
.x
) + rect
.width
/ 2 - x_diff
,
313 dc
.LogicalToDeviceY(rect
.y
) + rect
.height
/ 2,
314 flags
& wxCONTROL_EXPANDED
? GTK_EXPANDER_EXPANDED
315 : GTK_EXPANDER_COLLAPSED
321 // ----------------------------------------------------------------------------
322 // splitter sash drawing
323 // ----------------------------------------------------------------------------
325 static int GetGtkSplitterFullSize(GtkWidget
* widget
)
328 gtk_widget_style_get(widget
, "handle_size", &handle_size
, NULL
);
333 wxSplitterRenderParams
334 wxRendererGTK::GetSplitterParams(const wxWindow
*WXUNUSED(win
))
336 // we don't draw any border, hence 0 for the second field
337 return wxSplitterRenderParams
339 GetGtkSplitterFullSize(wxGTKPrivate::GetSplitterWidget()),
341 true // hot sensitive
346 wxRendererGTK::DrawSplitterBorder(wxWindow
* WXUNUSED(win
),
348 const wxRect
& WXUNUSED(rect
),
355 wxRendererGTK::DrawSplitterSash(wxWindow
* win
,
359 wxOrientation orient
,
362 if (gtk_widget_get_window(win
->m_wxwindow
) == NULL
)
364 // window not realized yet
368 wxGTKDrawable
* drawable
= wxGetGTKDrawable(win
, dc
);
369 if (drawable
== NULL
)
372 // are we drawing vertical or horizontal splitter?
373 const bool isVert
= orient
== wxVERTICAL
;
375 GtkWidget
* widget
= wxGTKPrivate::GetSplitterWidget(orient
);
376 const int full_size
= GetGtkSplitterFullSize(widget
);
384 rect
.width
= full_size
;
385 rect
.height
= size
.y
;
391 rect
.height
= full_size
;
396 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
400 cairo_t
* cr
= wxGetGTKDrawable(win
, dc
);
403 gtk_widget_set_state_flags(widget
, stateTypeToFlags
[flags
& wxCONTROL_CURRENT
? GTK_STATE_PRELIGHT
: GTK_STATE_NORMAL
], true);
404 GtkStyleContext
* sc
= gtk_widget_get_style_context(widget
);
405 gtk_render_handle(sc
, cr
, rect
.x
- x_diff
, rect
.y
, rect
.width
, rect
.height
);
408 GdkWindow
* gdk_window
= wxGetGTKDrawable(win
, dc
);
409 if (gdk_window
== NULL
)
413 gtk_widget_get_style(win
->m_wxwindow
),
415 flags
& wxCONTROL_CURRENT
? GTK_STATE_PRELIGHT
: GTK_STATE_NORMAL
,
417 NULL
/* no clipping */,
420 dc
.LogicalToDeviceX(rect
.x
) - x_diff
,
421 dc
.LogicalToDeviceY(rect
.y
),
424 isVert
? GTK_ORIENTATION_VERTICAL
: GTK_ORIENTATION_HORIZONTAL
430 wxRendererGTK::DrawDropArrow(wxWindow
* win
,
435 GtkWidget
*button
= wxGTKPrivate::GetButtonWidget();
437 // If we give WX_PIZZA(win->m_wxwindow)->bin_window as
438 // a window for gtk_paint_xxx function, then it won't
439 // work for wxMemoryDC. So that is why we assume wxDC
440 // is wxWindowDC (wxClientDC, wxMemoryDC and wxPaintDC
441 // are derived from it) and use its m_window.
443 // draw arrow so that there is even space horizontally
445 const int size
= rect
.width
/ 2;
446 const int x
= rect
.x
+ (size
+ 1) / 2;
447 const int y
= rect
.y
+ (rect
.height
- size
+ 1) / 2;
451 if ( flags
& wxCONTROL_PRESSED
)
452 state
= GTK_STATE_ACTIVE
;
453 else if ( flags
& wxCONTROL_DISABLED
)
454 state
= GTK_STATE_INSENSITIVE
;
455 else if ( flags
& wxCONTROL_CURRENT
)
456 state
= GTK_STATE_PRELIGHT
;
458 state
= GTK_STATE_NORMAL
;
461 cairo_t
* cr
= wxGetGTKDrawable(win
, dc
);
464 gtk_widget_set_state_flags(button
, stateTypeToFlags
[state
], true);
465 GtkStyleContext
* sc
= gtk_widget_get_style_context(button
);
466 gtk_render_arrow(sc
, cr
, G_PI
, x
, y
, size
);
469 GdkWindow
* gdk_window
= wxGetGTKDrawable(win
, dc
);
470 if (gdk_window
== NULL
)
472 // draw arrow on button
475 gtk_widget_get_style(button
),
478 flags
& wxCONTROL_PRESSED
? GTK_SHADOW_IN
: GTK_SHADOW_OUT
,
491 wxRendererGTK::DrawComboBoxDropButton(wxWindow
*win
,
496 DrawPushButton(win
,dc
,rect
,flags
);
497 DrawDropArrow(win
,dc
,rect
);
501 wxRendererGTK::GetCheckBoxSize(wxWindow
*WXUNUSED(win
))
503 gint indicator_size
, indicator_spacing
;
504 gtk_widget_style_get(wxGTKPrivate::GetCheckButtonWidget(),
505 "indicator_size", &indicator_size
,
506 "indicator_spacing", &indicator_spacing
,
509 int size
= indicator_size
+ indicator_spacing
* 2;
510 return wxSize(size
, size
);
514 wxRendererGTK::DrawCheckBox(wxWindow
* win
,
519 GtkWidget
*button
= wxGTKPrivate::GetCheckButtonWidget();
521 gint indicator_size
, indicator_spacing
;
522 gtk_widget_style_get(button
,
523 "indicator_size", &indicator_size
,
524 "indicator_spacing", &indicator_spacing
,
530 if ( flags
& wxCONTROL_PRESSED
)
531 state
= GTK_STATE_ACTIVE
;
532 else if ( flags
& wxCONTROL_DISABLED
)
533 state
= GTK_STATE_INSENSITIVE
;
534 else if ( flags
& wxCONTROL_CURRENT
)
535 state
= GTK_STATE_PRELIGHT
;
537 state
= GTK_STATE_NORMAL
;
539 GtkShadowType shadow_type
;
541 if ( flags
& wxCONTROL_UNDETERMINED
)
542 shadow_type
= GTK_SHADOW_ETCHED_IN
;
543 else if ( flags
& wxCONTROL_CHECKED
)
544 shadow_type
= GTK_SHADOW_IN
;
546 shadow_type
= GTK_SHADOW_OUT
;
550 cairo_t
* cr
= wxGetGTKDrawable(win
, dc
);
553 int stateFlags
= GTK_STATE_FLAG_NORMAL
;
554 if (flags
& wxCONTROL_CHECKED
)
555 stateFlags
= GTK_STATE_FLAG_ACTIVE
;
556 if (flags
& wxCONTROL_DISABLED
)
557 stateFlags
|= GTK_STATE_FLAG_INSENSITIVE
;
558 if (flags
& wxCONTROL_UNDETERMINED
)
559 stateFlags
|= GTK_STATE_FLAG_INCONSISTENT
;
560 if (flags
& wxCONTROL_CURRENT
)
561 stateFlags
|= GTK_STATE_FLAG_PRELIGHT
;
562 GtkStyleContext
* sc
= gtk_widget_get_style_context(button
);
563 gtk_style_context_save(sc
);
564 gtk_style_context_set_state(sc
, GtkStateFlags(stateFlags
));
565 gtk_style_context_add_class(sc
, GTK_STYLE_CLASS_CHECK
);
566 gtk_render_check(sc
, cr
,
567 rect
.x
+ (rect
.width
- indicator_size
) / 2,
568 rect
.y
+ (rect
.height
- indicator_size
) / 2,
569 indicator_size
, indicator_size
);
570 gtk_style_context_restore(sc
);
573 GdkWindow
* gdk_window
= wxGetGTKDrawable(win
, dc
);
574 if (gdk_window
== NULL
)
579 gtk_widget_get_style(button
),
586 dc
.LogicalToDeviceX(rect
.x
) + indicator_spacing
,
587 dc
.LogicalToDeviceY(rect
.y
) + indicator_spacing
,
588 indicator_size
, indicator_size
594 wxRendererGTK::DrawPushButton(wxWindow
* win
,
599 GtkWidget
*button
= wxGTKPrivate::GetButtonWidget();
604 if ( flags
& wxCONTROL_PRESSED
)
605 state
= GTK_STATE_ACTIVE
;
606 else if ( flags
& wxCONTROL_DISABLED
)
607 state
= GTK_STATE_INSENSITIVE
;
608 else if ( flags
& wxCONTROL_CURRENT
)
609 state
= GTK_STATE_PRELIGHT
;
611 state
= GTK_STATE_NORMAL
;
614 cairo_t
* cr
= wxGetGTKDrawable(win
, dc
);
617 GtkStyleContext
* sc
= gtk_widget_get_style_context(button
);
618 gtk_style_context_save(sc
);
619 gtk_style_context_set_state(sc
, stateTypeToFlags
[state
]);
620 gtk_render_background(sc
, cr
, rect
.x
, rect
.y
, rect
.width
, rect
.height
);
621 gtk_render_frame(sc
, cr
, rect
.x
, rect
.y
, rect
.width
, rect
.height
);
622 gtk_style_context_restore(sc
);
625 GdkWindow
* gdk_window
= wxGetGTKDrawable(win
, dc
);
626 if (gdk_window
== NULL
)
631 gtk_widget_get_style(button
),
634 flags
& wxCONTROL_PRESSED
? GTK_SHADOW_IN
: GTK_SHADOW_OUT
,
638 dc
.LogicalToDeviceX(rect
.x
),
639 dc
.LogicalToDeviceY(rect
.y
),
647 wxRendererGTK::DrawItemSelectionRect(wxWindow
* win
,
652 wxGTKDrawable
* drawable
= wxGetGTKDrawable(win
, dc
);
654 if (drawable
&& (flags
& wxCONTROL_SELECTED
))
657 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
660 // the wxCONTROL_FOCUSED state is deduced
661 // directly from the m_wxwindow by GTK+
662 gtk_paint_flat_box(gtk_widget_get_style(wxGTKPrivate::GetTreeWidget()),
669 dc
.LogicalToDeviceX(rect
.x
) - x_diff
,
670 dc
.LogicalToDeviceY(rect
.y
),
675 if ((flags
& wxCONTROL_CURRENT
) && (flags
& wxCONTROL_FOCUSED
))
676 DrawFocusRect(win
, dc
, rect
, flags
);
679 void wxRendererGTK::DrawFocusRect(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
681 wxGTKDrawable
* drawable
= wxGetGTKDrawable(win
, dc
);
682 if (drawable
== NULL
)
686 if (flags
& wxCONTROL_SELECTED
)
687 state
= GTK_STATE_SELECTED
;
689 state
= GTK_STATE_NORMAL
;
692 GtkStyleContext
* sc
= gtk_widget_get_style_context(win
->m_widget
);
693 gtk_style_context_save(sc
);
694 gtk_style_context_set_state(sc
, stateTypeToFlags
[state
]);
695 gtk_render_focus(sc
, drawable
, rect
.x
, rect
.y
, rect
.width
, rect
.height
);
696 gtk_style_context_restore(sc
);
698 gtk_paint_focus( gtk_widget_get_style(win
->m_widget
),
704 dc
.LogicalToDeviceX(rect
.x
),
705 dc
.LogicalToDeviceY(rect
.y
),
711 //TODO: GTK3 implementations for the remaining functions below
713 // Uses the theme to draw the border and fill for something like a wxTextCtrl
714 void wxRendererGTK::DrawTextCtrl(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
716 GtkWidget
*entry
= wxGTKPrivate::GetTextEntryWidget();
718 wxGTKDrawable
* drawable
= wxGetGTKDrawable(win
, dc
);
719 if (drawable
== NULL
)
722 GtkStateType state
= GTK_STATE_NORMAL
;
723 if ( flags
& wxCONTROL_DISABLED
)
724 state
= GTK_STATE_INSENSITIVE
;
726 gtk_widget_set_can_focus(entry
, (flags
& wxCONTROL_CURRENT
) != 0);
730 gtk_widget_get_style(entry
),
737 dc
.LogicalToDeviceX(rect
.x
),
738 dc
.LogicalToDeviceY(rect
.y
),
744 // Draw the equivalent of a wxComboBox
745 void wxRendererGTK::DrawComboBox(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
747 GtkWidget
*combo
= wxGTKPrivate::GetComboBoxWidget();
749 wxGTKDrawable
* drawable
= wxGetGTKDrawable(win
, dc
);
751 GtkStateType state
= GTK_STATE_NORMAL
;
752 if ( flags
& wxCONTROL_DISABLED
)
753 state
= GTK_STATE_INSENSITIVE
;
755 gtk_widget_set_can_focus(combo
, (flags
& wxCONTROL_CURRENT
) != 0);
757 if (drawable
== NULL
)
762 gtk_widget_get_style(combo
),
769 dc
.LogicalToDeviceX(rect
.x
),
770 dc
.LogicalToDeviceY(rect
.y
),
776 int extent
= rect
.height
/ 2;
777 r
.x
+= rect
.width
- extent
- extent
/2;
784 gtk_widget_get_style(combo
),
793 dc
.LogicalToDeviceX(r
.x
),
794 dc
.LogicalToDeviceY(r
.y
),
800 r
.x
+= rect
.width
- 2*extent
;
805 gtk_widget_get_style(combo
),
808 GTK_SHADOW_ETCHED_OUT
,
812 dc
.LogicalToDeviceX(r
.x
),
813 dc
.LogicalToDeviceY(r
.y
+1),
820 void wxRendererGTK::DrawChoice(wxWindow
* win
, wxDC
& dc
,
821 const wxRect
& rect
, int flags
)
823 DrawComboBox( win
, dc
, rect
, flags
);
827 // Draw a themed radio button
828 void wxRendererGTK::DrawRadioBitmap(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
830 GtkWidget
*button
= wxGTKPrivate::GetRadioButtonWidget();
832 wxGTKDrawable
* drawable
= wxGetGTKDrawable(win
, dc
);
833 if (drawable
== NULL
)
836 GtkShadowType shadow_type
= GTK_SHADOW_OUT
;
837 if ( flags
& wxCONTROL_CHECKED
)
838 shadow_type
= GTK_SHADOW_IN
;
839 else if ( flags
& wxCONTROL_UNDETERMINED
)
840 shadow_type
= GTK_SHADOW_ETCHED_IN
;
842 GtkStateType state
= GTK_STATE_NORMAL
;
843 if ( flags
& wxCONTROL_DISABLED
)
844 state
= GTK_STATE_INSENSITIVE
;
845 if ( flags
& wxCONTROL_PRESSED
)
846 state
= GTK_STATE_ACTIVE
;
848 Don't know when to set this
849 state_type = GTK_STATE_PRELIGHT;
854 gtk_widget_get_style(button
),
861 dc
.LogicalToDeviceX(rect
.x
),
862 dc
.LogicalToDeviceY(rect
.y
),
863 rect
.width
, rect
.height