Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgenericunixtheme.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
7
8#include <QPalette>
9#include <QFont>
10#include <QGuiApplication>
11#include <QDir>
12#include <QFileInfo>
13#include <QFile>
14#include <QDebug>
15#include <QHash>
16#include <QLoggingCategory>
17#include <QVariant>
18#include <QStandardPaths>
19#include <QStringList>
20#if QT_CONFIG(mimetype)
21#include <QMimeDatabase>
22#endif
23#if QT_CONFIG(settings)
24#include <QSettings>
25#if QT_CONFIG(dbus)
26#include "qkdetheme_p.h"
27#endif
28#endif
29
30#include <qpa/qplatformfontdatabase.h> // lcQpaFonts
31#include <qpa/qplatformintegration.h>
32#include <qpa/qplatformservices.h>
33#include <qpa/qplatformdialoghelper.h>
34#include <qpa/qplatformtheme_p.h>
35
36#if QT_CONFIG(dbus)
37#include <QJsonDocument>
38#include <QJsonArray>
39#include <QJsonObject>
40#include <QJsonValue>
41#include <QJsonParseError>
42#ifndef QT_NO_SYSTEMTRAYICON
43#include <private/qdbustrayicon_p.h>
44#include <private/qdbusmenubar_p.h>
45#endif
46#endif
47
48#include <private/qguiapplication_p.h>
49#include <qpa/qplatformintegration.h>
50#include <QtCore/QStandardPaths>
51#if QT_CONFIG(dbus)
52#include <QtDBus/QDBusConnectionInterface>
53#include <private/qdbustrayicon_p.h>
54#endif
55#if QT_CONFIG(mimetype)
56#include <QtCore/QMimeDatabase>
57#include <QtCore/QMimeData>
58#endif
59
60
62
63Q_DECLARE_LOGGING_CATEGORY(qLcTray)
64using namespace Qt::StringLiterals;
65
66const char *QGenericUnixTheme::name = "generic";
67
74{
75 fixedFont.setStyleHint(QFont::TypeWriter);
76 qCDebug(lcQpaFonts) << "default fonts: system" << systemFont << "fixed" << fixedFont;
77}
78
79QGenericUnixTheme::QGenericUnixTheme(QGenericUnixThemePrivate *p)
80 : QPlatformTheme(p)
81{}
82
83QGenericUnixTheme::QGenericUnixTheme()
84 : QPlatformTheme(new QGenericUnixThemePrivate())
85{}
86
87const QFont *QGenericUnixTheme::font(Font type) const
88{
89 Q_D(const QGenericUnixTheme);
90 switch (type) {
91 case QPlatformTheme::SystemFont:
92 return &d->systemFont;
93 case QPlatformTheme::FixedFont:
94 return &d->fixedFont;
95 default:
96 return nullptr;
97 }
98}
99
100#if QT_CONFIG(dbus)
101QPlatformMenuBar *QGenericUnixTheme::createPlatformMenuBar() const
102{
103 if (isDBusGlobalMenuAvailable())
104 return new QDBusMenuBar();
105 return nullptr;
106}
107#endif
108
109#if QT_CONFIG(dbus) && QT_CONFIG(systemtrayicon)
110QPlatformSystemTrayIcon *QGenericUnixTheme::createPlatformSystemTrayIcon() const
111{
112 if (shouldUseDBusTray())
113 return new QDBusTrayIcon();
114 return nullptr;
115}
116#endif
117
118QVariant QGenericUnixTheme::themeHint(ThemeHint hint) const
119{
120 switch (hint) {
121 case QPlatformTheme::SystemIconFallbackThemeName:
122 return QVariant(QString(QStringLiteral("hicolor")));
123 case QPlatformTheme::IconThemeSearchPaths:
124 return xdgIconThemePaths();
125 case QPlatformTheme::IconFallbackSearchPaths:
126 return iconFallbackPaths();
127 case QPlatformTheme::DialogButtonBoxButtonsHaveIcons:
128 return QVariant(true);
129 case QPlatformTheme::StyleNames: {
130 QStringList styleNames;
131 styleNames << QStringLiteral("Fusion") << QStringLiteral("Windows");
132 return QVariant(styleNames);
133 }
134 case QPlatformTheme::KeyboardScheme:
135 return QVariant(int(X11KeyboardScheme));
136 case QPlatformTheme::UiEffects:
137 return QVariant(int(HoverEffect));
138 case QPlatformTheme::MouseCursorTheme:
139 return QVariant(mouseCursorTheme());
140 case QPlatformTheme::MouseCursorSize:
141 return QVariant(mouseCursorSize());
142 case QPlatformTheme::PreferFileIconFromTheme:
143 return true;
144 default:
145 break;
146 }
147 return QPlatformTheme::themeHint(hint);
148}
149
150QStringList QGenericUnixTheme::themeNames()
151{
152 QStringList result;
153 if (QGuiApplication::desktopSettingsAware()) {
154 const QByteArray desktopEnvironment = QGuiApplicationPrivate::platformIntegration()->services()->desktopEnvironment();
155 QList<QByteArray> gtkBasedEnvironments;
156 gtkBasedEnvironments << "GNOME"
157 << "X-CINNAMON"
158 << "PANTHEON"
159 << "UNITY"
160 << "MATE"
161 << "XFCE"
162 << "LXDE";
163 const QList<QByteArray> desktopNames = desktopEnvironment.split(':');
164 for (const QByteArray &desktopName : desktopNames) {
165#if QT_CONFIG(dbus) && QT_CONFIG(settings) && (QT_CONFIG(xcb) || QT_CONFIG(wayland))
166 if (desktopEnvironment == "KDE") {
167 result.push_back(QLatin1StringView(QKdeTheme::name));
168 } else
169#endif
170 if (gtkBasedEnvironments.contains(desktopName)) {
171 // prefer the GTK3 theme implementation with native dialogs etc.
172 result.push_back(QStringLiteral("gtk3"));
173 // fallback to the generic Gnome theme if loading the GTK3 theme fails
174 result.push_back(QLatin1StringView(QGnomeTheme::name));
175 } else {
176 // unknown, but lowercase the name (our standard practice) and
177 // remove any "x-" prefix
178 QString s = QString::fromLatin1(desktopName.toLower());
179 result.push_back(s.startsWith("x-"_L1) ? s.mid(2) : s);
180 }
181 }
182 } // desktopSettingsAware
183 result.append(QLatin1StringView(QGenericUnixTheme::name));
184 return result;
185}
186
187/*!
188 \internal
189 \brief Creates a UNIX theme according to the given theme \a name
190*/
191QPlatformTheme *QGenericUnixTheme::createUnixTheme(const QString &name)
192{
193 if (name == QLatin1StringView(QGenericUnixTheme::name))
194 return new QGenericUnixTheme;
195#if QT_CONFIG(dbus) && QT_CONFIG(settings) && (QT_CONFIG(xcb) || QT_CONFIG(wayland))
196 if (name == QLatin1StringView(QKdeTheme::name))
197 return QKdeTheme::createKdeTheme();
198#endif
199 if (name == QLatin1StringView(QGnomeTheme::name))
200 return new QGnomeTheme;
201 return nullptr;
202}
203
204// Helper to return the icon theme paths from XDG.
205QStringList QGenericUnixTheme::xdgIconThemePaths()
206{
207 QStringList paths;
208 // Add home directory first in search path
209 const QFileInfo homeIconDir(QDir::homePath() + "/.icons"_L1);
210 if (homeIconDir.isDir())
211 paths.prepend(homeIconDir.absoluteFilePath());
212
213 paths.append(QStandardPaths::locateAll(QStandardPaths::GenericDataLocation,
214 QStringLiteral("icons"),
215 QStandardPaths::LocateDirectory));
216
217 return paths;
218}
219
220QStringList QGenericUnixTheme::iconFallbackPaths()
221{
222 QStringList paths;
223 const QFileInfo pixmapsIconsDir(QStringLiteral("/usr/share/pixmaps"));
224 if (pixmapsIconsDir.isDir())
225 paths.append(pixmapsIconsDir.absoluteFilePath());
226
227 return paths;
228}
229
230QString QGenericUnixTheme::mouseCursorTheme()
231{
232 static QString themeName = qEnvironmentVariable("XCURSOR_THEME");
233 return themeName;
234}
235
236QSize QGenericUnixTheme::mouseCursorSize()
237{
238 constexpr int defaultCursorSize = 24;
239 static const int xCursorSize = qEnvironmentVariableIntValue("XCURSOR_SIZE");
240 static const int s = xCursorSize > 0 ? xCursorSize : defaultCursorSize;
241 return QSize(s, s);
242}
243
244#if QT_CONFIG(dbus)
245static bool checkDBusGlobalMenuAvailable()
246{
247 const QDBusConnection connection = QDBusConnection::sessionBus();
248 static const QString registrarService = QStringLiteral("com.canonical.AppMenu.Registrar");
249 if (const auto iface = connection.interface())
250 return iface->isServiceRegistered(registrarService);
251 return false;
252}
253
254bool QGenericUnixTheme::isDBusGlobalMenuAvailable()
255{
256 static bool dbusGlobalMenuAvailable = checkDBusGlobalMenuAvailable();
257 return dbusGlobalMenuAvailable;
258}
259#endif
260
261#if QT_CONFIG(mimetype)
262QIcon QGenericUnixTheme::xdgFileIcon(const QFileInfo &fileInfo)
263{
264 QMimeDatabase mimeDatabase;
265 QMimeType mimeType = mimeDatabase.mimeTypeForFile(fileInfo);
266 if (!mimeType.isValid())
267 return QIcon();
268 const QString &iconName = mimeType.iconName();
269 if (!iconName.isEmpty()) {
270 QIcon icon = QIcon::fromTheme(iconName);
271 if (!icon.isNull())
272 return icon;
273 }
274 const QString &genericIconName = mimeType.genericIconName();
275 return genericIconName.isEmpty() ? QIcon() : QIcon::fromTheme(genericIconName);
276}
277#endif
278
279
280#if QT_CONFIG(dbus) && QT_CONFIG(systemtrayicon)
281bool QGenericUnixTheme::shouldUseDBusTray()
282{
283 // There's no other tray implementation to fallback to on non-X11
284 // and QDBusTrayIcon can register the icon on the fly after creation
285 if (QGuiApplication::platformName() != "xcb"_L1)
286 return true;
287 const bool result = QDBusMenuConnection().isWatcherRegistered();
288 qCDebug(qLcTray) << "D-Bus tray available:" << result;
289 return result;
290}
291#endif
292
293// Helper functions for implementing QPlatformTheme::fileIcon() for XDG icon themes.
294QList<QSize> QGenericUnixTheme::availableXdgFileIconSizes()
295{
296 return QIcon::fromTheme(QStringLiteral("inode-directory")).availableSizes();
297}
298
299
300QT_END_NAMESPACE