1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the plugins of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qgtk3theme.h" |
41 | #include "qgtk3dialoghelpers.h" |
42 | #include "qgtk3menu.h" |
43 | #include <QVariant> |
44 | |
45 | #undef signals |
46 | #include <gtk/gtk.h> |
47 | |
48 | #include <X11/Xlib.h> |
49 | |
50 | QT_BEGIN_NAMESPACE |
51 | |
52 | const char *QGtk3Theme::name = "gtk3" ; |
53 | |
54 | template <typename T> |
55 | static T gtkSetting(const gchar *propertyName) |
56 | { |
57 | GtkSettings *settings = gtk_settings_get_default(); |
58 | T value; |
59 | g_object_get(settings, propertyName, &value, NULL); |
60 | return value; |
61 | } |
62 | |
63 | static QString gtkSetting(const gchar *propertyName) |
64 | { |
65 | gchararray value = gtkSetting<gchararray>(propertyName); |
66 | QString str = QString::fromUtf8(value); |
67 | g_free(value); |
68 | return str; |
69 | } |
70 | |
71 | void gtkMessageHandler(const gchar *log_domain, |
72 | GLogLevelFlags log_level, |
73 | const gchar *message, |
74 | gpointer unused_data) { |
75 | /* Silence false-positive Gtk warnings (we are using Xlib to set |
76 | * the WM_TRANSIENT_FOR hint). |
77 | */ |
78 | if (g_strcmp0(message, "GtkDialog mapped without a transient parent. " |
79 | "This is discouraged." ) != 0) { |
80 | /* For other messages, call the default handler. */ |
81 | g_log_default_handler(log_domain, log_level, message, unused_data); |
82 | } |
83 | } |
84 | |
85 | QGtk3Theme::QGtk3Theme() |
86 | { |
87 | // gtk_init will reset the Xlib error handler, and that causes |
88 | // Qt applications to quit on X errors. Therefore, we need to manually restore it. |
89 | int (*oldErrorHandler)(Display *, XErrorEvent *) = XSetErrorHandler(nullptr); |
90 | |
91 | gtk_init(nullptr, nullptr); |
92 | |
93 | XSetErrorHandler(oldErrorHandler); |
94 | |
95 | /* Initialize some types here so that Gtk+ does not crash when reading |
96 | * the treemodel for GtkFontChooser. |
97 | */ |
98 | g_type_ensure(PANGO_TYPE_FONT_FAMILY); |
99 | g_type_ensure(PANGO_TYPE_FONT_FACE); |
100 | |
101 | /* Use our custom log handler. */ |
102 | g_log_set_handler("Gtk" , G_LOG_LEVEL_MESSAGE, gtkMessageHandler, nullptr); |
103 | } |
104 | |
105 | static inline QVariant gtkGetLongPressTime() |
106 | { |
107 | const char *gtk_long_press_time = "gtk-long-press-time" ; |
108 | static bool found = g_object_class_find_property(G_OBJECT_GET_CLASS(gtk_settings_get_default()), gtk_long_press_time); |
109 | if (!found) |
110 | return QVariant(); |
111 | return QVariant(gtkSetting<guint>(gtk_long_press_time)); // Since 3.14, apparently we support >= 3.6 |
112 | } |
113 | |
114 | QVariant QGtk3Theme::themeHint(QPlatformTheme::ThemeHint hint) const |
115 | { |
116 | switch (hint) { |
117 | case QPlatformTheme::CursorFlashTime: |
118 | return QVariant(gtkSetting<gint>("gtk-cursor-blink-time" )); |
119 | case QPlatformTheme::MouseDoubleClickDistance: |
120 | return QVariant(gtkSetting<gint>("gtk-double-click-distance" )); |
121 | case QPlatformTheme::MouseDoubleClickInterval: |
122 | return QVariant(gtkSetting<gint>("gtk-double-click-time" )); |
123 | case QPlatformTheme::MousePressAndHoldInterval: { |
124 | QVariant v = gtkGetLongPressTime(); |
125 | if (!v.isValid()) |
126 | v = QGnomeTheme::themeHint(hint); |
127 | return v; |
128 | } |
129 | case QPlatformTheme::PasswordMaskDelay: |
130 | return QVariant(gtkSetting<guint>("gtk-entry-password-hint-timeout" )); |
131 | case QPlatformTheme::StartDragDistance: |
132 | return QVariant(gtkSetting<gint>("gtk-dnd-drag-threshold" )); |
133 | case QPlatformTheme::SystemIconThemeName: |
134 | return QVariant(gtkSetting("gtk-icon-theme-name" )); |
135 | case QPlatformTheme::SystemIconFallbackThemeName: |
136 | return QVariant(gtkSetting("gtk-fallback-icon-theme" )); |
137 | default: |
138 | return QGnomeTheme::themeHint(hint); |
139 | } |
140 | } |
141 | |
142 | QString QGtk3Theme::gtkFontName() const |
143 | { |
144 | QString cfgFontName = gtkSetting("gtk-font-name" ); |
145 | if (!cfgFontName.isEmpty()) |
146 | return cfgFontName; |
147 | return QGnomeTheme::gtkFontName(); |
148 | } |
149 | |
150 | bool QGtk3Theme::usePlatformNativeDialog(DialogType type) const |
151 | { |
152 | switch (type) { |
153 | case ColorDialog: |
154 | return true; |
155 | case FileDialog: |
156 | return useNativeFileDialog(); |
157 | case FontDialog: |
158 | return true; |
159 | default: |
160 | return false; |
161 | } |
162 | } |
163 | |
164 | QPlatformDialogHelper *QGtk3Theme::createPlatformDialogHelper(DialogType type) const |
165 | { |
166 | switch (type) { |
167 | case ColorDialog: |
168 | return new QGtk3ColorDialogHelper; |
169 | case FileDialog: |
170 | if (!useNativeFileDialog()) |
171 | return nullptr; |
172 | return new QGtk3FileDialogHelper; |
173 | case FontDialog: |
174 | return new QGtk3FontDialogHelper; |
175 | default: |
176 | return nullptr; |
177 | } |
178 | } |
179 | |
180 | QPlatformMenu* QGtk3Theme::() const |
181 | { |
182 | return new QGtk3Menu; |
183 | } |
184 | |
185 | QPlatformMenuItem* QGtk3Theme::() const |
186 | { |
187 | return new QGtk3MenuItem; |
188 | } |
189 | |
190 | bool QGtk3Theme::useNativeFileDialog() |
191 | { |
192 | /* Require GTK3 >= 3.15.5 to avoid running into this bug: |
193 | * https://bugzilla.gnome.org/show_bug.cgi?id=725164 |
194 | * |
195 | * While this bug only occurs when using widget-based file dialogs |
196 | * (native GTK3 dialogs are fine) we have to disable platform file |
197 | * dialogs entirely since we can't avoid creation of a platform |
198 | * dialog helper. |
199 | */ |
200 | return gtk_check_version(3, 15, 5) == nullptr; |
201 | } |
202 | |
203 | QT_END_NAMESPACE |
204 | |