1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/renderer.cpp
3 // Purpose:     implementation of wxRendererNative for wxGTK
4 // Author:      Vadim Zeitlin
5 // Modified by:
6 // Created:     20.07.2003
7 // Copyright:   (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
8 // Licence:     wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10 
11 // ============================================================================
12 // declarations
13 // ============================================================================
14 
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18 
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21 
22 #ifdef __BORLANDC__
23     #pragma hdrstop
24 #endif
25 
26 #include "wx/renderer.h"
27 
28 #ifndef WX_PRECOMP
29     #include "wx/window.h"
30     #include "wx/dcclient.h"
31     #include "wx/settings.h"
32     #include "wx/module.h"
33 #endif
34 
35 #include "wx/dcgraph.h"
36 #ifndef __WXGTK3__
37     #include "wx/gtk/dc.h"
38     #include <gdk/gdk.h>
39     #if wxUSE_GRAPHICS_CONTEXT && defined(GDK_WINDOWING_X11)
40         #include <gdk/gdkx.h>
41         #include <cairo-xlib.h>
42     #endif
43 #endif
44 
45 #include <gtk/gtk.h>
46 #include "wx/gtk/private.h"
47 #include "wx/gtk/private/gtk2-compat.h"
48 
49 #if defined(__WXGTK3__) && !GTK_CHECK_VERSION(3,14,0)
50     #define GTK_STATE_FLAG_CHECKED (1 << 11)
51 #endif
52 
53 // ----------------------------------------------------------------------------
54 // wxRendererGTK: our wxRendererNative implementation
55 // ----------------------------------------------------------------------------
56 
57 class WXDLLEXPORT wxRendererGTK : public wxDelegateRendererNative
58 {
59 public:
60     // draw the header control button (used by wxListCtrl)
61     virtual int  DrawHeaderButton(wxWindow *win,
62                                   wxDC& dc,
63                                   const wxRect& rect,
64                                   int flags = 0,
65                                   wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE,
66                                   wxHeaderButtonParams* params = NULL);
67 
68     virtual int GetHeaderButtonHeight(wxWindow *win);
69 
70     virtual int GetHeaderButtonMargin(wxWindow *win);
71 
72 
73     // draw the expanded/collapsed icon for a tree control item
74     virtual void DrawTreeItemButton(wxWindow *win,
75                                     wxDC& dc,
76                                     const wxRect& rect,
77                                     int flags = 0);
78 
79     virtual void DrawSplitterBorder(wxWindow *win,
80                                     wxDC& dc,
81                                     const wxRect& rect,
82                                     int flags = 0);
83     virtual void DrawSplitterSash(wxWindow *win,
84                                   wxDC& dc,
85                                   const wxSize& size,
86                                   wxCoord position,
87                                   wxOrientation orient,
88                                   int flags = 0);
89 
90     virtual void DrawComboBoxDropButton(wxWindow *win,
91                                         wxDC& dc,
92                                         const wxRect& rect,
93                                         int flags = 0);
94 
95     virtual void DrawDropArrow(wxWindow *win,
96                                wxDC& dc,
97                                const wxRect& rect,
98                                int flags = 0);
99 
100     virtual void DrawCheckBox(wxWindow *win,
101                               wxDC& dc,
102                               const wxRect& rect,
103                               int flags = 0);
104 
105     virtual void DrawPushButton(wxWindow *win,
106                                 wxDC& dc,
107                                 const wxRect& rect,
108                                 int flags = 0);
109 
110     virtual void DrawItemSelectionRect(wxWindow *win,
111                                        wxDC& dc,
112                                        const wxRect& rect,
113                                        int flags = 0);
114 
115     virtual void DrawChoice(wxWindow* win,
116                             wxDC& dc,
117                             const wxRect& rect,
118                             int flags=0);
119 
120     virtual void DrawComboBox(wxWindow* win,
121                                 wxDC& dc,
122                                 const wxRect& rect,
123                                 int flags=0);
124 
125     virtual void DrawTextCtrl(wxWindow* win,
126                                 wxDC& dc,
127                                 const wxRect& rect,
128                                 int flags=0);
129 
130     virtual void DrawRadioBitmap(wxWindow* win,
131                                 wxDC& dc,
132                                 const wxRect& rect,
133                                 int flags=0);
134 
135     virtual void DrawFocusRect(wxWindow* win, wxDC& dc, const wxRect& rect, int flags = 0);
136 
137     virtual wxSize GetCheckBoxSize(wxWindow *win);
138 
139     virtual wxSplitterRenderParams GetSplitterParams(const wxWindow *win);
140 };
141 
142 // ============================================================================
143 // implementation
144 // ============================================================================
145 
146 /* static */
GetDefault()147 wxRendererNative& wxRendererNative::GetDefault()
148 {
149     static wxRendererGTK s_rendererGTK;
150 
151     return s_rendererGTK;
152 }
153 
154 #ifdef __WXGTK3__
155 #define NULL_RECT
156 typedef cairo_t wxGTKDrawable;
157 
wxGetGTKDrawable(wxWindow *,const wxDC & dc)158 static cairo_t* wxGetGTKDrawable(wxWindow*, const wxDC& dc)
159 {
160     wxGraphicsContext* gc = dc.GetGraphicsContext();
161     wxCHECK_MSG(gc, NULL, "cannot use wxRendererNative on wxDC of this type");
162     return static_cast<cairo_t*>(gc->GetNativeContext());
163 }
164 
165 static const GtkStateFlags stateTypeToFlags[] = {
166     GTK_STATE_FLAG_NORMAL, GTK_STATE_FLAG_ACTIVE, GTK_STATE_FLAG_PRELIGHT,
167     GTK_STATE_FLAG_SELECTED, GTK_STATE_FLAG_INSENSITIVE, GTK_STATE_FLAG_INCONSISTENT,
168     GTK_STATE_FLAG_FOCUSED
169 };
170 
171 #else
172 #define NULL_RECT NULL,
173 typedef GdkWindow wxGTKDrawable;
174 
wxGetGTKDrawable(wxWindow *,wxDC & dc)175 static GdkWindow* wxGetGTKDrawable(wxWindow*, wxDC& dc)
176 {
177     GdkWindow* gdk_window = NULL;
178 
179 #if wxUSE_GRAPHICS_CONTEXT && defined(GDK_WINDOWING_X11)
180     cairo_t* cr = NULL;
181     wxGraphicsContext* gc = dc.GetGraphicsContext();
182     if (gc)
183         cr = static_cast<cairo_t*>(gc->GetNativeContext());
184     if (cr)
185     {
186         cairo_surface_t* surf = cairo_get_target(cr);
187         if (cairo_surface_get_type(surf) == CAIRO_SURFACE_TYPE_XLIB)
188         {
189             gdk_window = static_cast<GdkWindow*>(
190                 gdk_xid_table_lookup(cairo_xlib_surface_get_drawable(surf)));
191         }
192     }
193     if (gdk_window == NULL)
194 #endif
195     {
196         wxDCImpl *impl = dc.GetImpl();
197         wxGTKDCImpl *gtk_impl = wxDynamicCast( impl, wxGTKDCImpl );
198         if (gtk_impl)
199             gdk_window = gtk_impl->GetGDKWindow();
200         else
201             wxFAIL_MSG("cannot use wxRendererNative on wxDC of this type");
202     }
203 
204     return gdk_window;
205 }
206 #endif
207 
208 // ----------------------------------------------------------------------------
209 // list/tree controls drawing
210 // ----------------------------------------------------------------------------
211 
212 int
DrawHeaderButton(wxWindow * win,wxDC & dc,const wxRect & rect,int flags,wxHeaderSortIconType sortArrow,wxHeaderButtonParams * params)213 wxRendererGTK::DrawHeaderButton(wxWindow *win,
214                                 wxDC& dc,
215                                 const wxRect& rect,
216                                 int flags,
217                                 wxHeaderSortIconType sortArrow,
218                                 wxHeaderButtonParams* params)
219 {
220     GtkWidget *button = wxGTKPrivate::GetHeaderButtonWidget();
221     if (flags & wxCONTROL_SPECIAL)
222         button = wxGTKPrivate::GetHeaderButtonWidgetFirst();
223     if (flags & wxCONTROL_DIRTY)
224         button = wxGTKPrivate::GetHeaderButtonWidgetLast();
225 
226     int x_diff = 0;
227     if (win->GetLayoutDirection() == wxLayout_RightToLeft)
228         x_diff = rect.width;
229 
230     GtkStateType state = GTK_STATE_NORMAL;
231     if (flags & wxCONTROL_DISABLED)
232         state = GTK_STATE_INSENSITIVE;
233     else
234     {
235         if (flags & wxCONTROL_CURRENT)
236             state = GTK_STATE_PRELIGHT;
237     }
238 
239 #ifdef __WXGTK3__
240     cairo_t* cr = wxGetGTKDrawable(win, dc);
241     if (cr == NULL)
242         return 0;
243 
244 #if GTK_CHECK_VERSION(3,20,0)
245     if (gtk_check_version(3,20,0) == NULL)
246     {
247         GtkWidgetPath* path = gtk_widget_path_new();
248         GtkStyleContext* parent;
249         GtkStyleContext* sc = gtk_style_context_new();
250 
251         gtk_widget_path_append_type(path, GTK_TYPE_WINDOW);
252         gtk_widget_path_iter_set_object_name(path, -1, "window");
253         gtk_widget_path_iter_add_class(path, -1, "background");
254         gtk_style_context_set_path(sc, path);
255 
256         parent = sc;
257         sc = gtk_style_context_new();
258         gtk_widget_path_append_type(path, GTK_TYPE_TREE_VIEW);
259         gtk_widget_path_iter_set_object_name(path, -1, "treeview");
260         gtk_widget_path_iter_add_class(path, -1, "view");
261         gtk_style_context_set_path(sc, path);
262         gtk_style_context_set_parent(sc, parent);
263         g_object_unref(parent);
264 
265         parent = sc;
266         sc = gtk_style_context_new();
267         gtk_widget_path_append_type(path, G_TYPE_NONE);
268         gtk_widget_path_iter_set_object_name(path, -1, "header");
269         gtk_style_context_set_path(sc, path);
270         gtk_style_context_set_parent(sc, parent);
271         g_object_unref(parent);
272 
273         parent = sc;
274         sc = gtk_style_context_new();
275         int pos = 1;
276         if (flags & wxCONTROL_SPECIAL)
277             pos = 0;
278         if (flags & wxCONTROL_DIRTY)
279             pos = 2;
280         GtkWidgetPath* siblings = gtk_widget_path_new();
281         gtk_widget_path_append_type(siblings, GTK_TYPE_BUTTON);
282         gtk_widget_path_iter_set_object_name(siblings, -1, "button");
283         gtk_widget_path_append_type(siblings, GTK_TYPE_BUTTON);
284         gtk_widget_path_iter_set_object_name(siblings, -1, "button");
285         gtk_widget_path_append_type(siblings, GTK_TYPE_BUTTON);
286         gtk_widget_path_iter_set_object_name(siblings, -1, "button");
287         gtk_widget_path_append_with_siblings(path, siblings, pos);
288         gtk_widget_path_unref(siblings);
289         gtk_style_context_set_path(sc, path);
290         gtk_style_context_set_parent(sc, parent);
291         g_object_unref(parent);
292         gtk_widget_path_unref(path);
293 
294         gtk_style_context_set_state(sc, stateTypeToFlags[state]);
295         gtk_render_background(sc, cr, rect.x - x_diff, rect.y, rect.width, rect.height);
296         gtk_render_frame(sc, cr, rect.x - x_diff, rect.y, rect.width, rect.height);
297 
298         g_object_unref(sc);
299     }
300     else
301 #endif
302     {
303         GtkStyleContext* sc = gtk_widget_get_style_context(button);
304         gtk_style_context_save(sc);
305         gtk_style_context_set_state(sc, stateTypeToFlags[state]);
306         gtk_render_background(sc, cr, rect.x - x_diff, rect.y, rect.width, rect.height);
307         gtk_render_frame(sc, cr, rect.x - x_diff, rect.y, rect.width, rect.height);
308         gtk_style_context_restore(sc);
309     }
310 #else
311     GdkWindow* gdk_window = wxGetGTKDrawable(win, dc);
312     gtk_paint_box
313     (
314         gtk_widget_get_style(button),
315         gdk_window,
316         state,
317         GTK_SHADOW_OUT,
318         NULL,
319         button,
320         "button",
321         dc.LogicalToDeviceX(rect.x) - x_diff, rect.y, rect.width, rect.height
322     );
323 #endif
324 
325     return DrawHeaderButtonContents(win, dc, rect, flags, sortArrow, params);
326 }
327 
GetHeaderButtonHeight(wxWindow * WXUNUSED (win))328 int wxRendererGTK::GetHeaderButtonHeight(wxWindow *WXUNUSED(win))
329 {
330     GtkWidget *button = wxGTKPrivate::GetHeaderButtonWidget();
331 
332     GtkRequisition req;
333 #ifdef __WXGTK3__
334     gtk_widget_get_preferred_height(button, NULL, &req.height);
335 #else
336     GTK_WIDGET_GET_CLASS(button)->size_request(button, &req);
337 #endif
338 
339     return req.height;
340 }
341 
GetHeaderButtonMargin(wxWindow * WXUNUSED (win))342 int wxRendererGTK::GetHeaderButtonMargin(wxWindow *WXUNUSED(win))
343 {
344     wxFAIL_MSG( "GetHeaderButtonMargin() not implemented" );
345     return -1;
346 }
347 
348 
349 // draw a ">" or "v" button
350 void
DrawTreeItemButton(wxWindow * win,wxDC & dc,const wxRect & rect,int flags)351 wxRendererGTK::DrawTreeItemButton(wxWindow* win,
352                                   wxDC& dc, const wxRect& rect, int flags)
353 {
354     wxGTKDrawable* drawable = wxGetGTKDrawable(win, dc);
355     if (drawable == NULL)
356         return;
357 
358     GtkWidget *tree = wxGTKPrivate::GetTreeWidget();
359 
360     int x_diff = 0;
361     if (win->GetLayoutDirection() == wxLayout_RightToLeft)
362         x_diff = rect.width;
363 
364 #ifdef __WXGTK3__
365     int state = GTK_STATE_FLAG_NORMAL;
366     if (flags & wxCONTROL_EXPANDED)
367     {
368         state = GTK_STATE_FLAG_ACTIVE;
369         if (gtk_check_version(3,14,0) == NULL)
370             state = GTK_STATE_FLAG_CHECKED;
371     }
372     if (flags & wxCONTROL_CURRENT)
373         state |= GTK_STATE_FLAG_PRELIGHT;
374 
375     int expander_size;
376     gtk_widget_style_get(tree, "expander-size", &expander_size, NULL);
377     // +1 to match GtkTreeView behavior
378     expander_size++;
379     const int x = rect.x + (rect.width - expander_size) / 2;
380     const int y = rect.y + (rect.width - expander_size) / 2;
381 
382     GtkStyleContext* sc = gtk_widget_get_style_context(tree);
383     gtk_style_context_save(sc);
384     gtk_style_context_set_state(sc, GtkStateFlags(state));
385     gtk_style_context_add_class(sc, GTK_STYLE_CLASS_EXPANDER);
386     gtk_render_expander(sc, drawable, x - x_diff, y, expander_size, expander_size);
387     gtk_style_context_restore(sc);
388 #else
389     GtkStateType state;
390     if ( flags & wxCONTROL_CURRENT )
391         state = GTK_STATE_PRELIGHT;
392     else
393         state = GTK_STATE_NORMAL;
394 
395     // x and y parameters specify the center of the expander
396     gtk_paint_expander
397     (
398         gtk_widget_get_style(tree),
399         drawable,
400         state,
401         NULL,
402         tree,
403         "treeview",
404         dc.LogicalToDeviceX(rect.x) + rect.width / 2 - x_diff,
405         dc.LogicalToDeviceY(rect.y) + rect.height / 2,
406         flags & wxCONTROL_EXPANDED ? GTK_EXPANDER_EXPANDED
407                                    : GTK_EXPANDER_COLLAPSED
408     );
409 #endif
410 }
411 
412 
413 // ----------------------------------------------------------------------------
414 // splitter sash drawing
415 // ----------------------------------------------------------------------------
416 
GetGtkSplitterFullSize(GtkWidget * widget)417 static int GetGtkSplitterFullSize(GtkWidget* widget)
418 {
419     gint handle_size;
420     gtk_widget_style_get(widget, "handle_size", &handle_size, NULL);
421     // Narrow handles don't work well with wxSplitterWindow
422     if (handle_size < 5)
423         handle_size = 5;
424 
425     return handle_size;
426 }
427 
428 wxSplitterRenderParams
GetSplitterParams(const wxWindow * WXUNUSED (win))429 wxRendererGTK::GetSplitterParams(const wxWindow *WXUNUSED(win))
430 {
431     // we don't draw any border, hence 0 for the second field
432     return wxSplitterRenderParams
433            (
434                GetGtkSplitterFullSize(wxGTKPrivate::GetSplitterWidget()),
435                0,
436                true     // hot sensitive
437            );
438 }
439 
440 void
DrawSplitterBorder(wxWindow * WXUNUSED (win),wxDC & WXUNUSED (dc),const wxRect & WXUNUSED (rect),int WXUNUSED (flags))441 wxRendererGTK::DrawSplitterBorder(wxWindow * WXUNUSED(win),
442                                   wxDC& WXUNUSED(dc),
443                                   const wxRect& WXUNUSED(rect),
444                                   int WXUNUSED(flags))
445 {
446     // nothing to do
447 }
448 
449 void
DrawSplitterSash(wxWindow * win,wxDC & dc,const wxSize & size,wxCoord position,wxOrientation orient,int flags)450 wxRendererGTK::DrawSplitterSash(wxWindow* win,
451                                 wxDC& dc,
452                                 const wxSize& size,
453                                 wxCoord position,
454                                 wxOrientation orient,
455                                 int flags)
456 {
457     if (gtk_widget_get_window(win->m_wxwindow) == NULL)
458     {
459         // window not realized yet
460         return;
461     }
462 
463     wxGTKDrawable* drawable = wxGetGTKDrawable(win, dc);
464     if (drawable == NULL)
465         return;
466 
467     // are we drawing vertical or horizontal splitter?
468     const bool isVert = orient == wxVERTICAL;
469 
470     GtkWidget* widget = wxGTKPrivate::GetSplitterWidget(orient);
471     const int full_size = GetGtkSplitterFullSize(widget);
472 
473     GdkRectangle rect;
474 
475     if ( isVert )
476     {
477         rect.x = position;
478         rect.y = 0;
479         rect.width = full_size;
480         rect.height = size.y;
481     }
482     else // horz
483     {
484         rect.x = 0;
485         rect.y = position;
486         rect.height = full_size;
487         rect.width = size.x;
488     }
489 
490     int x_diff = 0;
491     if (win->GetLayoutDirection() == wxLayout_RightToLeft)
492         x_diff = rect.width;
493 
494 #ifdef __WXGTK3__
495     GtkWidgetPath* path = gtk_widget_path_new();
496     GtkStyleContext* sc = gtk_style_context_new();
497     GtkStyleContext* sc1 = NULL;
498     gtk_widget_path_append_type(path, GTK_TYPE_PANED);
499 #if GTK_CHECK_VERSION(3,20,0)
500     if (gtk_check_version(3,20,0) == NULL)
501     {
502         gtk_widget_path_iter_set_object_name(path, -1, "paned");
503         sc1 = gtk_style_context_new();
504         gtk_style_context_set_path(sc1, path);
505         gtk_widget_path_append_type(path, G_TYPE_NONE);
506         gtk_widget_path_iter_set_object_name(path, -1, "separator");
507         gtk_style_context_set_path(sc, path);
508         gtk_style_context_set_parent(sc, sc1);
509     }
510     else
511 #endif
512     {
513         gtk_widget_path_iter_add_class(path, -1, GTK_STYLE_CLASS_PANE_SEPARATOR);
514         gtk_style_context_set_path(sc, path);
515     }
516 
517     gtk_style_context_set_state(sc,
518         flags & wxCONTROL_CURRENT ? GTK_STATE_FLAG_PRELIGHT : GTK_STATE_FLAG_NORMAL);
519     gtk_render_handle(sc, drawable, rect.x - x_diff, rect.y, rect.width, rect.height);
520 
521     gtk_widget_path_unref(path);
522     g_object_unref(sc);
523     if (sc1)
524         g_object_unref(sc1);
525 #else
526     GdkWindow* gdk_window = wxGetGTKDrawable(win, dc);
527     if (gdk_window == NULL)
528         return;
529     gtk_paint_handle
530     (
531         gtk_widget_get_style(win->m_wxwindow),
532         gdk_window,
533         flags & wxCONTROL_CURRENT ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
534         GTK_SHADOW_NONE,
535         NULL /* no clipping */,
536         win->m_wxwindow,
537         "paned",
538         dc.LogicalToDeviceX(rect.x) - x_diff,
539         dc.LogicalToDeviceY(rect.y),
540         rect.width,
541         rect.height,
542         isVert ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL
543     );
544 #endif
545 }
546 
547 void
DrawDropArrow(wxWindow * win,wxDC & dc,const wxRect & rect,int flags)548 wxRendererGTK::DrawDropArrow(wxWindow* win,
549                              wxDC& dc,
550                              const wxRect& rect,
551                              int flags)
552 {
553     GtkWidget *button = wxGTKPrivate::GetButtonWidget();
554 
555     // If we give WX_PIZZA(win->m_wxwindow)->bin_window as
556     // a window for gtk_paint_xxx function, then it won't
557     // work for wxMemoryDC. So that is why we assume wxDC
558     // is wxWindowDC (wxClientDC, wxMemoryDC and wxPaintDC
559     // are derived from it) and use its m_window.
560 
561     // draw arrow so that there is even space horizontally
562     // on both sides
563     const int size = rect.width / 2;
564     const int x = rect.x + (size + 1) / 2;
565     const int y = rect.y + (rect.height - size + 1) / 2;
566 
567     GtkStateType state;
568 
569     if ( flags & wxCONTROL_PRESSED )
570         state = GTK_STATE_ACTIVE;
571     else if ( flags & wxCONTROL_DISABLED )
572         state = GTK_STATE_INSENSITIVE;
573     else if ( flags & wxCONTROL_CURRENT )
574         state = GTK_STATE_PRELIGHT;
575     else
576         state = GTK_STATE_NORMAL;
577 
578 #ifdef __WXGTK3__
579     cairo_t* cr = wxGetGTKDrawable(win, dc);
580     if (cr)
581     {
582         gtk_widget_set_state_flags(button, stateTypeToFlags[state], true);
583         GtkStyleContext* sc = gtk_widget_get_style_context(button);
584         gtk_render_arrow(sc, cr, G_PI, x, y, size);
585     }
586 #else
587     GdkWindow* gdk_window = wxGetGTKDrawable(win, dc);
588     if (gdk_window == NULL)
589         return;
590     // draw arrow on button
591     gtk_paint_arrow
592     (
593         gtk_widget_get_style(button),
594         gdk_window,
595         state,
596         flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
597         NULL,
598         button,
599         "arrow",
600         GTK_ARROW_DOWN,
601         FALSE,
602         x, y,
603         size, size
604     );
605 #endif
606 }
607 
608 void
DrawComboBoxDropButton(wxWindow * win,wxDC & dc,const wxRect & rect,int flags)609 wxRendererGTK::DrawComboBoxDropButton(wxWindow *win,
610                                       wxDC& dc,
611                                       const wxRect& rect,
612                                       int flags)
613 {
614     DrawPushButton(win,dc,rect,flags);
615     DrawDropArrow(win,dc,rect);
616 }
617 
618 wxSize
GetCheckBoxSize(wxWindow * WXUNUSED (win))619 wxRendererGTK::GetCheckBoxSize(wxWindow *WXUNUSED(win))
620 {
621 #ifdef __WXGTK3__
622     int min_width, min_height;
623     GtkWidgetPath* path = gtk_widget_path_new();
624     GtkStyleContext* sc = gtk_style_context_new();
625     GtkStyleContext* sc1 = NULL;
626     gtk_widget_path_append_type(path, GTK_TYPE_CHECK_BUTTON);
627 #if GTK_CHECK_VERSION(3,20,0)
628     if (gtk_check_version(3,20,0) == NULL)
629     {
630         gtk_widget_path_iter_set_object_name(path, -1, "checkbutton");
631         sc1 = gtk_style_context_new();
632         gtk_style_context_set_path(sc1, path);
633         gtk_widget_path_append_type(path, G_TYPE_NONE);
634         gtk_widget_path_iter_set_object_name(path, -1, "check");
635         gtk_style_context_set_path(sc, path);
636         gtk_style_context_set_parent(sc, sc1);
637         gtk_style_context_get(sc, GTK_STATE_FLAG_NORMAL,
638             "min-width", &min_width, "min-height", &min_height, NULL);
639         GtkBorder margin;
640         gtk_style_context_get_margin(sc, GTK_STATE_FLAG_NORMAL, &margin);
641         min_width += margin.left + margin.right;
642         min_height += margin.top + margin.bottom;
643     }
644     else
645 #endif
646     {
647         gtk_style_context_set_path(sc, path);
648         GValue value = G_VALUE_INIT;
649         g_value_init(&value, G_TYPE_INT);
650         gtk_style_context_get_style_property(sc, "indicator-size", &value);
651         min_width = g_value_get_int(&value);
652         gtk_style_context_get_style_property(sc, "indicator-spacing", &value);
653         min_width += 2 * g_value_get_int(&value);
654         min_height = min_width;
655         g_value_unset(&value);
656     }
657     gtk_widget_path_unref(path);
658     g_object_unref(sc);
659     if (sc1)
660         g_object_unref(sc1);
661 
662     return wxSize(min_width, min_height);
663 #else // !__WXGTK3__
664     gint indicator_size, indicator_spacing;
665     gtk_widget_style_get(wxGTKPrivate::GetCheckButtonWidget(),
666                          "indicator_size", &indicator_size,
667                          "indicator_spacing", &indicator_spacing,
668                          NULL);
669 
670     int size = indicator_size + indicator_spacing * 2;
671     return wxSize(size, size);
672 #endif // !__WXGTK3__
673 }
674 
675 void
DrawCheckBox(wxWindow * win,wxDC & dc,const wxRect & rect,int flags)676 wxRendererGTK::DrawCheckBox(wxWindow* win,
677                             wxDC& dc,
678                             const wxRect& rect,
679                             int flags )
680 {
681 #ifndef __WXGTK3__
682     GtkWidget *button = wxGTKPrivate::GetCheckButtonWidget();
683 
684     gint indicator_size, indicator_spacing;
685     gtk_widget_style_get(button,
686                          "indicator_size", &indicator_size,
687                          "indicator_spacing", &indicator_spacing,
688                          NULL);
689 
690     GtkStateType state;
691 
692     if ( flags & wxCONTROL_PRESSED )
693         state = GTK_STATE_ACTIVE;
694     else if ( flags & wxCONTROL_DISABLED )
695         state = GTK_STATE_INSENSITIVE;
696     else if ( flags & wxCONTROL_CURRENT )
697         state = GTK_STATE_PRELIGHT;
698     else
699         state = GTK_STATE_NORMAL;
700 
701     GtkShadowType shadow_type;
702 
703     if ( flags & wxCONTROL_UNDETERMINED )
704         shadow_type = GTK_SHADOW_ETCHED_IN;
705     else if ( flags & wxCONTROL_CHECKED )
706         shadow_type = GTK_SHADOW_IN;
707     else
708         shadow_type = GTK_SHADOW_OUT;
709 #endif
710 
711 #ifdef __WXGTK3__
712     cairo_t* cr = wxGetGTKDrawable(win, dc);
713     if (cr == NULL)
714         return;
715 
716     int state = GTK_STATE_FLAG_NORMAL;
717     if (flags & wxCONTROL_CHECKED)
718     {
719         state = GTK_STATE_FLAG_ACTIVE;
720         if (gtk_check_version(3,14,0) == NULL)
721             state = GTK_STATE_FLAG_CHECKED;
722     }
723     if (flags & wxCONTROL_DISABLED)
724         state |= GTK_STATE_FLAG_INSENSITIVE;
725     if (flags & wxCONTROL_UNDETERMINED)
726         state |= GTK_STATE_FLAG_INCONSISTENT;
727     if (flags & wxCONTROL_CURRENT)
728         state |= GTK_STATE_FLAG_PRELIGHT;
729 
730     int min_width, min_height;
731     GtkWidgetPath* path = gtk_widget_path_new();
732     GtkStyleContext* sc = gtk_style_context_new();
733     GtkStyleContext* sc1 = NULL;
734     gtk_widget_path_append_type(path, GTK_TYPE_CHECK_BUTTON);
735 #if GTK_CHECK_VERSION(3,20,0)
736     if (gtk_check_version(3,20,0) == NULL)
737     {
738         gtk_widget_path_iter_set_object_name(path, -1, "checkbutton");
739         sc1 = gtk_style_context_new();
740         gtk_style_context_set_path(sc1, path);
741         gtk_widget_path_append_type(path, G_TYPE_NONE);
742         gtk_widget_path_iter_set_object_name(path, -1, "check");
743         gtk_style_context_set_path(sc, path);
744         gtk_style_context_set_parent(sc, sc1);
745         gtk_style_context_get(sc, GTK_STATE_FLAG_NORMAL,
746             "min-width", &min_width, "min-height", &min_height, NULL);
747     }
748     else
749 #endif
750     {
751         gtk_style_context_set_path(sc, path);
752         GValue value = G_VALUE_INIT;
753         g_value_init(&value, G_TYPE_INT);
754         gtk_style_context_get_style_property(sc, "indicator-size", &value);
755         min_width = g_value_get_int(&value);
756         min_height = min_width;
757         g_value_unset(&value);
758     }
759 
760     // need save/restore for GTK+ 3.6 & 3.8
761     gtk_style_context_save(sc);
762     gtk_style_context_set_state(sc, GtkStateFlags(state));
763     const int x = rect.x + (rect.width - min_width) / 2;
764     const int y = rect.y + (rect.height - min_height) / 2;
765     gtk_render_background(sc, cr, x, y, min_width, min_height);
766     gtk_render_frame(sc, cr, x, y, min_width, min_height);
767     gtk_style_context_add_class(sc, "check");
768     gtk_render_check(sc, cr, x, y, min_width, min_height);
769     gtk_style_context_restore(sc);
770 
771     gtk_widget_path_unref(path);
772     g_object_unref(sc);
773     if (sc1)
774         g_object_unref(sc1);
775 #else
776     GdkWindow* gdk_window = wxGetGTKDrawable(win, dc);
777     if (gdk_window == NULL)
778         return;
779 
780     gtk_paint_check
781     (
782         gtk_widget_get_style(button),
783         gdk_window,
784         state,
785         shadow_type,
786         NULL,
787         button,
788         "cellcheck",
789         dc.LogicalToDeviceX(rect.x) + indicator_spacing,
790         dc.LogicalToDeviceY(rect.y) + indicator_spacing,
791         indicator_size, indicator_size
792     );
793 #endif
794 }
795 
796 void
DrawPushButton(wxWindow * win,wxDC & dc,const wxRect & rect,int flags)797 wxRendererGTK::DrawPushButton(wxWindow* win,
798                               wxDC& dc,
799                               const wxRect& rect,
800                               int flags)
801 {
802     GtkWidget *button = wxGTKPrivate::GetButtonWidget();
803 
804     // draw button
805     GtkStateType state;
806 
807     if ( flags & wxCONTROL_PRESSED )
808         state = GTK_STATE_ACTIVE;
809     else if ( flags & wxCONTROL_DISABLED )
810         state = GTK_STATE_INSENSITIVE;
811     else if ( flags & wxCONTROL_CURRENT )
812         state = GTK_STATE_PRELIGHT;
813     else
814         state = GTK_STATE_NORMAL;
815 
816 #ifdef __WXGTK3__
817     cairo_t* cr = wxGetGTKDrawable(win, dc);
818     if (cr)
819     {
820         GtkStyleContext* sc = gtk_widget_get_style_context(button);
821         gtk_style_context_save(sc);
822         gtk_style_context_set_state(sc, stateTypeToFlags[state]);
823         gtk_render_background(sc, cr, rect.x, rect.y, rect.width, rect.height);
824         gtk_render_frame(sc, cr, rect.x, rect.y, rect.width, rect.height);
825         gtk_style_context_restore(sc);
826     }
827 #else
828     GdkWindow* gdk_window = wxGetGTKDrawable(win, dc);
829     if (gdk_window == NULL)
830         return;
831 
832     gtk_paint_box
833     (
834         gtk_widget_get_style(button),
835         gdk_window,
836         state,
837         flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
838         NULL,
839         button,
840         "button",
841         dc.LogicalToDeviceX(rect.x),
842         dc.LogicalToDeviceY(rect.y),
843         rect.width,
844         rect.height
845     );
846 #endif
847 }
848 
849 void
DrawItemSelectionRect(wxWindow * win,wxDC & dc,const wxRect & rect,int flags)850 wxRendererGTK::DrawItemSelectionRect(wxWindow* win,
851                                      wxDC& dc,
852                                      const wxRect& rect,
853                                      int flags )
854 {
855     wxGTKDrawable* drawable = wxGetGTKDrawable(win, dc);
856     if (drawable == NULL)
857         return;
858 
859     if (flags & wxCONTROL_SELECTED)
860     {
861         int x_diff = 0;
862         if (win->GetLayoutDirection() == wxLayout_RightToLeft)
863             x_diff = rect.width;
864 
865         GtkWidget* treeWidget = wxGTKPrivate::GetTreeWidget();
866 
867 #ifdef __WXGTK3__
868         GtkStyleContext* sc = gtk_widget_get_style_context(treeWidget);
869         gtk_style_context_save(sc);
870         int state = GTK_STATE_FLAG_SELECTED;
871         if (flags & wxCONTROL_FOCUSED)
872             state |= GTK_STATE_FLAG_FOCUSED;
873         gtk_style_context_set_state(sc, GtkStateFlags(state));
874         gtk_style_context_add_class(sc, GTK_STYLE_CLASS_CELL);
875         gtk_render_background(sc, drawable, rect.x - x_diff, rect.y, rect.width, rect.height);
876         gtk_style_context_restore(sc);
877 #else
878         // the wxCONTROL_FOCUSED state is deduced
879         // directly from the m_wxwindow by GTK+
880         gtk_paint_flat_box(gtk_widget_get_style(treeWidget),
881                         drawable,
882                         GTK_STATE_SELECTED,
883                         GTK_SHADOW_NONE,
884                         NULL_RECT
885                         win->m_wxwindow,
886                         "cell_even",
887                         dc.LogicalToDeviceX(rect.x) - x_diff,
888                         dc.LogicalToDeviceY(rect.y),
889                         rect.width,
890                         rect.height );
891 #endif
892     }
893 
894     if ((flags & wxCONTROL_CURRENT) && (flags & wxCONTROL_FOCUSED))
895         DrawFocusRect(win, dc, rect, flags);
896 }
897 
DrawFocusRect(wxWindow * win,wxDC & dc,const wxRect & rect,int flags)898 void wxRendererGTK::DrawFocusRect(wxWindow* win, wxDC& dc, const wxRect& rect, int flags)
899 {
900     wxGTKDrawable* drawable = wxGetGTKDrawable(win, dc);
901     if (drawable == NULL)
902         return;
903 
904     GtkStateType state;
905     if (flags & wxCONTROL_SELECTED)
906         state = GTK_STATE_SELECTED;
907     else
908         state = GTK_STATE_NORMAL;
909 
910 #ifdef __WXGTK3__
911     GtkStyleContext* sc = gtk_widget_get_style_context(win->m_widget);
912     gtk_style_context_save(sc);
913     gtk_style_context_set_state(sc, stateTypeToFlags[state]);
914     gtk_render_focus(sc, drawable, rect.x, rect.y, rect.width, rect.height);
915     gtk_style_context_restore(sc);
916 #else
917     gtk_paint_focus( gtk_widget_get_style(win->m_widget),
918                      drawable,
919                      state,
920                      NULL_RECT
921                      win->m_wxwindow,
922                      NULL,
923                      dc.LogicalToDeviceX(rect.x),
924                      dc.LogicalToDeviceY(rect.y),
925                      rect.width,
926                      rect.height );
927 #endif
928 }
929 
930 // Uses the theme to draw the border and fill for something like a wxTextCtrl
DrawTextCtrl(wxWindow * win,wxDC & dc,const wxRect & rect,int flags)931 void wxRendererGTK::DrawTextCtrl(wxWindow* win, wxDC& dc, const wxRect& rect, int flags)
932 {
933     wxGTKDrawable* drawable = wxGetGTKDrawable(win, dc);
934     if (drawable == NULL)
935         return;
936 
937 #ifdef __WXGTK3__
938     int state = GTK_STATE_FLAG_NORMAL;
939     if (flags & wxCONTROL_FOCUSED)
940         state = GTK_STATE_FLAG_FOCUSED;
941     if (flags & wxCONTROL_DISABLED)
942         state = GTK_STATE_FLAG_INSENSITIVE;
943 
944     GtkWidgetPath* path = gtk_widget_path_new();
945     GtkStyleContext* sc = gtk_style_context_new();
946     gtk_widget_path_append_type(path, GTK_TYPE_ENTRY);
947 #if GTK_CHECK_VERSION(3,20,0)
948     if (gtk_check_version(3,20,0) == NULL)
949         gtk_widget_path_iter_set_object_name(path, -1, "entry");
950 #endif
951     gtk_widget_path_iter_add_class(path, -1, "entry");
952     gtk_style_context_set_path(sc, path);
953 
954     gtk_style_context_set_state(sc, GtkStateFlags(state));
955     gtk_render_background(sc, drawable, rect.x, rect.y, rect.width, rect.height);
956     gtk_render_frame(sc, drawable, rect.x, rect.y, rect.width, rect.height);
957 
958     gtk_widget_path_unref(path);
959     g_object_unref(sc);
960 #else
961     GtkWidget* entry = wxGTKPrivate::GetTextEntryWidget();
962 
963     GtkStateType state = GTK_STATE_NORMAL;
964     if ( flags & wxCONTROL_DISABLED )
965         state = GTK_STATE_INSENSITIVE;
966 
967     gtk_widget_set_can_focus(entry, (flags & wxCONTROL_CURRENT) != 0);
968 
969     gtk_paint_shadow
970     (
971         gtk_widget_get_style(entry),
972         drawable,
973         state,
974         GTK_SHADOW_OUT,
975         NULL_RECT
976         entry,
977         "entry",
978         dc.LogicalToDeviceX(rect.x),
979         dc.LogicalToDeviceY(rect.y),
980         rect.width,
981         rect.height
982   );
983 #endif
984 }
985 
986 // Draw the equivalent of a wxComboBox
DrawComboBox(wxWindow * win,wxDC & dc,const wxRect & rect,int flags)987 void wxRendererGTK::DrawComboBox(wxWindow* win, wxDC& dc, const wxRect& rect, int flags)
988 {
989     wxGTKDrawable* drawable = wxGetGTKDrawable(win, dc);
990     if (drawable == NULL)
991         return;
992 
993     GtkWidget* combo = wxGTKPrivate::GetComboBoxWidget();
994 
995     GtkStateType state = GTK_STATE_NORMAL;
996     if ( flags & wxCONTROL_DISABLED )
997        state = GTK_STATE_INSENSITIVE;
998 
999     gtk_widget_set_can_focus(combo, (flags & wxCONTROL_CURRENT) != 0);
1000 
1001 #ifdef __WXGTK3__
1002     GtkStyleContext* sc = gtk_widget_get_style_context(combo);
1003     gtk_style_context_save(sc);
1004     gtk_style_context_set_state(sc, stateTypeToFlags[state]);
1005     gtk_render_background(sc, drawable, rect.x, rect.y, rect.width, rect.height);
1006     gtk_render_frame(sc, drawable, rect.x, rect.y, rect.width, rect.height);
1007     gtk_style_context_restore(sc);
1008     wxRect r = rect;
1009     r.x += r.width - r.height;
1010     r.width = r.height;
1011     DrawComboBoxDropButton(win, dc, r, flags);
1012 #else
1013     gtk_paint_shadow
1014     (
1015         gtk_widget_get_style(combo),
1016         drawable,
1017         state,
1018         GTK_SHADOW_OUT,
1019         NULL_RECT
1020         combo,
1021         "combobox",
1022         dc.LogicalToDeviceX(rect.x),
1023         dc.LogicalToDeviceY(rect.y),
1024         rect.width,
1025         rect.height
1026     );
1027 
1028     wxRect r = rect;
1029     int extent = rect.height / 2;
1030     r.x += rect.width - extent - extent/2;
1031     r.y += extent/2;
1032     r.width = extent;
1033     r.height = extent;
1034 
1035     gtk_paint_arrow
1036     (
1037         gtk_widget_get_style(combo),
1038         drawable,
1039         state,
1040         GTK_SHADOW_OUT,
1041         NULL_RECT
1042         combo,
1043         "arrow",
1044         GTK_ARROW_DOWN,
1045         TRUE,
1046         dc.LogicalToDeviceX(r.x),
1047         dc.LogicalToDeviceY(r.y),
1048         r.width,
1049         r.height
1050     );
1051 
1052     r = rect;
1053     r.x += rect.width - 2*extent;
1054     r.width = 2;
1055 
1056     gtk_paint_box
1057     (
1058         gtk_widget_get_style(combo),
1059         drawable,
1060         state,
1061         GTK_SHADOW_ETCHED_OUT,
1062         NULL_RECT
1063         combo,
1064         "vseparator",
1065         dc.LogicalToDeviceX(r.x),
1066         dc.LogicalToDeviceY(r.y+1),
1067         r.width,
1068         r.height-2
1069     );
1070 #endif
1071 }
1072 
DrawChoice(wxWindow * win,wxDC & dc,const wxRect & rect,int flags)1073 void wxRendererGTK::DrawChoice(wxWindow* win, wxDC& dc,
1074                            const wxRect& rect, int flags)
1075 {
1076     DrawComboBox( win, dc, rect, flags );
1077 }
1078 
1079 
1080 // Draw a themed radio button
DrawRadioBitmap(wxWindow * win,wxDC & dc,const wxRect & rect,int flags)1081 void wxRendererGTK::DrawRadioBitmap(wxWindow* win, wxDC& dc, const wxRect& rect, int flags)
1082 {
1083     wxGTKDrawable* drawable = wxGetGTKDrawable(win, dc);
1084     if (drawable == NULL)
1085         return;
1086 
1087 #ifdef __WXGTK3__
1088     int state = GTK_STATE_FLAG_NORMAL;
1089     if (flags & wxCONTROL_CHECKED)
1090     {
1091         state = GTK_STATE_FLAG_ACTIVE;
1092         if (gtk_check_version(3,14,0) == NULL)
1093             state = GTK_STATE_FLAG_CHECKED;
1094     }
1095     if (flags & wxCONTROL_DISABLED)
1096         state |= GTK_STATE_FLAG_INSENSITIVE;
1097     if (flags & wxCONTROL_UNDETERMINED)
1098         state |= GTK_STATE_FLAG_INCONSISTENT;
1099     if (flags & wxCONTROL_CURRENT)
1100         state |= GTK_STATE_FLAG_PRELIGHT;
1101 
1102     int min_width, min_height;
1103     GtkWidgetPath* path = gtk_widget_path_new();
1104     GtkStyleContext* sc = gtk_style_context_new();
1105     GtkStyleContext* sc1 = NULL;
1106     gtk_widget_path_append_type(path, GTK_TYPE_RADIO_BUTTON);
1107 #if GTK_CHECK_VERSION(3,20,0)
1108     if (gtk_check_version(3,20,0) == NULL)
1109     {
1110         gtk_widget_path_iter_set_object_name(path, -1, "radiobutton");
1111         sc1 = gtk_style_context_new();
1112         gtk_style_context_set_path(sc1, path);
1113         gtk_widget_path_append_type(path, G_TYPE_NONE);
1114         gtk_widget_path_iter_set_object_name(path, -1, "radio");
1115         gtk_style_context_set_path(sc, path);
1116         gtk_style_context_set_parent(sc, sc1);
1117         gtk_style_context_get(sc, GTK_STATE_FLAG_NORMAL,
1118             "min-width", &min_width, "min-height", &min_height, NULL);
1119     }
1120     else
1121 #endif
1122     {
1123         gtk_style_context_set_path(sc, path);
1124         GValue value = G_VALUE_INIT;
1125         g_value_init(&value, G_TYPE_INT);
1126         gtk_style_context_get_style_property(sc, "indicator-size", &value);
1127         min_width = g_value_get_int(&value);
1128         min_height = min_width;
1129         g_value_unset(&value);
1130     }
1131 
1132     // need save/restore for GTK+ 3.6 & 3.8
1133     gtk_style_context_save(sc);
1134     gtk_style_context_set_state(sc, GtkStateFlags(state));
1135     const int x = rect.x + (rect.width - min_width) / 2;
1136     const int y = rect.y + (rect.height - min_height) / 2;
1137     gtk_render_background(sc, drawable, x, y, min_width, min_height);
1138     gtk_render_frame(sc, drawable, x, y, min_width, min_height);
1139     gtk_style_context_add_class(sc, "radio");
1140     gtk_render_option(sc, drawable, x, y, min_width, min_height);
1141     gtk_style_context_restore(sc);
1142 
1143     gtk_widget_path_unref(path);
1144     g_object_unref(sc);
1145     if (sc1)
1146         g_object_unref(sc1);
1147 #else
1148     GtkWidget* button = wxGTKPrivate::GetRadioButtonWidget();
1149 
1150     GtkShadowType shadow_type = GTK_SHADOW_OUT;
1151     if ( flags & wxCONTROL_CHECKED )
1152         shadow_type = GTK_SHADOW_IN;
1153     else if ( flags & wxCONTROL_UNDETERMINED )
1154         shadow_type = GTK_SHADOW_ETCHED_IN;
1155 
1156     GtkStateType state = GTK_STATE_NORMAL;
1157     if ( flags & wxCONTROL_DISABLED )
1158         state = GTK_STATE_INSENSITIVE;
1159     if ( flags & wxCONTROL_PRESSED )
1160         state = GTK_STATE_ACTIVE;
1161 /*
1162     Don't know when to set this
1163        state_type = GTK_STATE_PRELIGHT;
1164 */
1165 
1166     gtk_paint_option
1167     (
1168         gtk_widget_get_style(button),
1169         drawable,
1170         state,
1171         shadow_type,
1172         NULL_RECT
1173         button,
1174         "radiobutton",
1175         dc.LogicalToDeviceX(rect.x),
1176         dc.LogicalToDeviceY(rect.y),
1177         rect.width, rect.height
1178     );
1179 #endif
1180 }
1181