Use QStringList::join(QChar) overload where applicable [QtWidgets]
[profile/ivi/qtbase.git] / src / platformsupport / themes / genericunix / qgenericunixthemes.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qgenericunixthemes_p.h"
43 #include "../../services/genericunix/qgenericunixservices_p.h"
44
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>
58
59 QT_BEGIN_NAMESPACE
60
61 ResourceHelper::ResourceHelper()
62 {
63     qFill(palettes, palettes + QPlatformTheme::NPalettes, static_cast<QPalette *>(0));
64     qFill(fonts, fonts + QPlatformTheme::NFonts, static_cast<QFont *>(0));
65 }
66
67 void ResourceHelper::clear()
68 {
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));
73 }
74
75 /*!
76     \class QGenericX11ThemeQKdeTheme
77     \brief QGenericX11Theme is a generic theme implementation for X11.
78     \since 5.0
79     \internal
80     \ingroup qpa
81 */
82
83 const char *QGenericUnixTheme::name = "generic";
84
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 };
89
90 QGenericUnixTheme::QGenericUnixTheme()
91     : m_systemFont(QLatin1String(defaultSystemFontNameC), defaultSystemFontSize)
92 {
93 }
94
95 const QFont *QGenericUnixTheme::font(Font type) const
96 {
97     if (type == QPlatformTheme::SystemFont)
98         return &m_systemFont;
99     return 0;
100 }
101
102 // Helper to return the icon theme paths from XDG.
103 QStringList QGenericUnixTheme::xdgIconThemePaths()
104 {
105     QStringList paths;
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());
110
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());
118     }
119     return paths;
120 }
121
122 QVariant QGenericUnixTheme::themeHint(ThemeHint hint) const
123 {
124     switch (hint) {
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);
135     }
136     case QPlatformTheme::KeyboardScheme:
137         return QVariant(int(X11KeyboardScheme));
138     default:
139         break;
140     }
141     return QPlatformTheme::themeHint(hint);
142 }
143
144 #ifndef QT_NO_SETTINGS
145
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)
150 {
151     const QVariant value = kdeSettings.value(key);
152     if (!value.isValid())
153         return false;
154     const QStringList values = value.toStringList();
155     if (values.size() != 3)
156         return false;
157     pal->setBrush(role, QColor(values.at(0).toInt(), values.at(1).toInt(), values.at(2).toInt()));
158     return true;
159 }
160
161 static inline void readKdeSystemPalette(const QSettings &kdeSettings, QPalette *pal)
162 {
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"));
174 }
175
176 /*!
177     \class QKdeTheme
178     \brief QKdeTheme is a theme implementation for the KDE desktop (version 4 or higher).
179     \since 5.0
180     \internal
181     \ingroup qpa
182 */
183
184 const char *QKdeTheme::name = "kde";
185
186 QKdeTheme::QKdeTheme(const QString &kdeHome, int kdeVersion) :
187     m_kdeHome(kdeHome), m_kdeVersion(kdeVersion),
188     m_toolButtonStyle(Qt::ToolButtonTextBesideIcon), m_toolBarIconSize(0)
189 {
190     refresh();
191 }
192
193 static inline QFont *readKdeFontSetting(const QSettings &settings, const QString &key)
194 {
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;
202         QString fontFamily;
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(','));
208             }
209         } else {
210             fontDescription = fontFamily = fontValue.toString();
211         }
212         if (!fontDescription.isEmpty()) {
213             QFont font(fontFamily);
214             if (font.fromString(fontDescription))
215                 return new QFont(font);
216         }
217     }
218     return 0;
219 }
220
221 void QKdeTheme::refresh()
222 {
223     m_resources.clear();
224
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");
230
231     // Read settings file.
232     const QString settingsFile = globalSettingsFile();
233     if (!QFileInfo(settingsFile).isReadable())
234         return;
235
236     const QSettings kdeSettings(settingsFile, QSettings::IniFormat);
237
238     QPalette systemPalette = QPalette();
239     readKdeSystemPalette(kdeSettings, &systemPalette);
240     m_resources.palettes[SystemPalette] = new QPalette(systemPalette);
241     //## TODO tooltip color
242
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);
248     }
249
250     const QVariant themeValue = kdeSettings.value(QStringLiteral("Icons/Theme"));
251     if (themeValue.isValid())
252         m_iconThemeName = themeValue.toString();
253
254     const QVariant toolBarIconSizeValue = kdeSettings.value(QStringLiteral("ToolbarIcons/Size"));
255     if (toolBarIconSizeValue.isValid())
256         m_toolBarIconSize = toolBarIconSizeValue.toInt();
257
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;
267     }
268
269     // Read system font, ignore 'fixed' 'smallestReadableFont'
270     if (QFont *systemFont = readKdeFontSetting(kdeSettings, QStringLiteral("font"))) {
271         m_resources.fonts[SystemFont] = systemFont;
272     } else {
273         m_resources.fonts[SystemFont] = new QFont(QLatin1String(defaultSystemFontNameC), defaultSystemFontSize);
274     }
275 }
276
277 QString QKdeTheme::globalSettingsFile() const
278 {
279     return m_kdeHome + QStringLiteral("/share/config/kdeglobals");
280 }
281
282 static QStringList kdeIconThemeSearchPaths(const QString &kdeHome)
283 {
284     QStringList candidates = QStringList(kdeHome);
285     const QString kdeDirs = QFile::decodeName(qgetenv("KDEDIRS"));
286     if (!kdeDirs.isEmpty())
287         candidates.append(kdeDirs.split(QLatin1Char(':')));
288
289     QStringList paths = QGenericUnixTheme::xdgIconThemePaths();
290     const QString iconPath = QStringLiteral("/share/icons");
291     foreach (const QString &candidate, candidates) {
292         const QFileInfo fi(candidate + iconPath);
293         if (fi.isDir())
294             paths.append(fi.absoluteFilePath());
295     }
296     return paths;
297 }
298
299 QVariant QKdeTheme::themeHint(QPlatformTheme::ThemeHint hint) const
300 {
301     switch (hint) {
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));
322     default:
323         break;
324     }
325     return QPlatformTheme::themeHint(hint);
326 }
327
328 QPlatformTheme *QKdeTheme::createKdeTheme()
329 {
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();
334     if (kdeVersion < 4)
335         return 0;
336     const QString kdeHomePathVar = QString::fromLocal8Bit(qgetenv("KDEHOME"));
337     if (!kdeHomePathVar.isEmpty())
338         return new QKdeTheme(kdeHomePathVar, kdeVersion);
339
340      const QString kdeVersionHomePath = QDir::homePath() + QStringLiteral("/.kde") + QLatin1String(kdeVersionBA);
341      if (QFileInfo(kdeVersionHomePath).isDir())
342          return new QKdeTheme(kdeVersionHomePath, kdeVersion);
343
344      const QString kdeHomePath = QDir::homePath() + QStringLiteral("/.kde");
345      if (QFileInfo(kdeHomePath).isDir())
346          return new QKdeTheme(kdeHomePath, kdeVersion);
347
348      qWarning("%s: Unable to determine KDEHOME", Q_FUNC_INFO);
349      return 0;
350 }
351
352 #endif // QT_NO_SETTINGS
353
354 /*!
355     \class QGnomeTheme
356     \brief QGnomeTheme is a theme implementation for the Gnome desktop.
357     \since 5.0
358     \internal
359     \ingroup qpa
360 */
361
362 const char *QGnomeTheme::name = "gnome";
363
364 QGnomeTheme::QGnomeTheme()
365    : m_systemFont(QLatin1String(defaultSystemFontNameC), defaultSystemFontSize)
366 {
367 }
368
369 QVariant QGnomeTheme::themeHint(QPlatformTheme::ThemeHint hint) const
370 {
371     switch (hint) {
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);
385     }
386     case QPlatformTheme::KeyboardScheme:
387         return QVariant(int(GnomeKeyboardScheme));
388     default:
389         break;
390     }
391     return QPlatformTheme::themeHint(hint);
392 }
393
394 const QFont *QGnomeTheme::font(Font type) const
395 {
396     if (type == QPlatformTheme::SystemFont)
397         return &m_systemFont;
398     return 0;
399 }
400
401 /*!
402     \brief Creates a UNIX theme according to the detected desktop environment.
403 */
404
405 QPlatformTheme *QGenericUnixTheme::createUnixTheme(const QString &name)
406 {
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())
412             return kdeTheme;
413 #endif
414     if (name == QLatin1String(QGnomeTheme::name))
415         return new QGnomeTheme;
416     return new QGenericUnixTheme;
417 }
418
419 QStringList QGenericUnixTheme::themeNames()
420 {
421     QStringList result;
422     if (QGuiApplication::desktopSettingsAware()) {
423         if (QGuiApplicationPrivate::platformIntegration()->services()->desktopEnvironment() == QByteArray("KDE")) {
424 #ifndef QT_NO_SETTINGS
425             result.push_back(QLatin1String(QKdeTheme::name));
426 #endif
427         } else if (QGuiApplicationPrivate::platformIntegration()->services()->desktopEnvironment() == QByteArray("GNOME")) {
428             result.push_back(QLatin1String(QGnomeTheme::name));
429         }
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));
436     return result;
437 }
438
439 QT_END_NAMESPACE