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
50QT_BEGIN_NAMESPACE
51
52const char *QGtk3Theme::name = "gtk3";
53
54template <typename T>
55static 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
63static 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
71void 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
85QGtk3Theme::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
105static 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
114QVariant 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
142QString QGtk3Theme::gtkFontName() const
143{
144 QString cfgFontName = gtkSetting("gtk-font-name");
145 if (!cfgFontName.isEmpty())
146 return cfgFontName;
147 return QGnomeTheme::gtkFontName();
148}
149
150bool 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
164QPlatformDialogHelper *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
180QPlatformMenu* QGtk3Theme::createPlatformMenu() const
181{
182 return new QGtk3Menu;
183}
184
185QPlatformMenuItem* QGtk3Theme::createPlatformMenuItem() const
186{
187 return new QGtk3MenuItem;
188}
189
190bool 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
203QT_END_NAMESPACE
204