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/gtk/dc.h"
37 #include "wx/gtk/private.h"
41 // ----------------------------------------------------------------------------
42 // wxRendererGTK: our wxRendererNative implementation
43 // ----------------------------------------------------------------------------
45 class WXDLLEXPORT wxRendererGTK
: public wxDelegateRendererNative
48 // draw the header control button (used by wxListCtrl)
49 virtual int DrawHeaderButton(wxWindow
*win
,
53 wxHeaderSortIconType sortArrow
= wxHDR_SORT_ICON_NONE
,
54 wxHeaderButtonParams
* params
= NULL
);
56 // draw the expanded/collapsed icon for a tree control item
57 virtual void DrawTreeItemButton(wxWindow
*win
,
62 virtual void DrawSplitterBorder(wxWindow
*win
,
66 virtual void DrawSplitterSash(wxWindow
*win
,
73 virtual void DrawComboBoxDropButton(wxWindow
*win
,
78 virtual void DrawDropArrow(wxWindow
*win
,
83 virtual void DrawCheckBox(wxWindow
*win
,
88 virtual void DrawPushButton(wxWindow
*win
,
93 virtual void DrawItemSelectionRect(wxWindow
*win
,
98 virtual void DrawChoice(wxWindow
* win
,
103 virtual void DrawComboBox(wxWindow
* win
,
108 virtual void DrawTextCtrl(wxWindow
* win
,
113 virtual void DrawRadioButton(wxWindow
* win
,
118 virtual void DrawFocusRect(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
= 0);
120 virtual wxSize
GetCheckBoxSize(wxWindow
*win
);
122 virtual wxSplitterRenderParams
GetSplitterParams(const wxWindow
*win
);
125 // ============================================================================
127 // ============================================================================
130 wxRendererNative
& wxRendererNative::GetDefault()
132 static wxRendererGTK s_rendererGTK
;
134 return s_rendererGTK
;
137 static GdkWindow
* wxGetGdkWindowForDC(wxDC
& dc
)
139 GdkWindow
* gdk_window
= NULL
;
141 wxDCImpl
*impl
= dc
.GetImpl();
142 wxGTKDCImpl
*gtk_impl
= wxDynamicCast( impl
, wxGTKDCImpl
);
144 gdk_window
= gtk_impl
->GetGDKWindow();
146 gdk_window
= dc
.GetGDKWindow();
151 // ----------------------------------------------------------------------------
152 // list/tree controls drawing
153 // ----------------------------------------------------------------------------
156 wxRendererGTK::DrawHeaderButton(wxWindow
*win
,
160 wxHeaderSortIconType sortArrow
,
161 wxHeaderButtonParams
* params
)
164 GtkWidget
*button
= wxGTKPrivate::GetHeaderButtonWidget();
166 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(dc
);
167 wxASSERT_MSG( gdk_window
,
168 wxT("cannot use wxRendererNative on wxDC of this type") );
171 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
174 GtkStateType state
= GTK_STATE_NORMAL
;
175 if (flags
& wxCONTROL_DISABLED
)
176 state
= GTK_STATE_INSENSITIVE
;
179 if (flags
& wxCONTROL_CURRENT
)
180 state
= GTK_STATE_PRELIGHT
;
192 dc
.LogicalToDeviceX(rect
.x
) - x_diff
, rect
.y
, rect
.width
, rect
.height
195 return DrawHeaderButtonContents(win
, dc
, rect
, flags
, sortArrow
, params
);
198 // draw a ">" or "v" button
200 wxRendererGTK::DrawTreeItemButton(wxWindow
* win
,
201 wxDC
& dc
, const wxRect
& rect
, int flags
)
203 GtkWidget
*tree
= wxGTKPrivate::GetTreeWidget();
205 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(dc
);
206 wxASSERT_MSG( gdk_window
,
207 wxT("cannot use wxRendererNative on wxDC of this type") );
210 if ( flags
& wxCONTROL_CURRENT
)
211 state
= GTK_STATE_PRELIGHT
;
213 state
= GTK_STATE_NORMAL
;
216 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
219 // VZ: I don't know how to get the size of the expander so as to centre it
220 // in the given rectangle, +2/3 below is just what looks good here...
229 dc
.LogicalToDeviceX(rect
.x
) + 6 - x_diff
,
230 dc
.LogicalToDeviceY(rect
.y
) + 3,
231 flags
& wxCONTROL_EXPANDED
? GTK_EXPANDER_EXPANDED
232 : GTK_EXPANDER_COLLAPSED
237 // ----------------------------------------------------------------------------
238 // splitter sash drawing
239 // ----------------------------------------------------------------------------
241 static int GetGtkSplitterFullSize(GtkWidget
* widget
)
244 gtk_widget_style_get(widget
, "handle_size", &handle_size
, NULL
);
249 wxSplitterRenderParams
250 wxRendererGTK::GetSplitterParams(const wxWindow
*WXUNUSED(win
))
252 // we don't draw any border, hence 0 for the second field
253 return wxSplitterRenderParams
255 GetGtkSplitterFullSize(wxGTKPrivate::GetSplitterWidget()),
257 true // hot sensitive
262 wxRendererGTK::DrawSplitterBorder(wxWindow
* WXUNUSED(win
),
264 const wxRect
& WXUNUSED(rect
),
271 wxRendererGTK::DrawSplitterSash(wxWindow
*win
,
275 wxOrientation orient
,
278 if ( !win
->m_wxwindow
->window
)
280 // window not realized yet
284 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(dc
);
285 wxASSERT_MSG( gdk_window
,
286 wxT("cannot use wxRendererNative on wxDC of this type") );
288 wxCoord full_size
= GetGtkSplitterFullSize(wxGTKPrivate::GetSplitterWidget());
290 // are we drawing vertical or horizontal splitter?
291 const bool isVert
= orient
== wxVERTICAL
;
299 rect
.width
= full_size
;
300 rect
.height
= size
.y
;
306 rect
.height
= full_size
;
311 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
316 win
->m_wxwindow
->style
,
318 flags
& wxCONTROL_CURRENT
? GTK_STATE_PRELIGHT
: GTK_STATE_NORMAL
,
320 NULL
/* no clipping */,
323 dc
.LogicalToDeviceX(rect
.x
) - x_diff
,
324 dc
.LogicalToDeviceY(rect
.y
),
327 isVert
? GTK_ORIENTATION_VERTICAL
: GTK_ORIENTATION_HORIZONTAL
332 wxRendererGTK::DrawDropArrow(wxWindow
*WXUNUSED(win
),
337 GtkWidget
*button
= wxGTKPrivate::GetButtonWidget();
339 // If we give GTK_PIZZA(win->m_wxwindow)->bin_window as
340 // a window for gtk_paint_xxx function, then it won't
341 // work for wxMemoryDC. So that is why we assume wxDC
342 // is wxWindowDC (wxClientDC, wxMemoryDC and wxPaintDC
343 // are derived from it) and use its m_window.
344 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(dc
);
345 wxASSERT_MSG( gdk_window
,
346 wxT("cannot use wxRendererNative on wxDC of this type") );
348 // draw arrow so that there is even space horizontally
350 int arrowX
= rect
.width
/4 + 1;
351 int arrowWidth
= rect
.width
- (arrowX
*2);
353 // scale arrow's height accoording to the width
354 int arrowHeight
= rect
.width
/3;
355 int arrowY
= (rect
.height
-arrowHeight
)/2 +
356 ((rect
.height
-arrowHeight
) & 1);
360 if ( flags
& wxCONTROL_PRESSED
)
361 state
= GTK_STATE_ACTIVE
;
362 else if ( flags
& wxCONTROL_DISABLED
)
363 state
= GTK_STATE_INSENSITIVE
;
364 else if ( flags
& wxCONTROL_CURRENT
)
365 state
= GTK_STATE_PRELIGHT
;
367 state
= GTK_STATE_NORMAL
;
369 // draw arrow on button
375 flags
& wxCONTROL_PRESSED
? GTK_SHADOW_IN
: GTK_SHADOW_OUT
,
389 wxRendererGTK::DrawComboBoxDropButton(wxWindow
*win
,
394 DrawPushButton(win
,dc
,rect
,flags
);
395 DrawDropArrow(win
,dc
,rect
);
399 wxRendererGTK::GetCheckBoxSize(wxWindow
*WXUNUSED(win
))
401 gint indicator_size
, indicator_spacing
;
402 gtk_widget_style_get(wxGTKPrivate::GetCheckButtonWidget(),
403 "indicator_size", &indicator_size
,
404 "indicator_spacing", &indicator_spacing
,
407 int size
= indicator_size
+ indicator_spacing
* 2;
408 return wxSize(size
, size
);
412 wxRendererGTK::DrawCheckBox(wxWindow
*WXUNUSED(win
),
417 GtkWidget
*button
= wxGTKPrivate::GetCheckButtonWidget();
419 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(dc
);
420 wxASSERT_MSG( gdk_window
,
421 wxT("cannot use wxRendererNative on wxDC of this type") );
423 gint indicator_size
, indicator_spacing
;
424 gtk_widget_style_get(button
,
425 "indicator_size", &indicator_size
,
426 "indicator_spacing", &indicator_spacing
,
431 if ( flags
& wxCONTROL_PRESSED
)
432 state
= GTK_STATE_ACTIVE
;
433 else if ( flags
& wxCONTROL_DISABLED
)
434 state
= GTK_STATE_INSENSITIVE
;
435 else if ( flags
& wxCONTROL_CURRENT
)
436 state
= GTK_STATE_PRELIGHT
;
438 state
= GTK_STATE_NORMAL
;
445 flags
& wxCONTROL_CHECKED
? GTK_SHADOW_IN
: GTK_SHADOW_OUT
,
449 dc
.LogicalToDeviceX(rect
.x
) + indicator_spacing
,
450 dc
.LogicalToDeviceY(rect
.y
) + indicator_spacing
,
451 indicator_size
, indicator_size
456 wxRendererGTK::DrawPushButton(wxWindow
*WXUNUSED(win
),
461 GtkWidget
*button
= wxGTKPrivate::GetButtonWidget();
463 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(dc
);
464 wxASSERT_MSG( gdk_window
,
465 wxT("cannot use wxRendererNative on wxDC of this type") );
470 if ( flags
& wxCONTROL_PRESSED
)
471 state
= GTK_STATE_ACTIVE
;
472 else if ( flags
& wxCONTROL_DISABLED
)
473 state
= GTK_STATE_INSENSITIVE
;
474 else if ( flags
& wxCONTROL_CURRENT
)
475 state
= GTK_STATE_PRELIGHT
;
477 state
= GTK_STATE_NORMAL
;
484 flags
& wxCONTROL_PRESSED
? GTK_SHADOW_IN
: GTK_SHADOW_OUT
,
488 dc
.LogicalToDeviceX(rect
.x
),
489 dc
.LogicalToDeviceY(rect
.y
),
496 wxRendererGTK::DrawItemSelectionRect(wxWindow
*win
,
501 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(dc
);
502 wxASSERT_MSG( gdk_window
,
503 wxT("cannot use wxRendererNative on wxDC of this type") );
506 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
510 if (flags
& wxCONTROL_SELECTED
)
512 // the wxCONTROL_FOCUSED state is deduced
513 // directly from the m_wxwindow by GTK+
514 state
= GTK_STATE_SELECTED
;
516 gtk_paint_flat_box( win
->m_widget
->style
,
523 dc
.LogicalToDeviceX(rect
.x
) - x_diff
,
524 dc
.LogicalToDeviceY(rect
.y
),
528 else // !wxCONTROL_SELECTED
530 state
= GTK_STATE_NORMAL
;
533 if ((flags
& wxCONTROL_CURRENT
) && (flags
& wxCONTROL_FOCUSED
))
535 gtk_paint_focus( win
->m_widget
->style
,
540 // Detail "treeview" causes warning with GTK+ 2.12 Clearlooks theme:
541 // "... no property named `row-ending-details'"
542 // Using "treeview-middle" would fix the warning, but the right
543 // edge of the focus rect is not getting erased properly either.
544 // Better to not specify this detail unless the drawing is fixed.
546 dc
.LogicalToDeviceX(rect
.x
),
547 dc
.LogicalToDeviceY(rect
.y
),
553 void wxRendererGTK::DrawFocusRect(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
555 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(dc
);
556 wxASSERT_MSG( gdk_window
,
557 wxT("cannot use wxRendererNative on wxDC of this type") );
560 if (flags
& wxCONTROL_SELECTED
)
561 state
= GTK_STATE_SELECTED
;
563 state
= GTK_STATE_NORMAL
;
565 gtk_paint_focus( win
->m_widget
->style
,
571 dc
.LogicalToDeviceX(rect
.x
),
572 dc
.LogicalToDeviceY(rect
.y
),
577 // Uses the theme to draw the border and fill for something like a wxTextCtrl
578 void wxRendererGTK::DrawTextCtrl(wxWindow
*, wxDC
& dc
, const wxRect
& rect
, int flags
)
580 GtkWidget
*entry
= wxGTKPrivate::GetTextEntryWidget();
582 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(dc
);
584 GtkStateType state
= GTK_STATE_NORMAL
;
585 if ( flags
& wxCONTROL_DISABLED
)
586 state
= GTK_STATE_INSENSITIVE
;
588 if (flags
& wxCONTROL_CURRENT
)
589 GTK_WIDGET_SET_FLAGS( entry
, GTK_HAS_FOCUS
);
591 GTK_WIDGET_UNSET_FLAGS( entry
, GTK_HAS_FOCUS
);
602 dc
.LogicalToDeviceX(rect
.x
),
603 dc
.LogicalToDeviceY(rect
.y
),
609 // Draw the equivallent of a wxComboBox
610 void wxRendererGTK::DrawComboBox(wxWindow
*, wxDC
& dc
, const wxRect
& rect
, int flags
)
612 GtkWidget
*combo
= wxGTKPrivate::GetComboBoxWidget();
614 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(dc
);
616 GtkStateType state
= GTK_STATE_NORMAL
;
617 if ( flags
& wxCONTROL_DISABLED
)
618 state
= GTK_STATE_INSENSITIVE
;
620 if (flags
& wxCONTROL_CURRENT
)
621 GTK_WIDGET_SET_FLAGS( combo
, GTK_HAS_FOCUS
);
623 GTK_WIDGET_UNSET_FLAGS( combo
, GTK_HAS_FOCUS
);
634 dc
.LogicalToDeviceX(rect
.x
),
635 dc
.LogicalToDeviceY(rect
.y
),
641 int extent
= rect
.height
/ 2;
642 r
.x
+= rect
.width
- extent
- extent
/2;
658 dc
.LogicalToDeviceX(r
.x
),
659 dc
.LogicalToDeviceY(r
.y
),
665 r
.x
+= rect
.width
- 2*extent
;
673 GTK_SHADOW_ETCHED_OUT
,
677 dc
.LogicalToDeviceX(r
.x
),
678 dc
.LogicalToDeviceY(r
.y
+1),
685 void wxRendererGTK::DrawChoice(wxWindow
* win
, wxDC
& dc
,
686 const wxRect
& rect
, int flags
)
688 DrawComboBox( win
, dc
, rect
, flags
);
692 // Draw a themed radio button
693 void wxRendererGTK::DrawRadioButton(wxWindow
*, wxDC
& dc
, const wxRect
& rect
, int flags
)
695 GtkWidget
*button
= wxGTKPrivate::GetRadioButtonWidget();
697 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(dc
);
699 GtkShadowType shadow_type
= GTK_SHADOW_OUT
;
700 if ( flags
& wxCONTROL_CHECKED
)
701 shadow_type
= GTK_SHADOW_IN
;
702 else if ( flags
& wxCONTROL_UNDETERMINED
)
703 shadow_type
= GTK_SHADOW_ETCHED_IN
;
705 GtkStateType state
= GTK_STATE_NORMAL
;
706 if ( flags
& wxCONTROL_DISABLED
)
707 state
= GTK_STATE_INSENSITIVE
;
708 if ( flags
& wxCONTROL_PRESSED
)
709 state
= GTK_STATE_ACTIVE
;
711 Don't know when to set this
712 state_type = GTK_STATE_PRELIGHT;
724 dc
.LogicalToDeviceX(rect
.x
),
725 dc
.LogicalToDeviceY(rect
.y
),
726 rect
.width
, rect
.height