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>
gtkSetting(const gchar * propertyName)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
gtkSetting(const gchar * propertyName)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
gtkMessageHandler(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer unused_data)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
QGtk3Theme()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
gtkGetLongPressTime()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
themeHint(QPlatformTheme::ThemeHint hint) const114 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
gtkFontName() const142 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
usePlatformNativeDialog(DialogType type) const150 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
createPlatformDialogHelper(DialogType type) const164 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
createPlatformMenu() const180 QPlatformMenu* QGtk3Theme::createPlatformMenu() const
181 {
182 return new QGtk3Menu;
183 }
184
createPlatformMenuItem() const185 QPlatformMenuItem* QGtk3Theme::createPlatformMenuItem() const
186 {
187 return new QGtk3MenuItem;
188 }
189
useNativeFileDialog()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