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
);
59 virtual int GetHeaderButtonMargin(wxWindow
*win
);
62 // draw the expanded/collapsed icon for a tree control item
63 virtual void DrawTreeItemButton(wxWindow
*win
,
68 virtual void DrawSplitterBorder(wxWindow
*win
,
72 virtual void DrawSplitterSash(wxWindow
*win
,
79 virtual void DrawComboBoxDropButton(wxWindow
*win
,
84 virtual void DrawDropArrow(wxWindow
*win
,
89 virtual void DrawCheckBox(wxWindow
*win
,
94 virtual void DrawPushButton(wxWindow
*win
,
99 virtual void DrawItemSelectionRect(wxWindow
*win
,
104 virtual void DrawChoice(wxWindow
* win
,
109 virtual void DrawComboBox(wxWindow
* win
,
114 virtual void DrawTextCtrl(wxWindow
* win
,
119 virtual void DrawRadioBitmap(wxWindow
* win
,
124 virtual void DrawFocusRect(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
= 0);
126 virtual wxSize
GetCheckBoxSize(wxWindow
*win
);
128 virtual wxSplitterRenderParams
GetSplitterParams(const wxWindow
*win
);
131 // ============================================================================
133 // ============================================================================
136 wxRendererNative
& wxRendererNative::GetDefault()
138 static wxRendererGTK s_rendererGTK
;
140 return s_rendererGTK
;
143 static GdkWindow
* wxGetGdkWindowForDC(wxWindow
* win
, wxDC
& dc
)
145 GdkWindow
* gdk_window
= NULL
;
147 #if wxUSE_GRAPHICS_CONTEXT
148 if ( dc
.IsKindOf( CLASSINFO(wxGCDC
) ) )
149 gdk_window
= win
->GTKGetDrawingWindow();
154 wxDCImpl
*impl
= dc
.GetImpl();
155 wxGTKDCImpl
*gtk_impl
= wxDynamicCast( impl
, wxGTKDCImpl
);
157 gdk_window
= gtk_impl
->GetGDKWindow();
159 gdk_window
= dc
.GetGDKWindow();
163 #if !wxUSE_GRAPHICS_CONTEXT
170 // ----------------------------------------------------------------------------
171 // list/tree controls drawing
172 // ----------------------------------------------------------------------------
175 wxRendererGTK::DrawHeaderButton(wxWindow
*win
,
179 wxHeaderSortIconType sortArrow
,
180 wxHeaderButtonParams
* params
)
183 GtkWidget
*button
= wxGTKPrivate::GetHeaderButtonWidget();
184 if (flags
& wxCONTROL_SPECIAL
)
185 button
= wxGTKPrivate::GetHeaderButtonWidgetFirst();
186 if (flags
& wxCONTROL_DIRTY
)
187 button
= wxGTKPrivate::GetHeaderButtonWidgetLast();
189 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
190 wxASSERT_MSG( gdk_window
,
191 wxT("cannot use wxRendererNative on wxDC of this type") );
194 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
197 GtkStateType state
= GTK_STATE_NORMAL
;
198 if (flags
& wxCONTROL_DISABLED
)
199 state
= GTK_STATE_INSENSITIVE
;
202 if (flags
& wxCONTROL_CURRENT
)
203 state
= GTK_STATE_PRELIGHT
;
208 gtk_widget_get_style(button
),
215 dc
.LogicalToDeviceX(rect
.x
) - x_diff
, rect
.y
, rect
.width
, rect
.height
218 return DrawHeaderButtonContents(win
, dc
, rect
, flags
, sortArrow
, params
);
221 int wxRendererGTK::GetHeaderButtonHeight(wxWindow
*WXUNUSED(win
))
223 GtkWidget
*button
= wxGTKPrivate::GetHeaderButtonWidget();
226 GTK_WIDGET_GET_CLASS(button
)->size_request(button
, &req
);
231 int wxRendererGTK::GetHeaderButtonMargin(wxWindow
*WXUNUSED(win
))
233 wxFAIL_MSG( "GetHeaderButtonMargin() not implemented" );
238 // draw a ">" or "v" button
240 wxRendererGTK::DrawTreeItemButton(wxWindow
* win
,
241 wxDC
& dc
, const wxRect
& rect
, int flags
)
243 GtkWidget
*tree
= wxGTKPrivate::GetTreeWidget();
245 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
246 wxASSERT_MSG( gdk_window
,
247 wxT("cannot use wxRendererNative on wxDC of this type") );
250 if ( flags
& wxCONTROL_CURRENT
)
251 state
= GTK_STATE_PRELIGHT
;
253 state
= GTK_STATE_NORMAL
;
256 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
259 // x and y parameters specify the center of the expander
262 gtk_widget_get_style(tree
),
268 dc
.LogicalToDeviceX(rect
.x
) + rect
.width
/ 2 - x_diff
,
269 dc
.LogicalToDeviceY(rect
.y
) + rect
.height
/ 2,
270 flags
& wxCONTROL_EXPANDED
? GTK_EXPANDER_EXPANDED
271 : GTK_EXPANDER_COLLAPSED
276 // ----------------------------------------------------------------------------
277 // splitter sash drawing
278 // ----------------------------------------------------------------------------
280 static int GetGtkSplitterFullSize(GtkWidget
* widget
)
283 gtk_widget_style_get(widget
, "handle_size", &handle_size
, NULL
);
288 wxSplitterRenderParams
289 wxRendererGTK::GetSplitterParams(const wxWindow
*WXUNUSED(win
))
291 // we don't draw any border, hence 0 for the second field
292 return wxSplitterRenderParams
294 GetGtkSplitterFullSize(wxGTKPrivate::GetSplitterWidget()),
296 true // hot sensitive
301 wxRendererGTK::DrawSplitterBorder(wxWindow
* WXUNUSED(win
),
303 const wxRect
& WXUNUSED(rect
),
310 wxRendererGTK::DrawSplitterSash(wxWindow
* win
,
314 wxOrientation orient
,
317 if (gtk_widget_get_window(win
->m_wxwindow
) == NULL
)
319 // window not realized yet
323 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
324 wxASSERT_MSG( gdk_window
,
325 wxT("cannot use wxRendererNative on wxDC of this type") );
327 wxCoord full_size
= GetGtkSplitterFullSize(wxGTKPrivate::GetSplitterWidget());
329 // are we drawing vertical or horizontal splitter?
330 const bool isVert
= orient
== wxVERTICAL
;
338 rect
.width
= full_size
;
339 rect
.height
= size
.y
;
345 rect
.height
= full_size
;
350 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
355 gtk_widget_get_style(win
->m_wxwindow
),
357 flags
& wxCONTROL_CURRENT
? GTK_STATE_PRELIGHT
: GTK_STATE_NORMAL
,
359 NULL
/* no clipping */,
362 dc
.LogicalToDeviceX(rect
.x
) - x_diff
,
363 dc
.LogicalToDeviceY(rect
.y
),
366 isVert
? GTK_ORIENTATION_VERTICAL
: GTK_ORIENTATION_HORIZONTAL
371 wxRendererGTK::DrawDropArrow(wxWindow
* win
,
376 GtkWidget
*button
= wxGTKPrivate::GetButtonWidget();
378 // If we give WX_PIZZA(win->m_wxwindow)->bin_window as
379 // a window for gtk_paint_xxx function, then it won't
380 // work for wxMemoryDC. So that is why we assume wxDC
381 // is wxWindowDC (wxClientDC, wxMemoryDC and wxPaintDC
382 // are derived from it) and use its m_window.
383 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
384 wxASSERT_MSG( gdk_window
,
385 wxT("cannot use wxRendererNative on wxDC of this type") );
387 // draw arrow so that there is even space horizontally
389 int arrowX
= rect
.width
/4 + 1;
390 int arrowWidth
= rect
.width
- (arrowX
*2);
392 // scale arrow's height accoording to the width
393 int arrowHeight
= rect
.width
/3;
394 int arrowY
= (rect
.height
-arrowHeight
)/2 +
395 ((rect
.height
-arrowHeight
) & 1);
399 if ( flags
& wxCONTROL_PRESSED
)
400 state
= GTK_STATE_ACTIVE
;
401 else if ( flags
& wxCONTROL_DISABLED
)
402 state
= GTK_STATE_INSENSITIVE
;
403 else if ( flags
& wxCONTROL_CURRENT
)
404 state
= GTK_STATE_PRELIGHT
;
406 state
= GTK_STATE_NORMAL
;
408 // draw arrow on button
411 gtk_widget_get_style(button
),
414 flags
& wxCONTROL_PRESSED
? GTK_SHADOW_IN
: GTK_SHADOW_OUT
,
428 wxRendererGTK::DrawComboBoxDropButton(wxWindow
*win
,
433 DrawPushButton(win
,dc
,rect
,flags
);
434 DrawDropArrow(win
,dc
,rect
);
438 wxRendererGTK::GetCheckBoxSize(wxWindow
*WXUNUSED(win
))
440 gint indicator_size
, indicator_spacing
;
441 gtk_widget_style_get(wxGTKPrivate::GetCheckButtonWidget(),
442 "indicator_size", &indicator_size
,
443 "indicator_spacing", &indicator_spacing
,
446 int size
= indicator_size
+ indicator_spacing
* 2;
447 return wxSize(size
, size
);
451 wxRendererGTK::DrawCheckBox(wxWindow
* win
,
456 GtkWidget
*button
= wxGTKPrivate::GetCheckButtonWidget();
458 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
459 wxASSERT_MSG( gdk_window
,
460 wxT("cannot use wxRendererNative on wxDC of this type") );
462 gint indicator_size
, indicator_spacing
;
463 gtk_widget_style_get(button
,
464 "indicator_size", &indicator_size
,
465 "indicator_spacing", &indicator_spacing
,
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
;
479 GtkShadowType shadow_type
;
481 if ( flags
& wxCONTROL_UNDETERMINED
)
482 shadow_type
= GTK_SHADOW_ETCHED_IN
;
483 else if ( flags
& wxCONTROL_CHECKED
)
484 shadow_type
= GTK_SHADOW_IN
;
486 shadow_type
= GTK_SHADOW_OUT
;
490 gtk_widget_get_style(button
),
497 dc
.LogicalToDeviceX(rect
.x
) + indicator_spacing
,
498 dc
.LogicalToDeviceY(rect
.y
) + indicator_spacing
,
499 indicator_size
, indicator_size
504 wxRendererGTK::DrawPushButton(wxWindow
* win
,
509 GtkWidget
*button
= wxGTKPrivate::GetButtonWidget();
511 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
512 wxASSERT_MSG( gdk_window
,
513 wxT("cannot use wxRendererNative on wxDC of this type") );
518 if ( flags
& wxCONTROL_PRESSED
)
519 state
= GTK_STATE_ACTIVE
;
520 else if ( flags
& wxCONTROL_DISABLED
)
521 state
= GTK_STATE_INSENSITIVE
;
522 else if ( flags
& wxCONTROL_CURRENT
)
523 state
= GTK_STATE_PRELIGHT
;
525 state
= GTK_STATE_NORMAL
;
529 gtk_widget_get_style(button
),
532 flags
& wxCONTROL_PRESSED
? GTK_SHADOW_IN
: GTK_SHADOW_OUT
,
536 dc
.LogicalToDeviceX(rect
.x
),
537 dc
.LogicalToDeviceY(rect
.y
),
544 wxRendererGTK::DrawItemSelectionRect(wxWindow
* win
,
549 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
550 wxASSERT_MSG( gdk_window
,
551 wxT("cannot use wxRendererNative on wxDC of this type") );
553 if (flags
& wxCONTROL_SELECTED
)
556 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
559 // the wxCONTROL_FOCUSED state is deduced
560 // directly from the m_wxwindow by GTK+
561 gtk_paint_flat_box(gtk_widget_get_style(wxGTKPrivate::GetTreeWidget()),
568 dc
.LogicalToDeviceX(rect
.x
) - x_diff
,
569 dc
.LogicalToDeviceY(rect
.y
),
574 if ((flags
& wxCONTROL_CURRENT
) && (flags
& wxCONTROL_FOCUSED
))
575 DrawFocusRect(win
, dc
, rect
, flags
);
578 void wxRendererGTK::DrawFocusRect(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
580 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
581 wxASSERT_MSG( gdk_window
,
582 wxT("cannot use wxRendererNative on wxDC of this type") );
585 if (flags
& wxCONTROL_SELECTED
)
586 state
= GTK_STATE_SELECTED
;
588 state
= GTK_STATE_NORMAL
;
590 gtk_paint_focus( gtk_widget_get_style(win
->m_widget
),
596 dc
.LogicalToDeviceX(rect
.x
),
597 dc
.LogicalToDeviceY(rect
.y
),
602 // Uses the theme to draw the border and fill for something like a wxTextCtrl
603 void wxRendererGTK::DrawTextCtrl(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
605 GtkWidget
*entry
= wxGTKPrivate::GetTextEntryWidget();
607 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
609 GtkStateType state
= GTK_STATE_NORMAL
;
610 if ( flags
& wxCONTROL_DISABLED
)
611 state
= GTK_STATE_INSENSITIVE
;
613 gtk_widget_set_can_focus(entry
, (flags
& wxCONTROL_CURRENT
) != 0);
617 gtk_widget_get_style(entry
),
624 dc
.LogicalToDeviceX(rect
.x
),
625 dc
.LogicalToDeviceY(rect
.y
),
631 // Draw the equivalent of a wxComboBox
632 void wxRendererGTK::DrawComboBox(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
634 GtkWidget
*combo
= wxGTKPrivate::GetComboBoxWidget();
636 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
638 GtkStateType state
= GTK_STATE_NORMAL
;
639 if ( flags
& wxCONTROL_DISABLED
)
640 state
= GTK_STATE_INSENSITIVE
;
642 gtk_widget_set_can_focus(combo
, (flags
& wxCONTROL_CURRENT
) != 0);
646 gtk_widget_get_style(combo
),
653 dc
.LogicalToDeviceX(rect
.x
),
654 dc
.LogicalToDeviceY(rect
.y
),
660 int extent
= rect
.height
/ 2;
661 r
.x
+= rect
.width
- extent
- extent
/2;
668 gtk_widget_get_style(combo
),
677 dc
.LogicalToDeviceX(r
.x
),
678 dc
.LogicalToDeviceY(r
.y
),
684 r
.x
+= rect
.width
- 2*extent
;
689 gtk_widget_get_style(combo
),
692 GTK_SHADOW_ETCHED_OUT
,
696 dc
.LogicalToDeviceX(r
.x
),
697 dc
.LogicalToDeviceY(r
.y
+1),
704 void wxRendererGTK::DrawChoice(wxWindow
* win
, wxDC
& dc
,
705 const wxRect
& rect
, int flags
)
707 DrawComboBox( win
, dc
, rect
, flags
);
711 // Draw a themed radio button
712 void wxRendererGTK::DrawRadioBitmap(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
714 GtkWidget
*button
= wxGTKPrivate::GetRadioButtonWidget();
716 GdkWindow
* gdk_window
= wxGetGdkWindowForDC(win
, dc
);
718 GtkShadowType shadow_type
= GTK_SHADOW_OUT
;
719 if ( flags
& wxCONTROL_CHECKED
)
720 shadow_type
= GTK_SHADOW_IN
;
721 else if ( flags
& wxCONTROL_UNDETERMINED
)
722 shadow_type
= GTK_SHADOW_ETCHED_IN
;
724 GtkStateType state
= GTK_STATE_NORMAL
;
725 if ( flags
& wxCONTROL_DISABLED
)
726 state
= GTK_STATE_INSENSITIVE
;
727 if ( flags
& wxCONTROL_PRESSED
)
728 state
= GTK_STATE_ACTIVE
;
730 Don't know when to set this
731 state_type = GTK_STATE_PRELIGHT;
736 gtk_widget_get_style(button
),
743 dc
.LogicalToDeviceX(rect
.x
),
744 dc
.LogicalToDeviceY(rect
.y
),
745 rect
.width
, rect
.height