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"
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 DrawRadioBitmap(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();
161 #if !wxUSE_GRAPHICS_CONTEXT
168 // ----------------------------------------------------------------------------
169 // list/tree controls drawing
170 // ----------------------------------------------------------------------------
173 wxRendererGTK::DrawHeaderButton(wxWindow
*win
,
177 wxHeaderSortIconType sortArrow
,
178 wxHeaderButtonParams
* params
)
181 GtkWidget
*button
= wxGTKPrivate::GetHeaderButtonWidget();
182 if (flags
& wxCONTROL_SPECIAL
)
183 button
= wxGTKPrivate::GetHeaderButtonWidgetFirst();
184 if (flags
& wxCONTROL_DIRTY
)
185 button
= wxGTKPrivate::GetHeaderButtonWidgetLast();
187 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
188 wxASSERT_MSG( gdk_window
,
189 wxT("cannot use wxRendererNative on wxDC of this type") );
192 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
195 GtkStateType state
= GTK_STATE_NORMAL
;
196 if (flags
& wxCONTROL_DISABLED
)
197 state
= GTK_STATE_INSENSITIVE
;
200 if (flags
& wxCONTROL_CURRENT
)
201 state
= GTK_STATE_PRELIGHT
;
213 dc
.LogicalToDeviceX(rect
.x
) - x_diff
, rect
.y
, rect
.width
, rect
.height
216 return DrawHeaderButtonContents(win
, dc
, rect
, flags
, sortArrow
, params
);
219 int wxRendererGTK::GetHeaderButtonHeight(wxWindow
*WXUNUSED(win
))
221 GtkWidget
*button
= wxGTKPrivate::GetHeaderButtonWidget();
224 GTK_WIDGET_GET_CLASS(button
)->size_request(button
, &req
);
230 // draw a ">" or "v" button
232 wxRendererGTK::DrawTreeItemButton(wxWindow
* win
,
233 wxDC
& dc
, const wxRect
& rect
, int flags
)
235 GtkWidget
*tree
= wxGTKPrivate::GetTreeWidget();
237 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
238 wxASSERT_MSG( gdk_window
,
239 wxT("cannot use wxRendererNative on wxDC of this type") );
242 if ( flags
& wxCONTROL_CURRENT
)
243 state
= GTK_STATE_PRELIGHT
;
245 state
= GTK_STATE_NORMAL
;
248 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
251 // VZ: I don't know how to get the size of the expander so as to centre it
252 // in the given rectangle, +2/3 below is just what looks good here...
261 dc
.LogicalToDeviceX(rect
.x
) + 6 - x_diff
,
262 dc
.LogicalToDeviceY(rect
.y
) + 3,
263 flags
& wxCONTROL_EXPANDED
? GTK_EXPANDER_EXPANDED
264 : GTK_EXPANDER_COLLAPSED
269 // ----------------------------------------------------------------------------
270 // splitter sash drawing
271 // ----------------------------------------------------------------------------
273 static int GetGtkSplitterFullSize(GtkWidget
* widget
)
276 gtk_widget_style_get(widget
, "handle_size", &handle_size
, NULL
);
281 wxSplitterRenderParams
282 wxRendererGTK::GetSplitterParams(const wxWindow
*WXUNUSED(win
))
284 // we don't draw any border, hence 0 for the second field
285 return wxSplitterRenderParams
287 GetGtkSplitterFullSize(wxGTKPrivate::GetSplitterWidget()),
289 true // hot sensitive
294 wxRendererGTK::DrawSplitterBorder(wxWindow
* WXUNUSED(win
),
296 const wxRect
& WXUNUSED(rect
),
303 wxRendererGTK::DrawSplitterSash(wxWindow
* win
,
307 wxOrientation orient
,
310 if ( !win
->m_wxwindow
->window
)
312 // window not realized yet
316 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
317 wxASSERT_MSG( gdk_window
,
318 wxT("cannot use wxRendererNative on wxDC of this type") );
320 wxCoord full_size
= GetGtkSplitterFullSize(wxGTKPrivate::GetSplitterWidget());
322 // are we drawing vertical or horizontal splitter?
323 const bool isVert
= orient
== wxVERTICAL
;
331 rect
.width
= full_size
;
332 rect
.height
= size
.y
;
338 rect
.height
= full_size
;
343 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
348 win
->m_wxwindow
->style
,
350 flags
& wxCONTROL_CURRENT
? GTK_STATE_PRELIGHT
: GTK_STATE_NORMAL
,
352 NULL
/* no clipping */,
355 dc
.LogicalToDeviceX(rect
.x
) - x_diff
,
356 dc
.LogicalToDeviceY(rect
.y
),
359 isVert
? GTK_ORIENTATION_VERTICAL
: GTK_ORIENTATION_HORIZONTAL
364 wxRendererGTK::DrawDropArrow(wxWindow
* win
,
369 GtkWidget
*button
= wxGTKPrivate::GetButtonWidget();
371 // If we give WX_PIZZA(win->m_wxwindow)->bin_window as
372 // a window for gtk_paint_xxx function, then it won't
373 // work for wxMemoryDC. So that is why we assume wxDC
374 // is wxWindowDC (wxClientDC, wxMemoryDC and wxPaintDC
375 // are derived from it) and use its m_window.
376 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
377 wxASSERT_MSG( gdk_window
,
378 wxT("cannot use wxRendererNative on wxDC of this type") );
380 // draw arrow so that there is even space horizontally
382 int arrowX
= rect
.width
/4 + 1;
383 int arrowWidth
= rect
.width
- (arrowX
*2);
385 // scale arrow's height accoording to the width
386 int arrowHeight
= rect
.width
/3;
387 int arrowY
= (rect
.height
-arrowHeight
)/2 +
388 ((rect
.height
-arrowHeight
) & 1);
392 if ( flags
& wxCONTROL_PRESSED
)
393 state
= GTK_STATE_ACTIVE
;
394 else if ( flags
& wxCONTROL_DISABLED
)
395 state
= GTK_STATE_INSENSITIVE
;
396 else if ( flags
& wxCONTROL_CURRENT
)
397 state
= GTK_STATE_PRELIGHT
;
399 state
= GTK_STATE_NORMAL
;
401 // draw arrow on button
407 flags
& wxCONTROL_PRESSED
? GTK_SHADOW_IN
: GTK_SHADOW_OUT
,
421 wxRendererGTK::DrawComboBoxDropButton(wxWindow
*win
,
426 DrawPushButton(win
,dc
,rect
,flags
);
427 DrawDropArrow(win
,dc
,rect
);
431 wxRendererGTK::GetCheckBoxSize(wxWindow
*WXUNUSED(win
))
433 gint indicator_size
, indicator_spacing
;
434 gtk_widget_style_get(wxGTKPrivate::GetCheckButtonWidget(),
435 "indicator_size", &indicator_size
,
436 "indicator_spacing", &indicator_spacing
,
439 int size
= indicator_size
+ indicator_spacing
* 2;
440 return wxSize(size
, size
);
444 wxRendererGTK::DrawCheckBox(wxWindow
* win
,
449 GtkWidget
*button
= wxGTKPrivate::GetCheckButtonWidget();
451 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
452 wxASSERT_MSG( gdk_window
,
453 wxT("cannot use wxRendererNative on wxDC of this type") );
455 gint indicator_size
, indicator_spacing
;
456 gtk_widget_style_get(button
,
457 "indicator_size", &indicator_size
,
458 "indicator_spacing", &indicator_spacing
,
463 if ( flags
& wxCONTROL_PRESSED
)
464 state
= GTK_STATE_ACTIVE
;
465 else if ( flags
& wxCONTROL_DISABLED
)
466 state
= GTK_STATE_INSENSITIVE
;
467 else if ( flags
& wxCONTROL_CURRENT
)
468 state
= GTK_STATE_PRELIGHT
;
470 state
= GTK_STATE_NORMAL
;
472 GtkShadowType shadow_type
;
474 if ( flags
& wxCONTROL_UNDETERMINED
)
475 shadow_type
= GTK_SHADOW_ETCHED_IN
;
476 else if ( flags
& wxCONTROL_CHECKED
)
477 shadow_type
= GTK_SHADOW_IN
;
479 shadow_type
= GTK_SHADOW_OUT
;
490 dc
.LogicalToDeviceX(rect
.x
) + indicator_spacing
,
491 dc
.LogicalToDeviceY(rect
.y
) + indicator_spacing
,
492 indicator_size
, indicator_size
497 wxRendererGTK::DrawPushButton(wxWindow
* win
,
502 GtkWidget
*button
= wxGTKPrivate::GetButtonWidget();
504 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
505 wxASSERT_MSG( gdk_window
,
506 wxT("cannot use wxRendererNative on wxDC of this type") );
511 if ( flags
& wxCONTROL_PRESSED
)
512 state
= GTK_STATE_ACTIVE
;
513 else if ( flags
& wxCONTROL_DISABLED
)
514 state
= GTK_STATE_INSENSITIVE
;
515 else if ( flags
& wxCONTROL_CURRENT
)
516 state
= GTK_STATE_PRELIGHT
;
518 state
= GTK_STATE_NORMAL
;
525 flags
& wxCONTROL_PRESSED
? GTK_SHADOW_IN
: GTK_SHADOW_OUT
,
529 dc
.LogicalToDeviceX(rect
.x
),
530 dc
.LogicalToDeviceY(rect
.y
),
537 wxRendererGTK::DrawItemSelectionRect(wxWindow
* win
,
542 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
543 wxASSERT_MSG( gdk_window
,
544 wxT("cannot use wxRendererNative on wxDC of this type") );
546 if (flags
& wxCONTROL_SELECTED
)
549 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
552 // the wxCONTROL_FOCUSED state is deduced
553 // directly from the m_wxwindow by GTK+
554 gtk_paint_flat_box(wxGTKPrivate::GetTreeWidget()->style
,
561 dc
.LogicalToDeviceX(rect
.x
) - x_diff
,
562 dc
.LogicalToDeviceY(rect
.y
),
567 if ((flags
& wxCONTROL_CURRENT
) && (flags
& wxCONTROL_FOCUSED
))
568 DrawFocusRect(win
, dc
, rect
, flags
);
571 void wxRendererGTK::DrawFocusRect(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
573 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
574 wxASSERT_MSG( gdk_window
,
575 wxT("cannot use wxRendererNative on wxDC of this type") );
578 if (flags
& wxCONTROL_SELECTED
)
579 state
= GTK_STATE_SELECTED
;
581 state
= GTK_STATE_NORMAL
;
583 gtk_paint_focus( win
->m_widget
->style
,
589 dc
.LogicalToDeviceX(rect
.x
),
590 dc
.LogicalToDeviceY(rect
.y
),
595 // Uses the theme to draw the border and fill for something like a wxTextCtrl
596 void wxRendererGTK::DrawTextCtrl(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
598 GtkWidget
*entry
= wxGTKPrivate::GetTextEntryWidget();
600 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
602 GtkStateType state
= GTK_STATE_NORMAL
;
603 if ( flags
& wxCONTROL_DISABLED
)
604 state
= GTK_STATE_INSENSITIVE
;
606 if (flags
& wxCONTROL_CURRENT
)
607 GTK_WIDGET_SET_FLAGS( entry
, GTK_HAS_FOCUS
);
609 GTK_WIDGET_UNSET_FLAGS( entry
, GTK_HAS_FOCUS
);
620 dc
.LogicalToDeviceX(rect
.x
),
621 dc
.LogicalToDeviceY(rect
.y
),
627 // Draw the equivallent of a wxComboBox
628 void wxRendererGTK::DrawComboBox(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
630 GtkWidget
*combo
= wxGTKPrivate::GetComboBoxWidget();
632 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
634 GtkStateType state
= GTK_STATE_NORMAL
;
635 if ( flags
& wxCONTROL_DISABLED
)
636 state
= GTK_STATE_INSENSITIVE
;
638 if (flags
& wxCONTROL_CURRENT
)
639 GTK_WIDGET_SET_FLAGS( combo
, GTK_HAS_FOCUS
);
641 GTK_WIDGET_UNSET_FLAGS( combo
, GTK_HAS_FOCUS
);
652 dc
.LogicalToDeviceX(rect
.x
),
653 dc
.LogicalToDeviceY(rect
.y
),
659 int extent
= rect
.height
/ 2;
660 r
.x
+= rect
.width
- extent
- extent
/2;
676 dc
.LogicalToDeviceX(r
.x
),
677 dc
.LogicalToDeviceY(r
.y
),
683 r
.x
+= rect
.width
- 2*extent
;
691 GTK_SHADOW_ETCHED_OUT
,
695 dc
.LogicalToDeviceX(r
.x
),
696 dc
.LogicalToDeviceY(r
.y
+1),
703 void wxRendererGTK::DrawChoice(wxWindow
* win
, wxDC
& dc
,
704 const wxRect
& rect
, int flags
)
706 DrawComboBox( win
, dc
, rect
, flags
);
710 // Draw a themed radio button
711 void wxRendererGTK::DrawRadioBitmap(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
713 GtkWidget
*button
= wxGTKPrivate::GetRadioButtonWidget();
715 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
717 GtkShadowType shadow_type
= GTK_SHADOW_OUT
;
718 if ( flags
& wxCONTROL_CHECKED
)
719 shadow_type
= GTK_SHADOW_IN
;
720 else if ( flags
& wxCONTROL_UNDETERMINED
)
721 shadow_type
= GTK_SHADOW_ETCHED_IN
;
723 GtkStateType state
= GTK_STATE_NORMAL
;
724 if ( flags
& wxCONTROL_DISABLED
)
725 state
= GTK_STATE_INSENSITIVE
;
726 if ( flags
& wxCONTROL_PRESSED
)
727 state
= GTK_STATE_ACTIVE
;
729 Don't know when to set this
730 state_type = GTK_STATE_PRELIGHT;
742 dc
.LogicalToDeviceX(rect
.x
),
743 dc
.LogicalToDeviceY(rect
.y
),
744 rect
.width
, rect
.height