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"
39 #include "wx/gtk/private/win_gtk.h"
43 // ----------------------------------------------------------------------------
44 // wxRendererGTK: our wxRendererNative implementation
45 // ----------------------------------------------------------------------------
47 class WXDLLEXPORT wxRendererGTK
: public wxDelegateRendererNative
50 // draw the header control button (used by wxListCtrl)
51 virtual int DrawHeaderButton(wxWindow
*win
,
55 wxHeaderSortIconType sortArrow
= wxHDR_SORT_ICON_NONE
,
56 wxHeaderButtonParams
* params
= NULL
);
58 // draw the expanded/collapsed icon for a tree control item
59 virtual void DrawTreeItemButton(wxWindow
*win
,
64 virtual void DrawSplitterBorder(wxWindow
*win
,
68 virtual void DrawSplitterSash(wxWindow
*win
,
75 virtual void DrawComboBoxDropButton(wxWindow
*win
,
80 virtual void DrawDropArrow(wxWindow
*win
,
85 virtual void DrawCheckBox(wxWindow
*win
,
90 virtual void DrawPushButton(wxWindow
*win
,
95 virtual void DrawItemSelectionRect(wxWindow
*win
,
100 virtual void DrawChoice(wxWindow
* win
,
105 virtual void DrawComboBox(wxWindow
* win
,
110 virtual void DrawTextCtrl(wxWindow
* win
,
115 virtual void DrawRadioButton(wxWindow
* win
,
120 virtual void DrawFocusRect(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
= 0);
122 virtual wxSize
GetCheckBoxSize(wxWindow
*win
);
124 virtual wxSplitterRenderParams
GetSplitterParams(const wxWindow
*win
);
127 // ============================================================================
129 // ============================================================================
132 wxRendererNative
& wxRendererNative::GetDefault()
134 static wxRendererGTK s_rendererGTK
;
136 return s_rendererGTK
;
139 static GdkWindow
* wxGetGdkWindowForDC(wxWindow
* win
, wxDC
& dc
)
141 GdkWindow
* gdk_window
= NULL
;
143 #if wxUSE_GRAPHICS_CONTEXT
144 if ( dc
.IsKindOf( CLASSINFO(wxGCDC
) ) )
145 gdk_window
= WX_PIZZA(win
->m_wxwindow
)->m_backing_window
;
150 wxDCImpl
*impl
= dc
.GetImpl();
151 wxGTKDCImpl
*gtk_impl
= wxDynamicCast( impl
, wxGTKDCImpl
);
153 gdk_window
= gtk_impl
->GetGDKWindow();
155 gdk_window
= dc
.GetGDKWindow();
161 // ----------------------------------------------------------------------------
162 // list/tree controls drawing
163 // ----------------------------------------------------------------------------
166 wxRendererGTK::DrawHeaderButton(wxWindow
*win
,
170 wxHeaderSortIconType sortArrow
,
171 wxHeaderButtonParams
* params
)
174 GtkWidget
*button
= wxGTKPrivate::GetHeaderButtonWidget();
176 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
177 wxASSERT_MSG( gdk_window
,
178 wxT("cannot use wxRendererNative on wxDC of this type") );
181 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
184 GtkStateType state
= GTK_STATE_NORMAL
;
185 if (flags
& wxCONTROL_DISABLED
)
186 state
= GTK_STATE_INSENSITIVE
;
189 if (flags
& wxCONTROL_CURRENT
)
190 state
= GTK_STATE_PRELIGHT
;
202 dc
.LogicalToDeviceX(rect
.x
) - x_diff
, rect
.y
, rect
.width
, rect
.height
205 return DrawHeaderButtonContents(win
, dc
, rect
, flags
, sortArrow
, params
);
208 // draw a ">" or "v" button
210 wxRendererGTK::DrawTreeItemButton(wxWindow
* win
,
211 wxDC
& dc
, const wxRect
& rect
, int flags
)
213 GtkWidget
*tree
= wxGTKPrivate::GetTreeWidget();
215 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
216 wxASSERT_MSG( gdk_window
,
217 wxT("cannot use wxRendererNative on wxDC of this type") );
220 if ( flags
& wxCONTROL_CURRENT
)
221 state
= GTK_STATE_PRELIGHT
;
223 state
= GTK_STATE_NORMAL
;
226 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
229 // VZ: I don't know how to get the size of the expander so as to centre it
230 // in the given rectangle, +2/3 below is just what looks good here...
239 dc
.LogicalToDeviceX(rect
.x
) + 6 - x_diff
,
240 dc
.LogicalToDeviceY(rect
.y
) + 3,
241 flags
& wxCONTROL_EXPANDED
? GTK_EXPANDER_EXPANDED
242 : GTK_EXPANDER_COLLAPSED
247 // ----------------------------------------------------------------------------
248 // splitter sash drawing
249 // ----------------------------------------------------------------------------
251 static int GetGtkSplitterFullSize(GtkWidget
* widget
)
254 gtk_widget_style_get(widget
, "handle_size", &handle_size
, NULL
);
259 wxSplitterRenderParams
260 wxRendererGTK::GetSplitterParams(const wxWindow
*WXUNUSED(win
))
262 // we don't draw any border, hence 0 for the second field
263 return wxSplitterRenderParams
265 GetGtkSplitterFullSize(wxGTKPrivate::GetSplitterWidget()),
267 true // hot sensitive
272 wxRendererGTK::DrawSplitterBorder(wxWindow
* WXUNUSED(win
),
274 const wxRect
& WXUNUSED(rect
),
281 wxRendererGTK::DrawSplitterSash(wxWindow
* win
,
285 wxOrientation orient
,
288 if ( !win
->m_wxwindow
->window
)
290 // window not realized yet
294 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
295 wxASSERT_MSG( gdk_window
,
296 wxT("cannot use wxRendererNative on wxDC of this type") );
298 wxCoord full_size
= GetGtkSplitterFullSize(wxGTKPrivate::GetSplitterWidget());
300 // are we drawing vertical or horizontal splitter?
301 const bool isVert
= orient
== wxVERTICAL
;
309 rect
.width
= full_size
;
310 rect
.height
= size
.y
;
316 rect
.height
= full_size
;
321 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
326 win
->m_wxwindow
->style
,
328 flags
& wxCONTROL_CURRENT
? GTK_STATE_PRELIGHT
: GTK_STATE_NORMAL
,
330 NULL
/* no clipping */,
333 dc
.LogicalToDeviceX(rect
.x
) - x_diff
,
334 dc
.LogicalToDeviceY(rect
.y
),
337 isVert
? GTK_ORIENTATION_VERTICAL
: GTK_ORIENTATION_HORIZONTAL
342 wxRendererGTK::DrawDropArrow(wxWindow
* win
,
347 GtkWidget
*button
= wxGTKPrivate::GetButtonWidget();
349 // If we give WX_PIZZA(win->m_wxwindow)->bin_window as
350 // a window for gtk_paint_xxx function, then it won't
351 // work for wxMemoryDC. So that is why we assume wxDC
352 // is wxWindowDC (wxClientDC, wxMemoryDC and wxPaintDC
353 // are derived from it) and use its m_window.
354 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
355 wxASSERT_MSG( gdk_window
,
356 wxT("cannot use wxRendererNative on wxDC of this type") );
358 // draw arrow so that there is even space horizontally
360 int arrowX
= rect
.width
/4 + 1;
361 int arrowWidth
= rect
.width
- (arrowX
*2);
363 // scale arrow's height accoording to the width
364 int arrowHeight
= rect
.width
/3;
365 int arrowY
= (rect
.height
-arrowHeight
)/2 +
366 ((rect
.height
-arrowHeight
) & 1);
370 if ( flags
& wxCONTROL_PRESSED
)
371 state
= GTK_STATE_ACTIVE
;
372 else if ( flags
& wxCONTROL_DISABLED
)
373 state
= GTK_STATE_INSENSITIVE
;
374 else if ( flags
& wxCONTROL_CURRENT
)
375 state
= GTK_STATE_PRELIGHT
;
377 state
= GTK_STATE_NORMAL
;
379 // draw arrow on button
385 flags
& wxCONTROL_PRESSED
? GTK_SHADOW_IN
: GTK_SHADOW_OUT
,
399 wxRendererGTK::DrawComboBoxDropButton(wxWindow
*win
,
404 DrawPushButton(win
,dc
,rect
,flags
);
405 DrawDropArrow(win
,dc
,rect
);
409 wxRendererGTK::GetCheckBoxSize(wxWindow
*WXUNUSED(win
))
411 gint indicator_size
, indicator_spacing
;
412 gtk_widget_style_get(wxGTKPrivate::GetCheckButtonWidget(),
413 "indicator_size", &indicator_size
,
414 "indicator_spacing", &indicator_spacing
,
417 int size
= indicator_size
+ indicator_spacing
* 2;
418 return wxSize(size
, size
);
422 wxRendererGTK::DrawCheckBox(wxWindow
* win
,
427 GtkWidget
*button
= wxGTKPrivate::GetCheckButtonWidget();
429 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
430 wxASSERT_MSG( gdk_window
,
431 wxT("cannot use wxRendererNative on wxDC of this type") );
433 gint indicator_size
, indicator_spacing
;
434 gtk_widget_style_get(button
,
435 "indicator_size", &indicator_size
,
436 "indicator_spacing", &indicator_spacing
,
441 if ( flags
& wxCONTROL_PRESSED
)
442 state
= GTK_STATE_ACTIVE
;
443 else if ( flags
& wxCONTROL_DISABLED
)
444 state
= GTK_STATE_INSENSITIVE
;
445 else if ( flags
& wxCONTROL_CURRENT
)
446 state
= GTK_STATE_PRELIGHT
;
448 state
= GTK_STATE_NORMAL
;
455 flags
& wxCONTROL_CHECKED
? GTK_SHADOW_IN
: GTK_SHADOW_OUT
,
459 dc
.LogicalToDeviceX(rect
.x
) + indicator_spacing
,
460 dc
.LogicalToDeviceY(rect
.y
) + indicator_spacing
,
461 indicator_size
, indicator_size
466 wxRendererGTK::DrawPushButton(wxWindow
* win
,
471 GtkWidget
*button
= wxGTKPrivate::GetButtonWidget();
473 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
474 wxASSERT_MSG( gdk_window
,
475 wxT("cannot use wxRendererNative on wxDC of this type") );
480 if ( flags
& wxCONTROL_PRESSED
)
481 state
= GTK_STATE_ACTIVE
;
482 else if ( flags
& wxCONTROL_DISABLED
)
483 state
= GTK_STATE_INSENSITIVE
;
484 else if ( flags
& wxCONTROL_CURRENT
)
485 state
= GTK_STATE_PRELIGHT
;
487 state
= GTK_STATE_NORMAL
;
494 flags
& wxCONTROL_PRESSED
? GTK_SHADOW_IN
: GTK_SHADOW_OUT
,
498 dc
.LogicalToDeviceX(rect
.x
),
499 dc
.LogicalToDeviceY(rect
.y
),
506 wxRendererGTK::DrawItemSelectionRect(wxWindow
* win
,
511 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
512 wxASSERT_MSG( gdk_window
,
513 wxT("cannot use wxRendererNative on wxDC of this type") );
516 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
520 if (flags
& wxCONTROL_SELECTED
)
522 // the wxCONTROL_FOCUSED state is deduced
523 // directly from the m_wxwindow by GTK+
524 state
= GTK_STATE_SELECTED
;
526 gtk_paint_flat_box( win
->m_widget
->style
,
533 dc
.LogicalToDeviceX(rect
.x
) - x_diff
,
534 dc
.LogicalToDeviceY(rect
.y
),
538 else // !wxCONTROL_SELECTED
540 state
= GTK_STATE_NORMAL
;
543 if ((flags
& wxCONTROL_CURRENT
) && (flags
& wxCONTROL_FOCUSED
))
545 gtk_paint_focus( win
->m_widget
->style
,
550 // Detail "treeview" causes warning with GTK+ 2.12 Clearlooks theme:
551 // "... no property named `row-ending-details'"
552 // Using "treeview-middle" would fix the warning, but the right
553 // edge of the focus rect is not getting erased properly either.
554 // Better to not specify this detail unless the drawing is fixed.
556 dc
.LogicalToDeviceX(rect
.x
),
557 dc
.LogicalToDeviceY(rect
.y
),
563 void wxRendererGTK::DrawFocusRect(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
565 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
566 wxASSERT_MSG( gdk_window
,
567 wxT("cannot use wxRendererNative on wxDC of this type") );
570 if (flags
& wxCONTROL_SELECTED
)
571 state
= GTK_STATE_SELECTED
;
573 state
= GTK_STATE_NORMAL
;
575 gtk_paint_focus( win
->m_widget
->style
,
581 dc
.LogicalToDeviceX(rect
.x
),
582 dc
.LogicalToDeviceY(rect
.y
),
587 // Uses the theme to draw the border and fill for something like a wxTextCtrl
588 void wxRendererGTK::DrawTextCtrl(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
590 GtkWidget
*entry
= wxGTKPrivate::GetTextEntryWidget();
592 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
594 GtkStateType state
= GTK_STATE_NORMAL
;
595 if ( flags
& wxCONTROL_DISABLED
)
596 state
= GTK_STATE_INSENSITIVE
;
598 if (flags
& wxCONTROL_CURRENT
)
599 GTK_WIDGET_SET_FLAGS( entry
, GTK_HAS_FOCUS
);
601 GTK_WIDGET_UNSET_FLAGS( entry
, GTK_HAS_FOCUS
);
612 dc
.LogicalToDeviceX(rect
.x
),
613 dc
.LogicalToDeviceY(rect
.y
),
619 // Draw the equivallent of a wxComboBox
620 void wxRendererGTK::DrawComboBox(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
622 GtkWidget
*combo
= wxGTKPrivate::GetComboBoxWidget();
624 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
626 GtkStateType state
= GTK_STATE_NORMAL
;
627 if ( flags
& wxCONTROL_DISABLED
)
628 state
= GTK_STATE_INSENSITIVE
;
630 if (flags
& wxCONTROL_CURRENT
)
631 GTK_WIDGET_SET_FLAGS( combo
, GTK_HAS_FOCUS
);
633 GTK_WIDGET_UNSET_FLAGS( combo
, GTK_HAS_FOCUS
);
644 dc
.LogicalToDeviceX(rect
.x
),
645 dc
.LogicalToDeviceY(rect
.y
),
651 int extent
= rect
.height
/ 2;
652 r
.x
+= rect
.width
- extent
- extent
/2;
668 dc
.LogicalToDeviceX(r
.x
),
669 dc
.LogicalToDeviceY(r
.y
),
675 r
.x
+= rect
.width
- 2*extent
;
683 GTK_SHADOW_ETCHED_OUT
,
687 dc
.LogicalToDeviceX(r
.x
),
688 dc
.LogicalToDeviceY(r
.y
+1),
695 void wxRendererGTK::DrawChoice(wxWindow
* win
, wxDC
& dc
,
696 const wxRect
& rect
, int flags
)
698 DrawComboBox( win
, dc
, rect
, flags
);
702 // Draw a themed radio button
703 void wxRendererGTK::DrawRadioButton(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
705 GtkWidget
*button
= wxGTKPrivate::GetRadioButtonWidget();
707 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
709 GtkShadowType shadow_type
= GTK_SHADOW_OUT
;
710 if ( flags
& wxCONTROL_CHECKED
)
711 shadow_type
= GTK_SHADOW_IN
;
712 else if ( flags
& wxCONTROL_UNDETERMINED
)
713 shadow_type
= GTK_SHADOW_ETCHED_IN
;
715 GtkStateType state
= GTK_STATE_NORMAL
;
716 if ( flags
& wxCONTROL_DISABLED
)
717 state
= GTK_STATE_INSENSITIVE
;
718 if ( flags
& wxCONTROL_PRESSED
)
719 state
= GTK_STATE_ACTIVE
;
721 Don't know when to set this
722 state_type = GTK_STATE_PRELIGHT;
734 dc
.LogicalToDeviceX(rect
.x
),
735 dc
.LogicalToDeviceY(rect
.y
),
736 rect
.width
, rect
.height