1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the plugins of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qgenericunixthemes_p.h"
43 #include "../../services/genericunix/qgenericunixservices_p.h"
45 #include <QtGui/QPalette>
46 #include <QtGui/QFont>
47 #include <QtGui/QGuiApplication>
48 #include <QtCore/QDir>
49 #include <QtCore/QFileInfo>
50 #include <QtCore/QFile>
51 #include <QtCore/QDebug>
52 #include <QtCore/QSettings>
53 #include <QtCore/QVariant>
54 #include <QtCore/QStringList>
55 #include <private/qguiapplication_p.h>
56 #include <qpa/qplatformintegration.h>
57 #include <qpa/qplatformservices.h>
61 ResourceHelper::ResourceHelper()
63 qFill(palettes, palettes + QPlatformTheme::NPalettes, static_cast<QPalette *>(0));
64 qFill(fonts, fonts + QPlatformTheme::NFonts, static_cast<QFont *>(0));
67 void ResourceHelper::clear()
69 qDeleteAll(palettes, palettes + QPlatformTheme::NPalettes);
70 qDeleteAll(fonts, fonts + QPlatformTheme::NFonts);
71 qFill(palettes, palettes + QPlatformTheme::NPalettes, static_cast<QPalette *>(0));
72 qFill(fonts, fonts + QPlatformTheme::NFonts, static_cast<QFont *>(0));
76 \class QGenericX11ThemeQKdeTheme
77 \brief QGenericX11Theme is a generic theme implementation for X11.
83 const char *QGenericUnixTheme::name = "generic";
85 // Default system font, corresponding to the value returned by 4.8 for
86 // XRender/FontConfig which we can now assume as default.
87 static const char defaultSystemFontNameC[] = "Sans Serif";
88 enum { defaultSystemFontSize = 9 };
90 QGenericUnixTheme::QGenericUnixTheme()
91 : m_systemFont(QLatin1String(defaultSystemFontNameC), defaultSystemFontSize)
95 const QFont *QGenericUnixTheme::font(Font type) const
97 if (type == QPlatformTheme::SystemFont)
102 // Helper to return the icon theme paths from XDG.
103 QStringList QGenericUnixTheme::xdgIconThemePaths()
106 // Add home directory first in search path
107 const QFileInfo homeIconDir(QDir::homePath() + QStringLiteral("/.icons"));
108 if (homeIconDir.isDir())
109 paths.prepend(homeIconDir.absoluteFilePath());
111 QString xdgDirString = QFile::decodeName(qgetenv("XDG_DATA_DIRS"));
112 if (xdgDirString.isEmpty())
113 xdgDirString = QLatin1String("/usr/local/share/:/usr/share/");
114 foreach (const QString &xdgDir, xdgDirString.split(QLatin1Char(':'))) {
115 const QFileInfo xdgIconsDir(xdgDir + QStringLiteral("/icons"));
116 if (xdgIconsDir.isDir())
117 paths.append(xdgIconsDir.absoluteFilePath());
122 QVariant QGenericUnixTheme::themeHint(ThemeHint hint) const
125 case QPlatformTheme::SystemIconFallbackThemeName:
126 return QVariant(QString(QStringLiteral("hicolor")));
127 case QPlatformTheme::IconThemeSearchPaths:
128 return xdgIconThemePaths();
129 case QPlatformTheme::DialogButtonBoxButtonsHaveIcons:
130 return QVariant(true);
131 case QPlatformTheme::StyleNames: {
132 QStringList styleNames;
133 styleNames << QStringLiteral("Plastique") << QStringLiteral("Windows");
134 return QVariant(styleNames);
136 case QPlatformTheme::KeyboardScheme:
137 return QVariant(int(X11KeyboardScheme));
141 return QPlatformTheme::themeHint(hint);
144 #ifndef QT_NO_SETTINGS
146 // Reads the color from the KDE configuration, and store it in the
147 // palette with the given color role if found.
148 static inline bool kdeColor(QPalette *pal, QPalette::ColorRole role,
149 const QSettings &kdeSettings, const QString &key)
151 const QVariant value = kdeSettings.value(key);
152 if (!value.isValid())
154 const QStringList values = value.toStringList();
155 if (values.size() != 3)
157 pal->setBrush(role, QColor(values.at(0).toInt(), values.at(1).toInt(), values.at(2).toInt()));
161 static inline void readKdeSystemPalette(const QSettings &kdeSettings, QPalette *pal)
163 kdeColor(pal, QPalette::Button, kdeSettings, QStringLiteral("Colors:Button/BackgroundNormal"));
164 kdeColor(pal, QPalette::Window, kdeSettings, QStringLiteral("Colors:Window/BackgroundNormal"));
165 kdeColor(pal, QPalette::Text, kdeSettings, QStringLiteral("Colors:View/ForegroundNormal"));
166 kdeColor(pal, QPalette::WindowText, kdeSettings, QStringLiteral("Colors:Window/ForegroundNormal"));
167 kdeColor(pal, QPalette::Base, kdeSettings, QStringLiteral("Colors:View/BackgroundNormal"));
168 kdeColor(pal, QPalette::Highlight, kdeSettings, QStringLiteral("Colors:Selection/BackgroundNormal"));
169 kdeColor(pal, QPalette::HighlightedText, kdeSettings, QStringLiteral("Colors:Selection/ForegroundNormal"));
170 kdeColor(pal, QPalette::AlternateBase, kdeSettings, QStringLiteral("Colors:View/BackgroundAlternate"));
171 kdeColor(pal, QPalette::ButtonText, kdeSettings, QStringLiteral("Colors:Button/ForegroundNormal"));
172 kdeColor(pal, QPalette::Link, kdeSettings, QStringLiteral("Colors:View/ForegroundLink"));
173 kdeColor(pal, QPalette::LinkVisited, kdeSettings, QStringLiteral("Colors:View/ForegroundVisited"));
178 \brief QKdeTheme is a theme implementation for the KDE desktop (version 4 or higher).
184 const char *QKdeTheme::name = "kde";
186 QKdeTheme::QKdeTheme(const QString &kdeHome, int kdeVersion) :
187 m_kdeHome(kdeHome), m_kdeVersion(kdeVersion),
188 m_toolButtonStyle(Qt::ToolButtonTextBesideIcon), m_toolBarIconSize(0)
193 static inline QFont *readKdeFontSetting(const QSettings &settings, const QString &key)
195 const QVariant fontValue = settings.value(key);
196 if (fontValue.isValid()) {
197 // Read font value: Might be a QStringList as KDE stores fonts without quotes.
198 // Also retrieve the family for the constructor since we cannot use the
199 // default constructor of QFont, which accesses QGuiApplication::systemFont()
200 // causing recursion.
201 QString fontDescription;
203 if (fontValue.type() == QVariant::StringList) {
204 const QStringList list = fontValue.toStringList();
205 if (!list.isEmpty()) {
206 fontFamily = list.first();
207 fontDescription = list.join(QLatin1Char(','));
210 fontDescription = fontFamily = fontValue.toString();
212 if (!fontDescription.isEmpty()) {
213 QFont font(fontFamily);
214 if (font.fromString(fontDescription))
215 return new QFont(font);
221 void QKdeTheme::refresh()
225 m_toolButtonStyle = Qt::ToolButtonTextBesideIcon;
226 m_toolBarIconSize = 0;
227 m_styleNames.clear();
228 m_styleNames << QStringLiteral("Oxygen") << QStringLiteral("plastique") << QStringLiteral("windows");
229 m_iconFallbackThemeName = m_iconThemeName = QStringLiteral("oxygen");
231 // Read settings file.
232 const QString settingsFile = globalSettingsFile();
233 if (!QFileInfo(settingsFile).isReadable())
236 const QSettings kdeSettings(settingsFile, QSettings::IniFormat);
238 QPalette systemPalette = QPalette();
239 readKdeSystemPalette(kdeSettings, &systemPalette);
240 m_resources.palettes[SystemPalette] = new QPalette(systemPalette);
241 //## TODO tooltip color
243 const QVariant styleValue = kdeSettings.value(QStringLiteral("widgetStyle"));
244 if (styleValue.isValid()) {
245 const QString style = styleValue.toString();
246 if (style != m_styleNames.front())
247 m_styleNames.push_front(style);
250 const QVariant themeValue = kdeSettings.value(QStringLiteral("Icons/Theme"));
251 if (themeValue.isValid())
252 m_iconThemeName = themeValue.toString();
254 const QVariant toolBarIconSizeValue = kdeSettings.value(QStringLiteral("ToolbarIcons/Size"));
255 if (toolBarIconSizeValue.isValid())
256 m_toolBarIconSize = toolBarIconSizeValue.toInt();
258 const QVariant toolbarStyleValue = kdeSettings.value(QStringLiteral("ToolButtonStyle"));
259 if (toolbarStyleValue.isValid()) {
260 const QString toolBarStyle = toolbarStyleValue.toString();
261 if (toolBarStyle == QStringLiteral("TextBesideIcon"))
262 m_toolButtonStyle = Qt::ToolButtonTextBesideIcon;
263 else if (toolBarStyle == QStringLiteral("TextOnly"))
264 m_toolButtonStyle = Qt::ToolButtonTextOnly;
265 else if (toolBarStyle == QStringLiteral("TextUnderIcon"))
266 m_toolButtonStyle = Qt::ToolButtonTextUnderIcon;
269 // Read system font, ignore 'fixed' 'smallestReadableFont'
270 if (QFont *systemFont = readKdeFontSetting(kdeSettings, QStringLiteral("font"))) {
271 m_resources.fonts[SystemFont] = systemFont;
273 m_resources.fonts[SystemFont] = new QFont(QLatin1String(defaultSystemFontNameC), defaultSystemFontSize);
277 QString QKdeTheme::globalSettingsFile() const
279 return m_kdeHome + QStringLiteral("/share/config/kdeglobals");
282 static QStringList kdeIconThemeSearchPaths(const QString &kdeHome)
284 QStringList candidates = QStringList(kdeHome);
285 const QString kdeDirs = QFile::decodeName(qgetenv("KDEDIRS"));
286 if (!kdeDirs.isEmpty())
287 candidates.append(kdeDirs.split(QLatin1Char(':')));
289 QStringList paths = QGenericUnixTheme::xdgIconThemePaths();
290 const QString iconPath = QStringLiteral("/share/icons");
291 foreach (const QString &candidate, candidates) {
292 const QFileInfo fi(candidate + iconPath);
294 paths.append(fi.absoluteFilePath());
299 QVariant QKdeTheme::themeHint(QPlatformTheme::ThemeHint hint) const
302 case QPlatformTheme::UseFullScreenForPopupMenu:
303 return QVariant(true);
304 case QPlatformTheme::DialogButtonBoxButtonsHaveIcons:
305 return QVariant(true);
306 case QPlatformTheme::DialogButtonBoxLayout:
307 return QVariant(2); // QDialogButtonBox::KdeLayout
308 case QPlatformTheme::ToolButtonStyle:
309 return QVariant(m_toolButtonStyle);
310 case QPlatformTheme::ToolBarIconSize:
311 return QVariant(m_toolBarIconSize);
312 case QPlatformTheme::SystemIconThemeName:
313 return QVariant(m_iconThemeName);
314 case QPlatformTheme::SystemIconFallbackThemeName:
315 return QVariant(m_iconFallbackThemeName);
316 case QPlatformTheme::IconThemeSearchPaths:
317 return QVariant(kdeIconThemeSearchPaths(m_kdeHome));
318 case QPlatformTheme::StyleNames:
319 return QVariant(m_styleNames);
320 case QPlatformTheme::KeyboardScheme:
321 return QVariant(int(KdeKeyboardScheme));
325 return QPlatformTheme::themeHint(hint);
328 QPlatformTheme *QKdeTheme::createKdeTheme()
330 // Check for version >= 4 and determine home folder from environment,
331 // defaulting to ~/.kde<version>, ~/.kde
332 const QByteArray kdeVersionBA = qgetenv("KDE_SESSION_VERSION");
333 const int kdeVersion = kdeVersionBA.toInt();
336 const QString kdeHomePathVar = QString::fromLocal8Bit(qgetenv("KDEHOME"));
337 if (!kdeHomePathVar.isEmpty())
338 return new QKdeTheme(kdeHomePathVar, kdeVersion);
340 const QString kdeVersionHomePath = QDir::homePath() + QStringLiteral("/.kde") + QLatin1String(kdeVersionBA);
341 if (QFileInfo(kdeVersionHomePath).isDir())
342 return new QKdeTheme(kdeVersionHomePath, kdeVersion);
344 const QString kdeHomePath = QDir::homePath() + QStringLiteral("/.kde");
345 if (QFileInfo(kdeHomePath).isDir())
346 return new QKdeTheme(kdeHomePath, kdeVersion);
348 qWarning("%s: Unable to determine KDEHOME", Q_FUNC_INFO);
352 #endif // QT_NO_SETTINGS
356 \brief QGnomeTheme is a theme implementation for the Gnome desktop.
362 const char *QGnomeTheme::name = "gnome";
364 QGnomeTheme::QGnomeTheme()
365 : m_systemFont(QLatin1String(defaultSystemFontNameC), defaultSystemFontSize)
369 QVariant QGnomeTheme::themeHint(QPlatformTheme::ThemeHint hint) const
372 case QPlatformTheme::DialogButtonBoxButtonsHaveIcons:
373 return QVariant(true);
374 case QPlatformTheme::DialogButtonBoxLayout:
375 return QVariant(3); // QDialogButtonBox::GnomeLayout
376 case QPlatformTheme::SystemIconThemeName:
377 case QPlatformTheme::SystemIconFallbackThemeName:
378 return QVariant(QString(QStringLiteral("gnome")));
379 case QPlatformTheme::IconThemeSearchPaths:
380 return QVariant(QGenericUnixTheme::xdgIconThemePaths());
381 case QPlatformTheme::StyleNames: {
382 QStringList styleNames;
383 styleNames << QStringLiteral("GTK+") << QStringLiteral("cleanlooks") << QStringLiteral("windows");
384 return QVariant(styleNames);
386 case QPlatformTheme::KeyboardScheme:
387 return QVariant(int(GnomeKeyboardScheme));
391 return QPlatformTheme::themeHint(hint);
394 const QFont *QGnomeTheme::font(Font type) const
396 if (type == QPlatformTheme::SystemFont)
397 return &m_systemFont;
402 \brief Creates a UNIX theme according to the detected desktop environment.
405 QPlatformTheme *QGenericUnixTheme::createUnixTheme(const QString &name)
407 if (name == QLatin1String(QGenericUnixTheme::name))
408 return new QGenericUnixTheme;
409 #ifndef QT_NO_SETTINGS
410 if (name == QLatin1String(QKdeTheme::name))
411 if (QPlatformTheme *kdeTheme = QKdeTheme::createKdeTheme())
414 if (name == QLatin1String(QGnomeTheme::name))
415 return new QGnomeTheme;
416 return new QGenericUnixTheme;
419 QStringList QGenericUnixTheme::themeNames()
422 if (QGuiApplication::desktopSettingsAware()) {
423 if (QGuiApplicationPrivate::platformIntegration()->services()->desktopEnvironment() == QByteArray("KDE")) {
424 #ifndef QT_NO_SETTINGS
425 result.push_back(QLatin1String(QKdeTheme::name));
427 } else if (QGuiApplicationPrivate::platformIntegration()->services()->desktopEnvironment() == QByteArray("GNOME")) {
428 result.push_back(QLatin1String(QGnomeTheme::name));
430 const QByteArray session = qgetenv("DESKTOP_SESSION");
431 if (!session.isEmpty() && session != "default")
432 result.push_back(QString::fromLocal8Bit(session));
433 } // desktopSettingsAware
434 if (result.isEmpty())
435 result.push_back(QLatin1String(QGenericUnixTheme::name));