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 // License: 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"
37 #include "wx/gtk/dc.h"
38 #include "wx/gtk/private.h"
42 // ----------------------------------------------------------------------------
43 // wxRendererGTK: our wxRendererNative implementation
44 // ----------------------------------------------------------------------------
46 class WXDLLEXPORT wxRendererGTK
: public wxDelegateRendererNative
49 // draw the header control button (used by wxListCtrl)
50 virtual int DrawHeaderButton(wxWindow
*win
,
54 wxHeaderSortIconType sortArrow
= wxHDR_SORT_ICON_NONE
,
55 wxHeaderButtonParams
* params
= NULL
);
57 virtual int GetHeaderButtonHeight(wxWindow
*win
);
60 // draw the expanded/collapsed icon for a tree control item
61 virtual void DrawTreeItemButton(wxWindow
*win
,
66 virtual void DrawSplitterBorder(wxWindow
*win
,
70 virtual void DrawSplitterSash(wxWindow
*win
,
77 virtual void DrawComboBoxDropButton(wxWindow
*win
,
82 virtual void DrawDropArrow(wxWindow
*win
,
87 virtual void DrawCheckBox(wxWindow
*win
,
92 virtual void DrawPushButton(wxWindow
*win
,
97 virtual void DrawItemSelectionRect(wxWindow
*win
,
102 virtual void DrawChoice(wxWindow
* win
,
107 virtual void DrawComboBox(wxWindow
* win
,
112 virtual void DrawTextCtrl(wxWindow
* win
,
117 virtual void DrawRadioButton(wxWindow
* win
,
122 virtual void DrawFocusRect(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
= 0);
124 virtual wxSize
GetCheckBoxSize(wxWindow
*win
);
126 virtual wxSplitterRenderParams
GetSplitterParams(const wxWindow
*win
);
129 // ============================================================================
131 // ============================================================================
134 wxRendererNative
& wxRendererNative::GetDefault()
136 static wxRendererGTK s_rendererGTK
;
138 return s_rendererGTK
;
141 static GdkWindow
* wxGetGdkWindowForDC(wxWindow
* win
, wxDC
& dc
)
143 GdkWindow
* gdk_window
= NULL
;
145 #if wxUSE_GRAPHICS_CONTEXT
146 if ( dc
.IsKindOf( CLASSINFO(wxGCDC
) ) )
147 gdk_window
= win
->GTKGetDrawingWindow();
152 wxDCImpl
*impl
= dc
.GetImpl();
153 wxGTKDCImpl
*gtk_impl
= wxDynamicCast( impl
, wxGTKDCImpl
);
155 gdk_window
= gtk_impl
->GetGDKWindow();
157 gdk_window
= dc
.GetGDKWindow();
163 // ----------------------------------------------------------------------------
164 // list/tree controls drawing
165 // ----------------------------------------------------------------------------
168 wxRendererGTK::DrawHeaderButton(wxWindow
*win
,
172 wxHeaderSortIconType sortArrow
,
173 wxHeaderButtonParams
* params
)
176 GtkWidget
*button
= wxGTKPrivate::GetHeaderButtonWidget();
177 if (flags
& wxCONTROL_SPECIAL
)
178 button
= wxGTKPrivate::GetHeaderButtonWidgetFirst();
179 if (flags
& wxCONTROL_DIRTY
)
180 button
= wxGTKPrivate::GetHeaderButtonWidgetLast();
182 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
183 wxASSERT_MSG( gdk_window
,
184 wxT("cannot use wxRendererNative on wxDC of this type") );
187 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
190 GtkStateType state
= GTK_STATE_NORMAL
;
191 if (flags
& wxCONTROL_DISABLED
)
192 state
= GTK_STATE_INSENSITIVE
;
195 if (flags
& wxCONTROL_CURRENT
)
196 state
= GTK_STATE_PRELIGHT
;
208 dc
.LogicalToDeviceX(rect
.x
) - x_diff
, rect
.y
, rect
.width
, rect
.height
211 return DrawHeaderButtonContents(win
, dc
, rect
, flags
, sortArrow
, params
);
214 int wxRendererGTK::GetHeaderButtonHeight(wxWindow
*WXUNUSED(win
))
216 GtkWidget
*button
= wxGTKPrivate::GetHeaderButtonWidget();
219 GTK_WIDGET_GET_CLASS(button
)->size_request(button
, &req
);
225 // draw a ">" or "v" button
227 wxRendererGTK::DrawTreeItemButton(wxWindow
* win
,
228 wxDC
& dc
, const wxRect
& rect
, int flags
)
230 GtkWidget
*tree
= wxGTKPrivate::GetTreeWidget();
232 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
233 wxASSERT_MSG( gdk_window
,
234 wxT("cannot use wxRendererNative on wxDC of this type") );
237 if ( flags
& wxCONTROL_CURRENT
)
238 state
= GTK_STATE_PRELIGHT
;
240 state
= GTK_STATE_NORMAL
;
243 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
246 // VZ: I don't know how to get the size of the expander so as to centre it
247 // in the given rectangle, +2/3 below is just what looks good here...
256 dc
.LogicalToDeviceX(rect
.x
) + 6 - x_diff
,
257 dc
.LogicalToDeviceY(rect
.y
) + 3,
258 flags
& wxCONTROL_EXPANDED
? GTK_EXPANDER_EXPANDED
259 : GTK_EXPANDER_COLLAPSED
264 // ----------------------------------------------------------------------------
265 // splitter sash drawing
266 // ----------------------------------------------------------------------------
268 static int GetGtkSplitterFullSize(GtkWidget
* widget
)
271 gtk_widget_style_get(widget
, "handle_size", &handle_size
, NULL
);
276 wxSplitterRenderParams
277 wxRendererGTK::GetSplitterParams(const wxWindow
*WXUNUSED(win
))
279 // we don't draw any border, hence 0 for the second field
280 return wxSplitterRenderParams
282 GetGtkSplitterFullSize(wxGTKPrivate::GetSplitterWidget()),
284 true // hot sensitive
289 wxRendererGTK::DrawSplitterBorder(wxWindow
* WXUNUSED(win
),
291 const wxRect
& WXUNUSED(rect
),
298 wxRendererGTK::DrawSplitterSash(wxWindow
* win
,
302 wxOrientation orient
,
305 if ( !win
->m_wxwindow
->window
)
307 // window not realized yet
311 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
312 wxASSERT_MSG( gdk_window
,
313 wxT("cannot use wxRendererNative on wxDC of this type") );
315 wxCoord full_size
= GetGtkSplitterFullSize(wxGTKPrivate::GetSplitterWidget());
317 // are we drawing vertical or horizontal splitter?
318 const bool isVert
= orient
== wxVERTICAL
;
326 rect
.width
= full_size
;
327 rect
.height
= size
.y
;
333 rect
.height
= full_size
;
338 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
343 win
->m_wxwindow
->style
,
345 flags
& wxCONTROL_CURRENT
? GTK_STATE_PRELIGHT
: GTK_STATE_NORMAL
,
347 NULL
/* no clipping */,
350 dc
.LogicalToDeviceX(rect
.x
) - x_diff
,
351 dc
.LogicalToDeviceY(rect
.y
),
354 isVert
? GTK_ORIENTATION_VERTICAL
: GTK_ORIENTATION_HORIZONTAL
359 wxRendererGTK::DrawDropArrow(wxWindow
* win
,
364 GtkWidget
*button
= wxGTKPrivate::GetButtonWidget();
366 // If we give WX_PIZZA(win->m_wxwindow)->bin_window as
367 // a window for gtk_paint_xxx function, then it won't
368 // work for wxMemoryDC. So that is why we assume wxDC
369 // is wxWindowDC (wxClientDC, wxMemoryDC and wxPaintDC
370 // are derived from it) and use its m_window.
371 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
372 wxASSERT_MSG( gdk_window
,
373 wxT("cannot use wxRendererNative on wxDC of this type") );
375 // draw arrow so that there is even space horizontally
377 int arrowX
= rect
.width
/4 + 1;
378 int arrowWidth
= rect
.width
- (arrowX
*2);
380 // scale arrow's height accoording to the width
381 int arrowHeight
= rect
.width
/3;
382 int arrowY
= (rect
.height
-arrowHeight
)/2 +
383 ((rect
.height
-arrowHeight
) & 1);
387 if ( flags
& wxCONTROL_PRESSED
)
388 state
= GTK_STATE_ACTIVE
;
389 else if ( flags
& wxCONTROL_DISABLED
)
390 state
= GTK_STATE_INSENSITIVE
;
391 else if ( flags
& wxCONTROL_CURRENT
)
392 state
= GTK_STATE_PRELIGHT
;
394 state
= GTK_STATE_NORMAL
;
396 // draw arrow on button
402 flags
& wxCONTROL_PRESSED
? GTK_SHADOW_IN
: GTK_SHADOW_OUT
,
416 wxRendererGTK::DrawComboBoxDropButton(wxWindow
*win
,
421 DrawPushButton(win
,dc
,rect
,flags
);
422 DrawDropArrow(win
,dc
,rect
);
426 wxRendererGTK::GetCheckBoxSize(wxWindow
*WXUNUSED(win
))
428 gint indicator_size
, indicator_spacing
;
429 gtk_widget_style_get(wxGTKPrivate::GetCheckButtonWidget(),
430 "indicator_size", &indicator_size
,
431 "indicator_spacing", &indicator_spacing
,
434 int size
= indicator_size
+ indicator_spacing
* 2;
435 return wxSize(size
, size
);
439 wxRendererGTK::DrawCheckBox(wxWindow
* win
,
444 GtkWidget
*button
= wxGTKPrivate::GetCheckButtonWidget();
446 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
447 wxASSERT_MSG( gdk_window
,
448 wxT("cannot use wxRendererNative on wxDC of this type") );
450 gint indicator_size
, indicator_spacing
;
451 gtk_widget_style_get(button
,
452 "indicator_size", &indicator_size
,
453 "indicator_spacing", &indicator_spacing
,
458 if ( flags
& wxCONTROL_PRESSED
)
459 state
= GTK_STATE_ACTIVE
;
460 else if ( flags
& wxCONTROL_DISABLED
)
461 state
= GTK_STATE_INSENSITIVE
;
462 else if ( flags
& wxCONTROL_CURRENT
)
463 state
= GTK_STATE_PRELIGHT
;
465 state
= GTK_STATE_NORMAL
;
472 flags
& wxCONTROL_CHECKED
? GTK_SHADOW_IN
: GTK_SHADOW_OUT
,
476 dc
.LogicalToDeviceX(rect
.x
) + indicator_spacing
,
477 dc
.LogicalToDeviceY(rect
.y
) + indicator_spacing
,
478 indicator_size
, indicator_size
483 wxRendererGTK::DrawPushButton(wxWindow
* win
,
488 GtkWidget
*button
= wxGTKPrivate::GetButtonWidget();
490 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
491 wxASSERT_MSG( gdk_window
,
492 wxT("cannot use wxRendererNative on wxDC of this type") );
497 if ( flags
& wxCONTROL_PRESSED
)
498 state
= GTK_STATE_ACTIVE
;
499 else if ( flags
& wxCONTROL_DISABLED
)
500 state
= GTK_STATE_INSENSITIVE
;
501 else if ( flags
& wxCONTROL_CURRENT
)
502 state
= GTK_STATE_PRELIGHT
;
504 state
= GTK_STATE_NORMAL
;
511 flags
& wxCONTROL_PRESSED
? GTK_SHADOW_IN
: GTK_SHADOW_OUT
,
515 dc
.LogicalToDeviceX(rect
.x
),
516 dc
.LogicalToDeviceY(rect
.y
),
523 wxRendererGTK::DrawItemSelectionRect(wxWindow
* win
,
528 GtkWidget
*tree
= wxGTKPrivate::GetTreeWidget();
530 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
531 wxASSERT_MSG( gdk_window
,
532 wxT("cannot use wxRendererNative on wxDC of this type") );
535 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
538 GtkStateType state
= GTK_STATE_NORMAL
;
540 if (flags
& wxCONTROL_SELECTED
)
542 // the wxCONTROL_FOCUSED state is deduced
543 // directly from the m_wxwindow by GTK+
544 state
= GTK_STATE_SELECTED
;
546 gtk_paint_flat_box( tree
->style
, // win->m_widget->style,
553 dc
.LogicalToDeviceX(rect
.x
) - x_diff
,
554 dc
.LogicalToDeviceY(rect
.y
),
558 else // !wxCONTROL_SELECTED
560 state
= GTK_STATE_NORMAL
;
563 if ((flags
& wxCONTROL_CURRENT
) && (flags
& wxCONTROL_FOCUSED
))
565 if (flags
& wxCONTROL_SELECTED
)
566 state
= GTK_STATE_SELECTED
;
568 gtk_paint_focus( tree
->style
,
573 // Detail "treeview" causes warning with GTK+ 2.12 Clearlooks theme:
574 // "... no property named `row-ending-details'"
575 // Using "treeview-middle" would fix the warning, but the right
576 // edge of the focus rect is not getting erased properly either.
577 // Better to not specify this detail unless the drawing is fixed.
579 dc
.LogicalToDeviceX(rect
.x
),
580 dc
.LogicalToDeviceY(rect
.y
),
586 void wxRendererGTK::DrawFocusRect(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
588 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
589 wxASSERT_MSG( gdk_window
,
590 wxT("cannot use wxRendererNative on wxDC of this type") );
593 if (flags
& wxCONTROL_SELECTED
)
594 state
= GTK_STATE_SELECTED
;
596 state
= GTK_STATE_NORMAL
;
598 gtk_paint_focus( win
->m_widget
->style
,
604 dc
.LogicalToDeviceX(rect
.x
),
605 dc
.LogicalToDeviceY(rect
.y
),
610 // Uses the theme to draw the border and fill for something like a wxTextCtrl
611 void wxRendererGTK::DrawTextCtrl(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
613 GtkWidget
*entry
= wxGTKPrivate::GetTextEntryWidget();
615 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
617 GtkStateType state
= GTK_STATE_NORMAL
;
618 if ( flags
& wxCONTROL_DISABLED
)
619 state
= GTK_STATE_INSENSITIVE
;
621 if (flags
& wxCONTROL_CURRENT
)
622 GTK_WIDGET_SET_FLAGS( entry
, GTK_HAS_FOCUS
);
624 GTK_WIDGET_UNSET_FLAGS( entry
, GTK_HAS_FOCUS
);
635 dc
.LogicalToDeviceX(rect
.x
),
636 dc
.LogicalToDeviceY(rect
.y
),
642 // Draw the equivallent of a wxComboBox
643 void wxRendererGTK::DrawComboBox(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
645 GtkWidget
*combo
= wxGTKPrivate::GetComboBoxWidget();
647 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
649 GtkStateType state
= GTK_STATE_NORMAL
;
650 if ( flags
& wxCONTROL_DISABLED
)
651 state
= GTK_STATE_INSENSITIVE
;
653 if (flags
& wxCONTROL_CURRENT
)
654 GTK_WIDGET_SET_FLAGS( combo
, GTK_HAS_FOCUS
);
656 GTK_WIDGET_UNSET_FLAGS( combo
, GTK_HAS_FOCUS
);
667 dc
.LogicalToDeviceX(rect
.x
),
668 dc
.LogicalToDeviceY(rect
.y
),
674 int extent
= rect
.height
/ 2;
675 r
.x
+= rect
.width
- extent
- extent
/2;
691 dc
.LogicalToDeviceX(r
.x
),
692 dc
.LogicalToDeviceY(r
.y
),
698 r
.x
+= rect
.width
- 2*extent
;
706 GTK_SHADOW_ETCHED_OUT
,
710 dc
.LogicalToDeviceX(r
.x
),
711 dc
.LogicalToDeviceY(r
.y
+1),
718 void wxRendererGTK::DrawChoice(wxWindow
* win
, wxDC
& dc
,
719 const wxRect
& rect
, int flags
)
721 DrawComboBox( win
, dc
, rect
, flags
);
725 // Draw a themed radio button
726 void wxRendererGTK::DrawRadioButton(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
728 GtkWidget
*button
= wxGTKPrivate::GetRadioButtonWidget();
730 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
732 GtkShadowType shadow_type
= GTK_SHADOW_OUT
;
733 if ( flags
& wxCONTROL_CHECKED
)
734 shadow_type
= GTK_SHADOW_IN
;
735 else if ( flags
& wxCONTROL_UNDETERMINED
)
736 shadow_type
= GTK_SHADOW_ETCHED_IN
;
738 GtkStateType state
= GTK_STATE_NORMAL
;
739 if ( flags
& wxCONTROL_DISABLED
)
740 state
= GTK_STATE_INSENSITIVE
;
741 if ( flags
& wxCONTROL_PRESSED
)
742 state
= GTK_STATE_ACTIVE
;
744 Don't know when to set this
745 state_type = GTK_STATE_PRELIGHT;
757 dc
.LogicalToDeviceX(rect
.x
),
758 dc
.LogicalToDeviceY(rect
.y
),
759 rect
.width
, rect
.height