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 // draw the expanded/collapsed icon for a tree control item
58 virtual void DrawTreeItemButton(wxWindow
*win
,
63 virtual void DrawSplitterBorder(wxWindow
*win
,
67 virtual void DrawSplitterSash(wxWindow
*win
,
74 virtual void DrawComboBoxDropButton(wxWindow
*win
,
79 virtual void DrawDropArrow(wxWindow
*win
,
84 virtual void DrawCheckBox(wxWindow
*win
,
89 virtual void DrawPushButton(wxWindow
*win
,
94 virtual void DrawItemSelectionRect(wxWindow
*win
,
99 virtual void DrawChoice(wxWindow
* win
,
104 virtual void DrawComboBox(wxWindow
* win
,
109 virtual void DrawTextCtrl(wxWindow
* win
,
114 virtual void DrawRadioButton(wxWindow
* win
,
119 virtual void DrawFocusRect(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
= 0);
121 virtual wxSize
GetCheckBoxSize(wxWindow
*win
);
123 virtual wxSplitterRenderParams
GetSplitterParams(const wxWindow
*win
);
126 // ============================================================================
128 // ============================================================================
131 wxRendererNative
& wxRendererNative::GetDefault()
133 static wxRendererGTK s_rendererGTK
;
135 return s_rendererGTK
;
138 static GdkWindow
* wxGetGdkWindowForDC(wxWindow
* win
, wxDC
& dc
)
140 GdkWindow
* gdk_window
= NULL
;
142 #if wxUSE_GRAPHICS_CONTEXT
143 if ( dc
.IsKindOf( CLASSINFO(wxGCDC
) ) )
144 gdk_window
= win
->GTKGetDrawingWindow();
149 wxDCImpl
*impl
= dc
.GetImpl();
150 wxGTKDCImpl
*gtk_impl
= wxDynamicCast( impl
, wxGTKDCImpl
);
152 gdk_window
= gtk_impl
->GetGDKWindow();
154 gdk_window
= dc
.GetGDKWindow();
160 // ----------------------------------------------------------------------------
161 // list/tree controls drawing
162 // ----------------------------------------------------------------------------
165 wxRendererGTK::DrawHeaderButton(wxWindow
*win
,
169 wxHeaderSortIconType sortArrow
,
170 wxHeaderButtonParams
* params
)
173 GtkWidget
*button
= wxGTKPrivate::GetHeaderButtonWidget();
174 if (flags
& wxCONTROL_SPECIAL
)
175 button
= wxGTKPrivate::GetHeaderButtonWidgetFirst();
176 if (flags
& wxCONTROL_EXPANDED
)
177 button
= wxGTKPrivate::GetHeaderButtonWidgetLast();
179 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
180 wxASSERT_MSG( gdk_window
,
181 wxT("cannot use wxRendererNative on wxDC of this type") );
184 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
187 GtkStateType state
= GTK_STATE_NORMAL
;
188 if (flags
& wxCONTROL_DISABLED
)
189 state
= GTK_STATE_INSENSITIVE
;
192 if (flags
& wxCONTROL_CURRENT
)
193 state
= GTK_STATE_PRELIGHT
;
205 dc
.LogicalToDeviceX(rect
.x
) - x_diff
, rect
.y
, rect
.width
, rect
.height
208 return DrawHeaderButtonContents(win
, dc
, rect
, flags
, sortArrow
, params
);
211 // draw a ">" or "v" button
213 wxRendererGTK::DrawTreeItemButton(wxWindow
* win
,
214 wxDC
& dc
, const wxRect
& rect
, int flags
)
216 GtkWidget
*tree
= wxGTKPrivate::GetTreeWidget();
218 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
219 wxASSERT_MSG( gdk_window
,
220 wxT("cannot use wxRendererNative on wxDC of this type") );
223 if ( flags
& wxCONTROL_CURRENT
)
224 state
= GTK_STATE_PRELIGHT
;
226 state
= GTK_STATE_NORMAL
;
229 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
232 // VZ: I don't know how to get the size of the expander so as to centre it
233 // in the given rectangle, +2/3 below is just what looks good here...
242 dc
.LogicalToDeviceX(rect
.x
) + 6 - x_diff
,
243 dc
.LogicalToDeviceY(rect
.y
) + 3,
244 flags
& wxCONTROL_EXPANDED
? GTK_EXPANDER_EXPANDED
245 : GTK_EXPANDER_COLLAPSED
250 // ----------------------------------------------------------------------------
251 // splitter sash drawing
252 // ----------------------------------------------------------------------------
254 static int GetGtkSplitterFullSize(GtkWidget
* widget
)
257 gtk_widget_style_get(widget
, "handle_size", &handle_size
, NULL
);
262 wxSplitterRenderParams
263 wxRendererGTK::GetSplitterParams(const wxWindow
*WXUNUSED(win
))
265 // we don't draw any border, hence 0 for the second field
266 return wxSplitterRenderParams
268 GetGtkSplitterFullSize(wxGTKPrivate::GetSplitterWidget()),
270 true // hot sensitive
275 wxRendererGTK::DrawSplitterBorder(wxWindow
* WXUNUSED(win
),
277 const wxRect
& WXUNUSED(rect
),
284 wxRendererGTK::DrawSplitterSash(wxWindow
* win
,
288 wxOrientation orient
,
291 if ( !win
->m_wxwindow
->window
)
293 // window not realized yet
297 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
298 wxASSERT_MSG( gdk_window
,
299 wxT("cannot use wxRendererNative on wxDC of this type") );
301 wxCoord full_size
= GetGtkSplitterFullSize(wxGTKPrivate::GetSplitterWidget());
303 // are we drawing vertical or horizontal splitter?
304 const bool isVert
= orient
== wxVERTICAL
;
312 rect
.width
= full_size
;
313 rect
.height
= size
.y
;
319 rect
.height
= full_size
;
324 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
329 win
->m_wxwindow
->style
,
331 flags
& wxCONTROL_CURRENT
? GTK_STATE_PRELIGHT
: GTK_STATE_NORMAL
,
333 NULL
/* no clipping */,
336 dc
.LogicalToDeviceX(rect
.x
) - x_diff
,
337 dc
.LogicalToDeviceY(rect
.y
),
340 isVert
? GTK_ORIENTATION_VERTICAL
: GTK_ORIENTATION_HORIZONTAL
345 wxRendererGTK::DrawDropArrow(wxWindow
* win
,
350 GtkWidget
*button
= wxGTKPrivate::GetButtonWidget();
352 // If we give WX_PIZZA(win->m_wxwindow)->bin_window as
353 // a window for gtk_paint_xxx function, then it won't
354 // work for wxMemoryDC. So that is why we assume wxDC
355 // is wxWindowDC (wxClientDC, wxMemoryDC and wxPaintDC
356 // are derived from it) and use its m_window.
357 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
358 wxASSERT_MSG( gdk_window
,
359 wxT("cannot use wxRendererNative on wxDC of this type") );
361 // draw arrow so that there is even space horizontally
363 int arrowX
= rect
.width
/4 + 1;
364 int arrowWidth
= rect
.width
- (arrowX
*2);
366 // scale arrow's height accoording to the width
367 int arrowHeight
= rect
.width
/3;
368 int arrowY
= (rect
.height
-arrowHeight
)/2 +
369 ((rect
.height
-arrowHeight
) & 1);
373 if ( flags
& wxCONTROL_PRESSED
)
374 state
= GTK_STATE_ACTIVE
;
375 else if ( flags
& wxCONTROL_DISABLED
)
376 state
= GTK_STATE_INSENSITIVE
;
377 else if ( flags
& wxCONTROL_CURRENT
)
378 state
= GTK_STATE_PRELIGHT
;
380 state
= GTK_STATE_NORMAL
;
382 // draw arrow on button
388 flags
& wxCONTROL_PRESSED
? GTK_SHADOW_IN
: GTK_SHADOW_OUT
,
402 wxRendererGTK::DrawComboBoxDropButton(wxWindow
*win
,
407 DrawPushButton(win
,dc
,rect
,flags
);
408 DrawDropArrow(win
,dc
,rect
);
412 wxRendererGTK::GetCheckBoxSize(wxWindow
*WXUNUSED(win
))
414 gint indicator_size
, indicator_spacing
;
415 gtk_widget_style_get(wxGTKPrivate::GetCheckButtonWidget(),
416 "indicator_size", &indicator_size
,
417 "indicator_spacing", &indicator_spacing
,
420 int size
= indicator_size
+ indicator_spacing
* 2;
421 return wxSize(size
, size
);
425 wxRendererGTK::DrawCheckBox(wxWindow
* win
,
430 GtkWidget
*button
= wxGTKPrivate::GetCheckButtonWidget();
432 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
433 wxASSERT_MSG( gdk_window
,
434 wxT("cannot use wxRendererNative on wxDC of this type") );
436 gint indicator_size
, indicator_spacing
;
437 gtk_widget_style_get(button
,
438 "indicator_size", &indicator_size
,
439 "indicator_spacing", &indicator_spacing
,
444 if ( flags
& wxCONTROL_PRESSED
)
445 state
= GTK_STATE_ACTIVE
;
446 else if ( flags
& wxCONTROL_DISABLED
)
447 state
= GTK_STATE_INSENSITIVE
;
448 else if ( flags
& wxCONTROL_CURRENT
)
449 state
= GTK_STATE_PRELIGHT
;
451 state
= GTK_STATE_NORMAL
;
458 flags
& wxCONTROL_CHECKED
? GTK_SHADOW_IN
: GTK_SHADOW_OUT
,
462 dc
.LogicalToDeviceX(rect
.x
) + indicator_spacing
,
463 dc
.LogicalToDeviceY(rect
.y
) + indicator_spacing
,
464 indicator_size
, indicator_size
469 wxRendererGTK::DrawPushButton(wxWindow
* win
,
474 GtkWidget
*button
= wxGTKPrivate::GetButtonWidget();
476 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
477 wxASSERT_MSG( gdk_window
,
478 wxT("cannot use wxRendererNative on wxDC of this type") );
483 if ( flags
& wxCONTROL_PRESSED
)
484 state
= GTK_STATE_ACTIVE
;
485 else if ( flags
& wxCONTROL_DISABLED
)
486 state
= GTK_STATE_INSENSITIVE
;
487 else if ( flags
& wxCONTROL_CURRENT
)
488 state
= GTK_STATE_PRELIGHT
;
490 state
= GTK_STATE_NORMAL
;
497 flags
& wxCONTROL_PRESSED
? GTK_SHADOW_IN
: GTK_SHADOW_OUT
,
501 dc
.LogicalToDeviceX(rect
.x
),
502 dc
.LogicalToDeviceY(rect
.y
),
509 wxRendererGTK::DrawItemSelectionRect(wxWindow
* win
,
514 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
515 wxASSERT_MSG( gdk_window
,
516 wxT("cannot use wxRendererNative on wxDC of this type") );
519 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
523 if (flags
& wxCONTROL_SELECTED
)
525 // the wxCONTROL_FOCUSED state is deduced
526 // directly from the m_wxwindow by GTK+
527 state
= GTK_STATE_SELECTED
;
529 gtk_paint_flat_box( win
->m_widget
->style
,
536 dc
.LogicalToDeviceX(rect
.x
) - x_diff
,
537 dc
.LogicalToDeviceY(rect
.y
),
541 else // !wxCONTROL_SELECTED
543 state
= GTK_STATE_NORMAL
;
546 if ((flags
& wxCONTROL_CURRENT
) && (flags
& wxCONTROL_FOCUSED
))
548 gtk_paint_focus( win
->m_widget
->style
,
553 // Detail "treeview" causes warning with GTK+ 2.12 Clearlooks theme:
554 // "... no property named `row-ending-details'"
555 // Using "treeview-middle" would fix the warning, but the right
556 // edge of the focus rect is not getting erased properly either.
557 // Better to not specify this detail unless the drawing is fixed.
559 dc
.LogicalToDeviceX(rect
.x
),
560 dc
.LogicalToDeviceY(rect
.y
),
566 void wxRendererGTK::DrawFocusRect(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
568 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
569 wxASSERT_MSG( gdk_window
,
570 wxT("cannot use wxRendererNative on wxDC of this type") );
573 if (flags
& wxCONTROL_SELECTED
)
574 state
= GTK_STATE_SELECTED
;
576 state
= GTK_STATE_NORMAL
;
578 gtk_paint_focus( win
->m_widget
->style
,
584 dc
.LogicalToDeviceX(rect
.x
),
585 dc
.LogicalToDeviceY(rect
.y
),
590 // Uses the theme to draw the border and fill for something like a wxTextCtrl
591 void wxRendererGTK::DrawTextCtrl(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
593 GtkWidget
*entry
= wxGTKPrivate::GetTextEntryWidget();
595 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
597 GtkStateType state
= GTK_STATE_NORMAL
;
598 if ( flags
& wxCONTROL_DISABLED
)
599 state
= GTK_STATE_INSENSITIVE
;
601 if (flags
& wxCONTROL_CURRENT
)
602 GTK_WIDGET_SET_FLAGS( entry
, GTK_HAS_FOCUS
);
604 GTK_WIDGET_UNSET_FLAGS( entry
, GTK_HAS_FOCUS
);
615 dc
.LogicalToDeviceX(rect
.x
),
616 dc
.LogicalToDeviceY(rect
.y
),
622 // Draw the equivallent of a wxComboBox
623 void wxRendererGTK::DrawComboBox(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
625 GtkWidget
*combo
= wxGTKPrivate::GetComboBoxWidget();
627 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
629 GtkStateType state
= GTK_STATE_NORMAL
;
630 if ( flags
& wxCONTROL_DISABLED
)
631 state
= GTK_STATE_INSENSITIVE
;
633 if (flags
& wxCONTROL_CURRENT
)
634 GTK_WIDGET_SET_FLAGS( combo
, GTK_HAS_FOCUS
);
636 GTK_WIDGET_UNSET_FLAGS( combo
, GTK_HAS_FOCUS
);
647 dc
.LogicalToDeviceX(rect
.x
),
648 dc
.LogicalToDeviceY(rect
.y
),
654 int extent
= rect
.height
/ 2;
655 r
.x
+= rect
.width
- extent
- extent
/2;
671 dc
.LogicalToDeviceX(r
.x
),
672 dc
.LogicalToDeviceY(r
.y
),
678 r
.x
+= rect
.width
- 2*extent
;
686 GTK_SHADOW_ETCHED_OUT
,
690 dc
.LogicalToDeviceX(r
.x
),
691 dc
.LogicalToDeviceY(r
.y
+1),
698 void wxRendererGTK::DrawChoice(wxWindow
* win
, wxDC
& dc
,
699 const wxRect
& rect
, int flags
)
701 DrawComboBox( win
, dc
, rect
, flags
);
705 // Draw a themed radio button
706 void wxRendererGTK::DrawRadioButton(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
708 GtkWidget
*button
= wxGTKPrivate::GetRadioButtonWidget();
710 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
712 GtkShadowType shadow_type
= GTK_SHADOW_OUT
;
713 if ( flags
& wxCONTROL_CHECKED
)
714 shadow_type
= GTK_SHADOW_IN
;
715 else if ( flags
& wxCONTROL_UNDETERMINED
)
716 shadow_type
= GTK_SHADOW_ETCHED_IN
;
718 GtkStateType state
= GTK_STATE_NORMAL
;
719 if ( flags
& wxCONTROL_DISABLED
)
720 state
= GTK_STATE_INSENSITIVE
;
721 if ( flags
& wxCONTROL_PRESSED
)
722 state
= GTK_STATE_ACTIVE
;
724 Don't know when to set this
725 state_type = GTK_STATE_PRELIGHT;
737 dc
.LogicalToDeviceX(rect
.x
),
738 dc
.LogicalToDeviceY(rect
.y
),
739 rect
.width
, rect
.height