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