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