1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/settings.cpp
3 // Purpose:
4 // Author:      Robert Roebling
5 // Modified by: Mart Raudsepp (GetMetric)
6 // Copyright:   (c) 1998 Robert Roebling
7 // Licence:     wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9 
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12 
13 #include "wx/settings.h"
14 
15 #ifndef WX_PRECOMP
16     #include "wx/toplevel.h"
17     #include "wx/module.h"
18 #endif
19 
20 #include "wx/fontutil.h"
21 #include "wx/fontenum.h"
22 
23 #include <gtk/gtk.h>
24 #include "wx/gtk/private/win_gtk.h"
25 #include "wx/gtk/private/gtk2-compat.h"
26 
27 bool wxGetFrameExtents(GdkWindow* window, int* left, int* right, int* top, int* bottom);
28 
29 // ----------------------------------------------------------------------------
30 // wxSystemSettings implementation
31 // ----------------------------------------------------------------------------
32 
33 static wxFont gs_fontSystem;
34 static int gs_scrollWidth;
35 static GtkWidget* gs_tlw_parent;
36 
ContainerWidget()37 static GtkContainer* ContainerWidget()
38 {
39     static GtkContainer* s_widget;
40     if (s_widget == NULL)
41     {
42         s_widget = GTK_CONTAINER(gtk_fixed_new());
43         g_object_add_weak_pointer(G_OBJECT(s_widget), (void**)&s_widget);
44         gs_tlw_parent = gtk_window_new(GTK_WINDOW_TOPLEVEL);
45         gtk_container_add(GTK_CONTAINER(gs_tlw_parent), GTK_WIDGET(s_widget));
46     }
47     return s_widget;
48 }
49 
ScrollBarWidget()50 static GtkWidget* ScrollBarWidget()
51 {
52     static GtkWidget* s_widget;
53     if (s_widget == NULL)
54     {
55         s_widget = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, NULL);
56         g_object_add_weak_pointer(G_OBJECT(s_widget), (void**)&s_widget);
57         gtk_container_add(ContainerWidget(), s_widget);
58 #ifndef __WXGTK3__
59         gtk_widget_ensure_style(s_widget);
60 #endif
61     }
62     return s_widget;
63 }
64 
65 #ifndef __WXGTK3__
66 
67 extern "C" {
style_set(GtkWidget *,GtkStyle *,void *)68 static void style_set(GtkWidget*, GtkStyle*, void*)
69 {
70     gs_fontSystem = wxNullFont;
71     gs_scrollWidth = 0;
72 }
73 }
74 
ButtonWidget()75 static GtkWidget* ButtonWidget()
76 {
77     static GtkWidget* s_widget;
78     if (s_widget == NULL)
79     {
80         s_widget = gtk_button_new();
81         g_object_add_weak_pointer(G_OBJECT(s_widget), (void**)&s_widget);
82         gtk_container_add(ContainerWidget(), s_widget);
83         gtk_widget_ensure_style(s_widget);
84         g_signal_connect(s_widget, "style_set", G_CALLBACK(style_set), NULL);
85     }
86     return s_widget;
87 }
88 
ListWidget()89 static GtkWidget* ListWidget()
90 {
91     static GtkWidget* s_widget;
92     if (s_widget == NULL)
93     {
94         s_widget = gtk_tree_view_new_with_model(
95             GTK_TREE_MODEL(gtk_list_store_new(1, G_TYPE_INT)));
96         g_object_add_weak_pointer(G_OBJECT(s_widget), (void**)&s_widget);
97         gtk_container_add(ContainerWidget(), s_widget);
98         gtk_widget_ensure_style(s_widget);
99     }
100     return s_widget;
101 }
102 
TextCtrlWidget()103 static GtkWidget* TextCtrlWidget()
104 {
105     static GtkWidget* s_widget;
106     if (s_widget == NULL)
107     {
108         s_widget = gtk_text_view_new();
109         g_object_add_weak_pointer(G_OBJECT(s_widget), (void**)&s_widget);
110         gtk_container_add(ContainerWidget(), s_widget);
111         gtk_widget_ensure_style(s_widget);
112     }
113     return s_widget;
114 }
115 
MenuItemWidget()116 static GtkWidget* MenuItemWidget()
117 {
118     static GtkWidget* s_widget;
119     if (s_widget == NULL)
120     {
121         s_widget = gtk_menu_item_new();
122         g_object_add_weak_pointer(G_OBJECT(s_widget), (void**)&s_widget);
123         gtk_container_add(ContainerWidget(), s_widget);
124         gtk_widget_ensure_style(s_widget);
125     }
126     return s_widget;
127 }
128 
MenuBarWidget()129 static GtkWidget* MenuBarWidget()
130 {
131     static GtkWidget* s_widget;
132     if (s_widget == NULL)
133     {
134         s_widget = gtk_menu_bar_new();
135         g_object_add_weak_pointer(G_OBJECT(s_widget), (void**)&s_widget);
136         gtk_container_add(ContainerWidget(), s_widget);
137         gtk_widget_ensure_style(s_widget);
138     }
139     return s_widget;
140 }
141 
ToolTipWidget()142 static GtkWidget* ToolTipWidget()
143 {
144     static GtkWidget* s_widget;
145     if (s_widget == NULL)
146     {
147         s_widget = gtk_window_new(GTK_WINDOW_POPUP);
148         g_object_add_weak_pointer(G_OBJECT(s_widget), (void**)&s_widget);
149         g_signal_connect_swapped(ContainerWidget(), "destroy",
150             G_CALLBACK(gtk_widget_destroy), s_widget);
151         const char* name = "gtk-tooltip";
152         if (gtk_check_version(2, 11, 0))
153             name = "gtk-tooltips";
154         gtk_widget_set_name(s_widget, name);
155         gtk_widget_ensure_style(s_widget);
156     }
157     return s_widget;
158 }
159 #endif // !__WXGTK3__
160 
161 #ifdef __WXGTK3__
162 
163 #if !GTK_CHECK_VERSION(3,12,0)
164     #define GTK_STATE_FLAG_LINK (1 << 9)
165 #endif
166 
167 static wxColour gs_systemColorCache[wxSYS_COLOUR_MAX + 1];
168 
169 extern "C" {
notify_gtk_theme_name(GObject *,GParamSpec *,void *)170 static void notify_gtk_theme_name(GObject*, GParamSpec*, void*)
171 {
172     gs_fontSystem.UnRef();
173     gs_scrollWidth = 0;
174     for (int i = wxSYS_COLOUR_MAX; i--;)
175         gs_systemColorCache[i].UnRef();
176 }
177 
notify_gtk_font_name(GObject *,GParamSpec *,void *)178 static void notify_gtk_font_name(GObject*, GParamSpec*, void*)
179 {
180     gs_fontSystem.UnRef();
181 }
182 }
183 
184 // Some notes on using GtkStyleContext. Style information from a context
185 // attached to a non-visible GtkWidget is not accurate. The context has an
186 // internal visibility state, controlled by the widget, which it presumably
187 // uses to avoid doing unnecessary work. Creating a new style context from the
188 // GtkWidgetPath in a context attached to a widget also does not work. The path
189 // does not accurately reproduce the context state with older versions of GTK+,
190 // and there is no context hierarchy (parent contexts). The hierarchy of parent
191 // contexts is necessary, even though it would seem that the widget path has
192 // the same hierarchy in it. So the best way to get style information seems
193 // to be creating the widget paths and context hierarchy directly.
194 
StyleContext(GtkStyleContext * parent,GtkWidgetPath * path,GType type,const char * objectName,const char * className1=NULL,const char * className2=NULL)195 static GtkStyleContext* StyleContext(
196     GtkStyleContext* parent,
197     GtkWidgetPath* path,
198     GType type,
199     const char* objectName,
200     const char* className1 = NULL,
201     const char* className2 = NULL)
202 {
203     gtk_widget_path_append_type(path, type);
204 #if GTK_CHECK_VERSION(3,20,0)
205     if (gtk_check_version(3,20,0) == NULL)
206         gtk_widget_path_iter_set_object_name(path, -1, objectName);
207 #endif
208     if (className1)
209         gtk_widget_path_iter_add_class(path, -1, className1);
210     if (className2)
211         gtk_widget_path_iter_add_class(path, -1, className2);
212     GtkStyleContext* sc = gtk_style_context_new();
213     gtk_style_context_set_path(sc, path);
214     if (parent)
215     {
216 #if GTK_CHECK_VERSION(3,4,0)
217         if (gtk_check_version(3,4,0) == NULL)
218             gtk_style_context_set_parent(sc, parent);
219 #endif
220         g_object_unref(parent);
221     }
222     return sc;
223 }
224 
StyleContext(GtkWidgetPath * path,GType type,const char * objectName,const char * className1=NULL,const char * className2=NULL)225 static GtkStyleContext* StyleContext(
226     GtkWidgetPath* path,
227     GType type,
228     const char* objectName,
229     const char* className1 = NULL,
230     const char* className2 = NULL)
231 {
232     GtkStyleContext* sc;
233     sc = StyleContext(NULL, path, GTK_TYPE_WINDOW, "window", "background");
234     sc = StyleContext(sc, path, type, objectName, className1, className2);
235     return sc;
236 }
237 
StyleContextFree(GtkStyleContext * sc)238 static void StyleContextFree(GtkStyleContext* sc)
239 {
240     if (gtk_check_version(3,16,0) == NULL || gtk_check_version(3,4,0))
241     {
242         g_object_unref(sc);
243         return;
244     }
245 #if GTK_CHECK_VERSION(3,4,0)
246     // GTK+ < 3.16 does not properly handle freeing child context before parent
247     do {
248         GtkStyleContext* parent = gtk_style_context_get_parent(sc);
249         if (parent)
250         {
251             g_object_ref(parent);
252             gtk_style_context_set_parent(sc, NULL);
253         }
254         g_object_unref(sc);
255         sc = parent;
256     } while (sc);
257 #endif
258 }
259 
ButtonContext(GtkWidgetPath * path)260 static GtkStyleContext* ButtonContext(GtkWidgetPath* path)
261 {
262     GtkStyleContext* sc;
263     sc = StyleContext(path, GTK_TYPE_BUTTON, "button", "button");
264     return sc;
265 }
266 
ButtonLabelContext(GtkWidgetPath * path)267 static GtkStyleContext* ButtonLabelContext(GtkWidgetPath* path)
268 {
269     GtkStyleContext* sc;
270     sc = ButtonContext(path);
271     sc = StyleContext(sc, path, GTK_TYPE_LABEL, "label");
272     return sc;
273 }
274 
HeaderbarContext(GtkWidgetPath * path)275 static GtkStyleContext* HeaderbarContext(GtkWidgetPath* path)
276 {
277     GtkStyleContext* sc;
278     sc = StyleContext(path, GTK_TYPE_HEADER_BAR, "headerbar", "titlebar", "header-bar");
279     return sc;
280 }
281 
HeaderbarLabelContext(GtkWidgetPath * path)282 static GtkStyleContext* HeaderbarLabelContext(GtkWidgetPath* path)
283 {
284     GtkStyleContext* sc;
285     sc = HeaderbarContext(path);
286     sc = StyleContext(sc, path, GTK_TYPE_LABEL, "label");
287     return sc;
288 }
289 
MenuContext(GtkWidgetPath * path)290 static GtkStyleContext* MenuContext(GtkWidgetPath* path)
291 {
292     GtkStyleContext* sc;
293     sc = StyleContext(NULL, path, GTK_TYPE_WINDOW, "window", "background", "popup");
294     sc = StyleContext(sc, path, GTK_TYPE_MENU, "menu", "menu");
295     return sc;
296 }
297 
MenuItemContext(GtkWidgetPath * path)298 static GtkStyleContext* MenuItemContext(GtkWidgetPath* path)
299 {
300     GtkStyleContext* sc;
301     sc = MenuContext(path);
302     sc = StyleContext(sc, path, GTK_TYPE_MENU_ITEM, "menuitem", "menuitem");
303     return sc;
304 }
305 
TextviewContext(GtkWidgetPath * path,const char * child1=NULL,const char * child2=NULL)306 static GtkStyleContext* TextviewContext(GtkWidgetPath* path, const char* child1 = NULL, const char* child2 = NULL)
307 {
308     GtkStyleContext* sc;
309     sc = StyleContext(path, GTK_TYPE_TEXT_VIEW, "textview", "view");
310     if (child1 && gtk_check_version(3,20,0) == NULL)
311     {
312         sc = StyleContext(sc, path, G_TYPE_NONE, child1);
313         if (child2)
314             sc = StyleContext(sc, path, G_TYPE_NONE, child2);
315     }
316     return sc;
317 }
318 
TreeviewContext(GtkWidgetPath * path)319 static GtkStyleContext* TreeviewContext(GtkWidgetPath* path)
320 {
321     GtkStyleContext* sc;
322     sc = StyleContext(path, GTK_TYPE_TREE_VIEW, "treeview", "view");
323     return sc;
324 }
325 
TooltipContext(GtkWidgetPath * path)326 static GtkStyleContext* TooltipContext(GtkWidgetPath* path)
327 {
328     gtk_widget_path_append_type(path, GTK_TYPE_WINDOW);
329 #if GTK_CHECK_VERSION(3,20,0)
330     if (gtk_check_version(3,20,0) == NULL)
331         gtk_widget_path_iter_set_object_name(path, -1, "tooltip");
332 #endif
333     gtk_widget_path_iter_add_class(path, -1, "background");
334     gtk_widget_path_iter_add_class(path, -1, "tooltip");
335     gtk_widget_path_iter_set_name(path, -1, "gtk-tooltip");
336     GtkStyleContext* sc = gtk_style_context_new();
337     gtk_style_context_set_path(sc, path);
338     return sc;
339 }
340 
bg(GtkStyleContext * sc,wxColour & color,int state=GTK_STATE_FLAG_NORMAL)341 static void bg(GtkStyleContext* sc, wxColour& color, int state = GTK_STATE_FLAG_NORMAL)
342 {
343     GdkRGBA* rgba;
344     cairo_pattern_t* pattern = NULL;
345     gtk_style_context_set_state(sc, GtkStateFlags(state));
346     gtk_style_context_get(sc, GtkStateFlags(state),
347         "background-color", &rgba, "background-image", &pattern, NULL);
348     color = wxColour(*rgba);
349     gdk_rgba_free(rgba);
350 
351     // "background-image" takes precedence over "background-color".
352     // If there is an image, try to get a color out of it.
353     if (pattern)
354     {
355         if (cairo_pattern_get_type(pattern) == CAIRO_PATTERN_TYPE_SURFACE)
356         {
357             cairo_surface_t* surf;
358             cairo_pattern_get_surface(pattern, &surf);
359             if (cairo_surface_get_type(surf) == CAIRO_SURFACE_TYPE_IMAGE)
360             {
361                 const guchar* data = cairo_image_surface_get_data(surf);
362                 const int stride = cairo_image_surface_get_stride(surf);
363                 // choose a pixel in the middle vertically,
364                 // images often have a vertical gradient
365                 const int i = stride * (cairo_image_surface_get_height(surf) / 2);
366                 const unsigned* p = reinterpret_cast<const unsigned*>(data + i);
367                 const unsigned pixel = *p;
368                 guchar r, g, b, a = 0xff;
369                 switch (cairo_image_surface_get_format(surf))
370                 {
371                 case CAIRO_FORMAT_ARGB32:
372                     a = guchar(pixel >> 24);
373                     // fallthrough
374                 case CAIRO_FORMAT_RGB24:
375                     r = guchar(pixel >> 16);
376                     g = guchar(pixel >> 8);
377                     b = guchar(pixel);
378                     break;
379                 default:
380                     a = 0;
381                     break;
382                 }
383                 if (a != 0)
384                 {
385                     if (a != 0xff)
386                     {
387                         // un-premultiply
388                         r = guchar((r * 0xff) / a);
389                         g = guchar((g * 0xff) / a);
390                         b = guchar((b * 0xff) / a);
391                     }
392                     color.Set(r, g, b, a);
393                 }
394             }
395         }
396         cairo_pattern_destroy(pattern);
397     }
398 
399     if (color.Alpha() == 0)
400     {
401         // Try TLW as last resort, but not if we're already doing it
402         const GtkWidgetPath* path0 = gtk_style_context_get_path(sc);
403         if (gtk_widget_path_length(path0) > 1 ||
404             gtk_widget_path_iter_get_object_type(path0, 0) != GTK_TYPE_WINDOW)
405         {
406             GtkWidgetPath* path = gtk_widget_path_new();
407             GtkStyleContext* sc2;
408             sc2 = StyleContext(NULL, path, GTK_TYPE_WINDOW, "window", "background");
409             gtk_widget_path_unref(path);
410             bg(sc2, color, state);
411         }
412     }
413 
414     StyleContextFree(sc);
415 }
416 
fg(GtkStyleContext * sc,wxColour & color,int state=GTK_STATE_FLAG_NORMAL)417 static void fg(GtkStyleContext* sc, wxColour& color, int state = GTK_STATE_FLAG_NORMAL)
418 {
419     GdkRGBA rgba;
420     gtk_style_context_set_state(sc, GtkStateFlags(state));
421     gtk_style_context_get_color(sc, GtkStateFlags(state), &rgba);
422     color = wxColour(rgba);
423     StyleContextFree(sc);
424 }
425 
border(GtkStyleContext * sc,wxColour & color)426 static void border(GtkStyleContext* sc, wxColour& color)
427 {
428     GdkRGBA* rgba;
429     gtk_style_context_get(sc, GTK_STATE_FLAG_NORMAL, "border-color", &rgba, NULL);
430     color = wxColour(*rgba);
431     gdk_rgba_free(rgba);
432     StyleContextFree(sc);
433 }
434 
GetColour(wxSystemColour index)435 wxColour wxSystemSettingsNative::GetColour(wxSystemColour index)
436 {
437     if (unsigned(index) > wxSYS_COLOUR_MAX)
438         index = wxSYS_COLOUR_MAX;
439 
440     wxColour& color = gs_systemColorCache[index];
441     if (color.IsOk())
442         return color;
443 
444     static bool once;
445     if (!once)
446     {
447         once = true;
448         g_signal_connect(gtk_settings_get_default(), "notify::gtk-theme-name",
449             G_CALLBACK(notify_gtk_theme_name), NULL);
450     }
451 
452     GtkWidgetPath* path = gtk_widget_path_new();
453     GtkStyleContext* sc;
454 
455     switch (index)
456     {
457     case wxSYS_COLOUR_ACTIVECAPTION:
458     case wxSYS_COLOUR_INACTIVECAPTION:
459     case wxSYS_COLOUR_GRADIENTACTIVECAPTION:
460     case wxSYS_COLOUR_GRADIENTINACTIVECAPTION:
461 #if GTK_CHECK_VERSION(3,10,0)
462         if (gtk_check_version(3,10,0) == NULL)
463         {
464             sc = HeaderbarContext(path);
465             int state = GTK_STATE_FLAG_NORMAL;
466             if (index == wxSYS_COLOUR_INACTIVECAPTION ||
467                 index == wxSYS_COLOUR_GRADIENTINACTIVECAPTION)
468             {
469                 state = GTK_STATE_FLAG_BACKDROP;
470             }
471             bg(sc, color, state);
472             break;
473         }
474 #endif
475         // fall through
476     case wxSYS_COLOUR_3DLIGHT:
477     case wxSYS_COLOUR_ACTIVEBORDER:
478     case wxSYS_COLOUR_BTNFACE:
479     case wxSYS_COLOUR_DESKTOP:
480     case wxSYS_COLOUR_INACTIVEBORDER:
481     case wxSYS_COLOUR_SCROLLBAR:
482     case wxSYS_COLOUR_WINDOWFRAME:
483         sc = ButtonContext(path);
484         bg(sc, color);
485         break;
486     case wxSYS_COLOUR_HIGHLIGHT:
487         sc = TextviewContext(path, "text", "selection");
488         bg(sc, color, GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED);
489         break;
490     case wxSYS_COLOUR_HIGHLIGHTTEXT:
491         sc = TextviewContext(path, "text", "selection");
492         fg(sc, color, GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED);
493         break;
494     case wxSYS_COLOUR_WINDOWTEXT:
495         sc = TextviewContext(path, "text");
496         fg(sc, color);
497         break;
498     case wxSYS_COLOUR_BTNHIGHLIGHT:
499         sc = ButtonContext(path);
500         bg(sc, color, GTK_STATE_FLAG_PRELIGHT);
501         break;
502     case wxSYS_COLOUR_BTNSHADOW:
503         sc = ButtonContext(path);
504         border(sc, color);
505         break;
506     case wxSYS_COLOUR_CAPTIONTEXT:
507 #if GTK_CHECK_VERSION(3,10,0)
508         if (gtk_check_version(3,10,0) == NULL)
509         {
510             sc = HeaderbarLabelContext(path);
511             fg(sc, color);
512             break;
513         }
514 #endif
515         // fall through
516     case wxSYS_COLOUR_BTNTEXT:
517         sc = ButtonLabelContext(path);
518         fg(sc, color);
519         break;
520     case wxSYS_COLOUR_INACTIVECAPTIONTEXT:
521 #if GTK_CHECK_VERSION(3,10,0)
522         if (gtk_check_version(3,10,0) == NULL)
523         {
524             sc = HeaderbarLabelContext(path);
525             fg(sc, color, GTK_STATE_FLAG_BACKDROP);
526             break;
527         }
528 #endif
529         // fall through
530     case wxSYS_COLOUR_GRAYTEXT:
531         sc = StyleContext(path, GTK_TYPE_LABEL, "label");
532         fg(sc, color, GTK_STATE_FLAG_INSENSITIVE);
533         break;
534     case wxSYS_COLOUR_HOTLIGHT:
535         sc = StyleContext(path, GTK_TYPE_LINK_BUTTON, "button", "link");
536         if (gtk_check_version(3,12,0) == NULL)
537             fg(sc, color, GTK_STATE_FLAG_LINK);
538         else
539         {
540             wxGCC_WARNING_SUPPRESS(deprecated-declarations)
541             GValue value = G_VALUE_INIT;
542             g_value_init(&value, GDK_TYPE_COLOR);
543             gtk_style_context_get_style_property(sc, "link-color", &value);
544             GdkColor* link_color = static_cast<GdkColor*>(g_value_get_boxed(&value));
545             GdkColor gdkColor = { 0, 0, 0, 0xeeee };
546             if (link_color)
547                 gdkColor = *link_color;
548             color = wxColour(gdkColor);
549             g_value_unset(&value);
550             StyleContextFree(sc);
551             wxGCC_WARNING_RESTORE()
552         }
553         break;
554     case wxSYS_COLOUR_INFOBK:
555         sc = TooltipContext(path);
556         bg(sc, color);
557         break;
558     case wxSYS_COLOUR_INFOTEXT:
559         sc = TooltipContext(path);
560         sc = StyleContext(sc, path, GTK_TYPE_LABEL, "label");
561         fg(sc, color);
562         break;
563     case wxSYS_COLOUR_LISTBOX:
564         sc = TreeviewContext(path);
565         bg(sc, color);
566         break;
567     case wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT:
568         sc = TreeviewContext(path);
569         fg(sc, color, GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED);
570         break;
571     case wxSYS_COLOUR_LISTBOXTEXT:
572         sc = TreeviewContext(path);
573         fg(sc, color);
574         break;
575     case wxSYS_COLOUR_MENU:
576         sc = MenuContext(path);
577         bg(sc, color);
578         break;
579     case wxSYS_COLOUR_MENUBAR:
580         sc = StyleContext(path, GTK_TYPE_MENU_BAR, "menubar", "menubar");
581         bg(sc, color);
582         break;
583     case wxSYS_COLOUR_MENUHILIGHT:
584         sc = MenuItemContext(path);
585         bg(sc, color, GTK_STATE_FLAG_PRELIGHT);
586         break;
587     case wxSYS_COLOUR_MENUTEXT:
588         sc = MenuItemContext(path);
589         sc = StyleContext(sc, path, GTK_TYPE_LABEL, "label");
590         fg(sc, color);
591         break;
592     case wxSYS_COLOUR_APPWORKSPACE:
593     case wxSYS_COLOUR_WINDOW:
594         sc = TextviewContext(path);
595         bg(sc, color);
596         break;
597     case wxSYS_COLOUR_3DDKSHADOW:
598         color.Set(0, 0, 0);
599         break;
600     default:
601         wxFAIL_MSG("invalid system colour index");
602         color.Set(0, 0, 0, 0);
603         break;
604     }
605 
606     gtk_widget_path_unref(path);
607 
608     return color;
609 }
610 #else // !__WXGTK3__
ButtonStyle()611 static const GtkStyle* ButtonStyle()
612 {
613     return gtk_widget_get_style(ButtonWidget());
614 }
615 
ListStyle()616 static const GtkStyle* ListStyle()
617 {
618     return gtk_widget_get_style(ListWidget());
619 }
620 
TextCtrlStyle()621 static const GtkStyle* TextCtrlStyle()
622 {
623     return gtk_widget_get_style(TextCtrlWidget());
624 }
625 
MenuItemStyle()626 static const GtkStyle* MenuItemStyle()
627 {
628     return gtk_widget_get_style(MenuItemWidget());
629 }
630 
MenuBarStyle()631 static const GtkStyle* MenuBarStyle()
632 {
633     return gtk_widget_get_style(MenuBarWidget());
634 }
635 
ToolTipStyle()636 static const GtkStyle* ToolTipStyle()
637 {
638     return gtk_widget_get_style(ToolTipWidget());
639 }
640 
GetColour(wxSystemColour index)641 wxColour wxSystemSettingsNative::GetColour( wxSystemColour index )
642 {
643     wxColor color;
644     switch (index)
645     {
646         case wxSYS_COLOUR_SCROLLBAR:
647         case wxSYS_COLOUR_BACKGROUND:
648         //case wxSYS_COLOUR_DESKTOP:
649         case wxSYS_COLOUR_INACTIVECAPTION:
650         case wxSYS_COLOUR_GRADIENTINACTIVECAPTION:
651         case wxSYS_COLOUR_MENU:
652         case wxSYS_COLOUR_WINDOWFRAME:
653         case wxSYS_COLOUR_ACTIVEBORDER:
654         case wxSYS_COLOUR_INACTIVEBORDER:
655         case wxSYS_COLOUR_BTNFACE:
656         //case wxSYS_COLOUR_3DFACE:
657         case wxSYS_COLOUR_3DLIGHT:
658             color = wxColor(ButtonStyle()->bg[GTK_STATE_NORMAL]);
659             break;
660 
661         case wxSYS_COLOUR_WINDOW:
662             color = wxColor(TextCtrlStyle()->base[GTK_STATE_NORMAL]);
663             break;
664 
665         case wxSYS_COLOUR_MENUBAR:
666             color = wxColor(MenuBarStyle()->bg[GTK_STATE_NORMAL]);
667             break;
668 
669         case wxSYS_COLOUR_3DDKSHADOW:
670             color = *wxBLACK;
671             break;
672 
673         case wxSYS_COLOUR_GRAYTEXT:
674         case wxSYS_COLOUR_BTNSHADOW:
675         //case wxSYS_COLOUR_3DSHADOW:
676             {
677                 wxColour faceColour(GetColour(wxSYS_COLOUR_3DFACE));
678                 color =
679                    wxColour((unsigned char) (faceColour.Red() * 2 / 3),
680                             (unsigned char) (faceColour.Green() * 2 / 3),
681                             (unsigned char) (faceColour.Blue() * 2 / 3));
682             }
683             break;
684 
685         case wxSYS_COLOUR_BTNHIGHLIGHT:
686         //case wxSYS_COLOUR_BTNHILIGHT:
687         //case wxSYS_COLOUR_3DHIGHLIGHT:
688         //case wxSYS_COLOUR_3DHILIGHT:
689             color = *wxWHITE;
690             break;
691 
692         case wxSYS_COLOUR_HIGHLIGHT:
693             color = wxColor(ButtonStyle()->bg[GTK_STATE_SELECTED]);
694             break;
695 
696         case wxSYS_COLOUR_LISTBOX:
697             color = wxColor(ListStyle()->base[GTK_STATE_NORMAL]);
698             break;
699 
700         case wxSYS_COLOUR_LISTBOXTEXT:
701             color = wxColor(ListStyle()->text[GTK_STATE_NORMAL]);
702             break;
703 
704         case wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT:
705             // This is for the text in a list control (or tree) when the
706             // item is selected, but not focused
707             color = wxColor(ListStyle()->text[GTK_STATE_ACTIVE]);
708             break;
709 
710         case wxSYS_COLOUR_MENUTEXT:
711         case wxSYS_COLOUR_WINDOWTEXT:
712         case wxSYS_COLOUR_CAPTIONTEXT:
713         case wxSYS_COLOUR_INACTIVECAPTIONTEXT:
714         case wxSYS_COLOUR_BTNTEXT:
715             color = wxColor(ButtonStyle()->fg[GTK_STATE_NORMAL]);
716             break;
717 
718         case wxSYS_COLOUR_INFOBK:
719             color = wxColor(ToolTipStyle()->bg[GTK_STATE_NORMAL]);
720             break;
721 
722         case wxSYS_COLOUR_INFOTEXT:
723             color = wxColor(ToolTipStyle()->fg[GTK_STATE_NORMAL]);
724             break;
725 
726         case wxSYS_COLOUR_HIGHLIGHTTEXT:
727             color = wxColor(ButtonStyle()->fg[GTK_STATE_SELECTED]);
728             break;
729 
730         case wxSYS_COLOUR_APPWORKSPACE:
731             color = *wxWHITE;    // ?
732             break;
733 
734         case wxSYS_COLOUR_ACTIVECAPTION:
735         case wxSYS_COLOUR_GRADIENTACTIVECAPTION:
736         case wxSYS_COLOUR_MENUHILIGHT:
737             color = wxColor(MenuItemStyle()->bg[GTK_STATE_SELECTED]);
738             break;
739 
740         case wxSYS_COLOUR_HOTLIGHT:
741             {
742                 GdkColor c = { 0, 0, 0, 0xeeee };
743                 if (gtk_check_version(2,10,0) == NULL)
744                 {
745                     GdkColor* linkColor = NULL;
746                     gtk_widget_style_get(ButtonWidget(), "link-color", &linkColor, NULL);
747                     if (linkColor)
748                     {
749                         c = *linkColor;
750                         gdk_color_free(linkColor);
751                     }
752                 }
753                 color = wxColour(c);
754             }
755             break;
756 
757         case wxSYS_COLOUR_MAX:
758         default:
759             wxFAIL_MSG( wxT("unknown system colour index") );
760             color = *wxWHITE;
761             break;
762     }
763 
764     wxASSERT(color.IsOk());
765     return color;
766 }
767 #endif // !__WXGTK3__
768 
GetFont(wxSystemFont index)769 wxFont wxSystemSettingsNative::GetFont( wxSystemFont index )
770 {
771     wxFont font;
772     switch (index)
773     {
774         case wxSYS_OEM_FIXED_FONT:
775         case wxSYS_ANSI_FIXED_FONT:
776         case wxSYS_SYSTEM_FIXED_FONT:
777             font = *wxNORMAL_FONT;
778             break;
779 
780         case wxSYS_ANSI_VAR_FONT:
781         case wxSYS_SYSTEM_FONT:
782         case wxSYS_DEVICE_DEFAULT_FONT:
783         case wxSYS_DEFAULT_GUI_FONT:
784             if (!gs_fontSystem.IsOk())
785             {
786                 wxNativeFontInfo info;
787 #ifdef __WXGTK3__
788                 static bool once;
789                 if (!once)
790                 {
791                     once = true;
792                     g_signal_connect(gtk_settings_get_default(), "notify::gtk-font-name",
793                         G_CALLBACK(notify_gtk_font_name), NULL);
794                 }
795                 GtkWidgetPath* path = gtk_widget_path_new();
796                 GtkStyleContext* sc;
797                 sc = ButtonLabelContext(path);
798                 gtk_style_context_get(sc, GTK_STATE_FLAG_NORMAL,
799                     GTK_STYLE_PROPERTY_FONT, &info.description, NULL);
800                 gtk_widget_path_unref(path);
801                 StyleContextFree(sc);
802 #else
803                 info.description = ButtonStyle()->font_desc;
804 #endif
805                 gs_fontSystem = wxFont(info);
806 
807 #if wxUSE_FONTENUM
808                 // (try to) heal the default font (on some common systems e.g. Ubuntu
809                 // it's "Sans Serif" but the real font is called "Sans"):
810                 if (!wxFontEnumerator::IsValidFacename(gs_fontSystem.GetFaceName()) &&
811                     gs_fontSystem.GetFaceName() == "Sans Serif")
812                 {
813                     gs_fontSystem.SetFaceName("Sans");
814                 }
815 #endif // wxUSE_FONTENUM
816 
817 #ifndef __WXGTK3__
818                 info.description = NULL;
819 #endif
820             }
821             font = gs_fontSystem;
822             break;
823 
824         default:
825             break;
826     }
827 
828     wxASSERT( font.IsOk() );
829 
830     return font;
831 }
832 
833 // helper: return the GtkSettings either for the screen the current window is
834 // on or for the default screen if window is NULL
GetSettingsForWindowScreen(GdkWindow * window)835 static GtkSettings *GetSettingsForWindowScreen(GdkWindow *window)
836 {
837     return window ? gtk_settings_get_for_screen(gdk_window_get_screen(window))
838                   : gtk_settings_get_default();
839 }
840 
GetBorderWidth(wxSystemMetric index,wxWindow * win)841 static int GetBorderWidth(wxSystemMetric index, wxWindow* win)
842 {
843     if (win->m_wxwindow)
844     {
845         wxPizza* pizza = WX_PIZZA(win->m_wxwindow);
846         GtkBorder border;
847         pizza->get_border(border);
848         switch (index)
849         {
850             case wxSYS_BORDER_X:
851             case wxSYS_EDGE_X:
852             case wxSYS_FRAMESIZE_X:
853                 return border.left;
854             default:
855                 return border.top;
856         }
857     }
858     return -1;
859 }
860 
GetScrollbarWidth()861 static int GetScrollbarWidth()
862 {
863     int width;
864 #ifdef __WXGTK3__
865     if (gtk_check_version(3,20,0) == NULL)
866     {
867         GtkBorder border;
868         GtkWidgetPath* path = gtk_widget_path_new();
869         GtkStyleContext* sc;
870         sc = StyleContext(path, GTK_TYPE_SCROLLBAR, "scrollbar", "scrollbar", "right");
871 
872         gtk_style_context_get_border(sc, GTK_STATE_FLAG_NORMAL, &border);
873 
874         sc = StyleContext(sc, path, G_TYPE_NONE, "contents");
875         sc = StyleContext(sc, path, G_TYPE_NONE, "trough");
876         sc = StyleContext(sc, path, G_TYPE_NONE, "slider");
877 
878         gtk_style_context_get(sc, GTK_STATE_FLAG_NORMAL, "min-width", &width, NULL);
879         width += border.left + border.right;
880 
881         gtk_style_context_get_border(sc, GTK_STATE_FLAG_NORMAL, &border);
882         width += border.left + border.right;
883         gtk_style_context_get_padding(sc, GTK_STATE_FLAG_NORMAL, &border);
884         width += border.left + border.right;
885         gtk_style_context_get_margin(sc, GTK_STATE_FLAG_NORMAL, &border);
886         width += border.left + border.right;
887 
888         gtk_widget_path_unref(path);
889         StyleContextFree(sc);
890     }
891     else
892 #endif
893     {
894         int slider_width, trough_border;
895         gtk_widget_style_get(ScrollBarWidget(),
896             "slider-width", &slider_width, "trough-border", &trough_border, NULL);
897         width = slider_width + (2 * trough_border);
898     }
899     return width;
900 }
901 
GetMetric(wxSystemMetric index,wxWindow * win)902 int wxSystemSettingsNative::GetMetric( wxSystemMetric index, wxWindow* win )
903 {
904     GdkWindow *window = NULL;
905     if (win)
906         window = gtk_widget_get_window(win->GetHandle());
907 
908     switch (index)
909     {
910         case wxSYS_BORDER_X:
911         case wxSYS_BORDER_Y:
912         case wxSYS_EDGE_X:
913         case wxSYS_EDGE_Y:
914         case wxSYS_FRAMESIZE_X:
915         case wxSYS_FRAMESIZE_Y:
916             if (win)
917             {
918                 wxTopLevelWindow *tlw = wxDynamicCast(win, wxTopLevelWindow);
919                 if (!tlw)
920                     return GetBorderWidth(index, win);
921                 else if (window)
922                 {
923                     // Get the frame extents from the windowmanager.
924                     // In most cases the top extent is the titlebar, so we use the bottom extent
925                     // for the heights.
926                     int right, bottom;
927                     if (wxGetFrameExtents(window, NULL, &right, NULL, &bottom))
928                     {
929                         switch (index)
930                         {
931                             case wxSYS_BORDER_X:
932                             case wxSYS_EDGE_X:
933                             case wxSYS_FRAMESIZE_X:
934                                 return right; // width of right extent
935                             default:
936                                 return bottom; // height of bottom extent
937                         }
938                     }
939                 }
940             }
941 
942             return -1; // no window specified
943 
944         case wxSYS_CURSOR_X:
945         case wxSYS_CURSOR_Y:
946                 return gdk_display_get_default_cursor_size(
947                             window ? gdk_window_get_display(window)
948                                    : gdk_display_get_default());
949 
950         case wxSYS_DCLICK_X:
951         case wxSYS_DCLICK_Y:
952             gint dclick_distance;
953             g_object_get(GetSettingsForWindowScreen(window),
954                             "gtk-double-click-distance", &dclick_distance, NULL);
955 
956             return dclick_distance * 2;
957 
958         case wxSYS_DCLICK_MSEC:
959             gint dclick;
960             g_object_get(GetSettingsForWindowScreen(window),
961                             "gtk-double-click-time", &dclick, NULL);
962             return dclick;
963 
964         case wxSYS_DRAG_X:
965         case wxSYS_DRAG_Y:
966             gint drag_threshold;
967             g_object_get(GetSettingsForWindowScreen(window),
968                             "gtk-dnd-drag-threshold", &drag_threshold, NULL);
969 
970             // The correct thing here would be to double the value
971             // since that is what the API wants. But the values
972             // are much bigger under GNOME than under Windows and
973             // just seem to much in many cases to be useful.
974             // drag_threshold *= 2;
975 
976             return drag_threshold;
977 
978         case wxSYS_ICON_X:
979         case wxSYS_ICON_Y:
980             return 32;
981 
982         case wxSYS_SCREEN_X:
983             if (window)
984                 return gdk_screen_get_width(gdk_window_get_screen(window));
985             else
986                 return gdk_screen_width();
987 
988         case wxSYS_SCREEN_Y:
989             if (window)
990                 return gdk_screen_get_height(gdk_window_get_screen(window));
991             else
992                 return gdk_screen_height();
993 
994         case wxSYS_HSCROLL_Y:
995         case wxSYS_VSCROLL_X:
996             if (gs_scrollWidth == 0)
997                 gs_scrollWidth = GetScrollbarWidth();
998             return gs_scrollWidth;
999 
1000         case wxSYS_CAPTION_Y:
1001             if (!window)
1002                 // No realized window specified, and no implementation for that case yet.
1003                 return -1;
1004 
1005             wxASSERT_MSG( wxDynamicCast(win, wxTopLevelWindow),
1006                           wxT("Asking for caption height of a non toplevel window") );
1007 
1008             // Get the height of the top windowmanager border.
1009             // This is the titlebar in most cases. The titlebar might be elsewhere, and
1010             // we could check which is the thickest wm border to decide on which side the
1011             // titlebar is, but this might lead to interesting behaviours in used code.
1012             // Reconsider when we have a way to report to the user on which side it is.
1013             {
1014                 int top;
1015                 if (wxGetFrameExtents(window, NULL, NULL, &top, NULL))
1016                 {
1017                     return top; // top frame extent
1018                 }
1019             }
1020 
1021             // Try a default approach without a window pointer, if possible
1022             // ...
1023 
1024             return -1;
1025 
1026         case wxSYS_PENWINDOWS_PRESENT:
1027             // No MS Windows for Pen computing extension available in X11 based gtk+.
1028             return 0;
1029 
1030         default:
1031             return -1;   // metric is unknown
1032     }
1033 }
1034 
HasFeature(wxSystemFeature index)1035 bool wxSystemSettingsNative::HasFeature(wxSystemFeature index)
1036 {
1037     switch (index)
1038     {
1039         case wxSYS_CAN_ICONIZE_FRAME:
1040             return false;
1041 
1042         case wxSYS_CAN_DRAW_FRAME_DECORATIONS:
1043             return true;
1044 
1045         default:
1046             return false;
1047     }
1048 }
1049 
1050 class wxSystemSettingsModule: public wxModule
1051 {
1052 public:
OnInit()1053     virtual bool OnInit() { return true; }
1054     virtual void OnExit();
1055     wxDECLARE_DYNAMIC_CLASS(wxSystemSettingsModule);
1056 };
1057 wxIMPLEMENT_DYNAMIC_CLASS(wxSystemSettingsModule, wxModule);
1058 
OnExit()1059 void wxSystemSettingsModule::OnExit()
1060 {
1061 #ifdef __WXGTK3__
1062     GtkSettings* settings = gtk_settings_get_default();
1063     if (settings)
1064     {
1065         g_signal_handlers_disconnect_by_func(settings,
1066             (void*)notify_gtk_theme_name, NULL);
1067         g_signal_handlers_disconnect_by_func(settings,
1068             (void*)notify_gtk_font_name, NULL);
1069     }
1070 #endif
1071     if (gs_tlw_parent)
1072     {
1073         gtk_widget_destroy(gs_tlw_parent);
1074         gs_tlw_parent = NULL;
1075     }
1076 }
1077