1
/****************************************************************************
3
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4
** Contact: Qt Software Information (qt-info@nokia.com)
6
** This file is part of the QtGui module of the Qt Toolkit.
9
** Licensees holding valid Qt Commercial licenses may use this file in
10
** accordance with the Qt Commercial License Agreement provided with the
11
** Software or, alternatively, in accordance with the terms contained in
12
** a written agreement between you and Nokia.
14
** GNU Lesser General Public License Usage
15
** Alternatively, this file may be used under the terms of the GNU Lesser
16
** General Public License version 2.1 as published by the Free Software
17
** Foundation and appearing in the file LICENSE.LGPL included in the
18
** packaging of this file. Please review the following information to
19
** ensure the GNU Lesser General Public License version 2.1 requirements
20
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22
** In addition, as a special exception, Nokia gives you certain
23
** additional rights. These rights are described in the Nokia Qt LGPL
24
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
27
** GNU General Public License Usage
28
** Alternatively, this file may be used under the terms of the GNU
29
** General Public License version 3.0 as published by the Free Software
30
** Foundation and appearing in the file LICENSE.GPL included in the
31
** packaging of this file. Please review the following information to
32
** ensure the GNU General Public License version 3.0 requirements will be
33
** met: http://www.gnu.org/copyleft/gpl.html.
35
** If you are unsure which license is appropriate for your use, please
36
** contact the sales department at qt-sales@nokia.com.
38
****************************************************************************/
41
#include "qticonloader.h"
42
#include <QtGui/QPixmapCache>
44
#include <QtCore/QList>
45
#include <QtCore/QHash>
46
#include <QtCore/QDir>
47
#include <QtCore/QString>
48
#include <QtCore/QLibrary>
49
#include <QtCore/QSettings>
50
#include <QtCore/QTextStream>
57
QIconTheme(QHash <int, QString> dirList, QStringList parents) :
58
_dirList(dirList), _parents(parents), _valid(true){ }
59
QIconTheme() : _valid(false){ }
60
QHash <int, QString> dirList() {return _dirList;}
61
QStringList parents() {return _parents;}
62
bool isValid() {return _valid;}
65
QHash <int, QString> _dirList;
70
class QtIconLoaderImplementation
73
QtIconLoaderImplementation();
74
QPixmap findIcon(int size, const QString &name) const;
77
QIconTheme parseIndexFile(const QString &themeName) const;
78
void lookupIconTheme() const;
79
QPixmap findIconHelper(int size,
80
const QString &themeName,
81
const QString &iconName,
82
QStringList &visited) const;
83
mutable QString themeName;
84
mutable QStringList iconDirs;
85
mutable QHash <QString, QIconTheme> themeList;
88
Q_GLOBAL_STATIC(QtIconLoaderImplementation, iconLoaderInstance)
93
Returns the standard icon for the given icon /a name
94
as specified in the freedesktop icon spec
95
http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
97
/a fallback is an optional argument to specify the icon to be used if
98
no icon is found on the platform. This is particularily useful for
102
QIcon QtIconLoader::icon(const QString &name, const QIcon &fallback)
106
QString pngExtension(QLatin1String(".png"));
107
QList<int> iconSizes;
108
iconSizes << 16 << 24 << 32 << 48 << 64;
109
Q_FOREACH (int size, iconSizes) {
110
icon.addPixmap(iconLoaderInstance()->findIcon(size, name + pngExtension));
121
QtIconLoaderImplementation::QtIconLoaderImplementation()
129
typedef void (*Ptr_g_type_init)();
130
typedef GConfClient* (*Ptr_gconf_client_get_default)();
131
typedef char* (*Ptr_gconf_client_get_string)(GConfClient*, const char*, GError **);
132
typedef void (*Ptr_g_object_unref)(void *);
133
typedef void (*Ptr_g_error_free)(GError *);
134
typedef void (*Ptr_g_free)(void*);
135
static Ptr_g_type_init p_g_type_init = 0;
136
static Ptr_gconf_client_get_default p_gconf_client_get_default = 0;
137
static Ptr_gconf_client_get_string p_gconf_client_get_string = 0;
138
static Ptr_g_object_unref p_g_object_unref = 0;
139
static Ptr_g_error_free p_g_error_free = 0;
140
static Ptr_g_free p_g_free = 0;
144
static int kdeVersion()
146
static int version = qgetenv("KDE_SESSION_VERSION").toInt();
150
static QString kdeHome()
152
static QString kdeHomePath;
153
if (kdeHomePath.isEmpty()) {
154
kdeHomePath = QFile::decodeName(qgetenv("KDEHOME"));
155
if (kdeHomePath.isEmpty()) {
156
int kdeSessionVersion = kdeVersion();
157
QDir homeDir(QDir::homePath());
158
QString kdeConfDir(QLatin1String("/.kde"));
159
if (4 == kdeSessionVersion && homeDir.exists(QLatin1String(".kde4")))
160
kdeConfDir = QLatin1String("/.kde4");
161
kdeHomePath = QDir::homePath() + kdeConfDir;
167
void QtIconLoaderImplementation::lookupIconTheme() const
171
QString dataDirs = QFile::decodeName(getenv("XDG_DATA_DIRS"));
172
if (dataDirs.isEmpty())
173
dataDirs = QLatin1String("/usr/local/share/:/usr/share/");
175
dataDirs.prepend(QDir::homePath() + QLatin1String("/:"));
176
iconDirs = dataDirs.split(QLatin1Char(':'));
178
// If we are running GNOME we resolve and use GConf. In all other
179
// cases we currently use the KDE icon theme
181
if (qgetenv("DESKTOP_SESSION") == "gnome" ||
182
!qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty()) {
184
if (themeName.isEmpty()) {
185
// Resolve glib and gconf
187
p_g_type_init = (Ptr_g_type_init)QLibrary::resolve(QLatin1String("gobject-2.0"), 0, "g_type_init");
188
p_gconf_client_get_default = (Ptr_gconf_client_get_default)QLibrary::resolve(QLatin1String("gconf-2"), 4, "gconf_client_get_default");
189
p_gconf_client_get_string = (Ptr_gconf_client_get_string)QLibrary::resolve(QLatin1String("gconf-2"), 4, "gconf_client_get_string");
190
p_g_object_unref = (Ptr_g_object_unref)QLibrary::resolve(QLatin1String("gobject-2.0"), 0, "g_object_unref");
191
p_g_error_free = (Ptr_g_error_free)QLibrary::resolve(QLatin1String("glib-2.0"), 0, "g_error_free");
192
p_g_free = (Ptr_g_free)QLibrary::resolve(QLatin1String("glib-2.0"), 0, "g_free");
194
if (p_g_type_init && p_gconf_client_get_default &&
195
p_gconf_client_get_string && p_g_object_unref &&
196
p_g_error_free && p_g_free) {
199
GConfClient* client = p_gconf_client_get_default();
202
char *str = p_gconf_client_get_string(client, "/desktop/gnome/interface/icon_theme", &err);
204
themeName = QString::fromUtf8(str);
208
p_g_object_unref(client);
210
p_g_error_free (err);
212
if (themeName.isEmpty())
213
themeName = QLatin1String("gnome");
216
if (!themeName.isEmpty())
221
if (dataDirs.isEmpty())
222
dataDirs = QLatin1String("/usr/local/share/:/usr/share/");
224
dataDirs += QLatin1Char(':') + kdeHome() + QLatin1String("/share");
225
dataDirs.prepend(QDir::homePath() + QLatin1String("/:"));
226
QStringList kdeDirs = QFile::decodeName(getenv("KDEDIRS")).split(QLatin1Char(':'));
227
Q_FOREACH (const QString dirName, kdeDirs)
228
dataDirs.append(QLatin1Char(':') + dirName + QLatin1String("/share"));
229
iconDirs = dataDirs.split(QLatin1Char(':'));
231
QFileInfo fileInfo(QLatin1String("/usr/share/icons/default.kde"));
232
QDir dir(fileInfo.canonicalFilePath());
233
QString kdeDefault = kdeVersion() >= 4 ? QString::fromLatin1("oxygen") : QString::fromLatin1("crystalsvg");
234
QString defaultTheme = fileInfo.exists() ? dir.dirName() : kdeDefault;
235
QSettings settings(kdeHome() + QLatin1String("/share/config/kdeglobals"), QSettings::IniFormat);
236
settings.beginGroup(QLatin1String("Icons"));
237
themeName = settings.value(QLatin1String("Theme"), defaultTheme).toString();
241
QIconTheme QtIconLoaderImplementation::parseIndexFile(const QString &themeName) const
246
QHash <int, QString> dirList;
248
for ( int i = 0 ; i < iconDirs.size() && !themeIndex.exists() ; ++i) {
249
const QString &contentDir = QLatin1String(iconDirs[i].startsWith(QDir::homePath()) ? "/.icons/" : "/icons/");
250
themeIndex.setFileName(iconDirs[i] + contentDir + themeName + QLatin1String("/index.theme"));
253
if (themeIndex.exists()) {
254
QSettings indexReader(themeIndex.fileName(), QSettings::IniFormat);
255
Q_FOREACH (const QString &key, indexReader.allKeys()) {
256
if (key.endsWith("/Size")) {
257
if (int size = indexReader.value(key).toInt())
258
dirList.insertMulti(size, key.left(key.size() - 5));
262
// Parent themes provide fallbacks for missing icons
263
parents = indexReader.value(QLatin1String("Icon Theme/Inherits")).toString().split(QLatin1Char(','));
266
if (kdeVersion() >= 3) {
267
QFileInfo fileInfo(QLatin1String("/usr/share/icons/default.kde"));
268
QDir dir(fileInfo.canonicalFilePath());
269
QString defaultKDETheme = dir.exists() ? dir.dirName() : kdeVersion() == 3 ?
270
QString::fromLatin1("crystalsvg") : QString::fromLatin1("oxygen");
271
if (!parents.contains(defaultKDETheme) && themeName != defaultKDETheme)
272
parents.append(defaultKDETheme);
273
} else if (parents.isEmpty() && themeName != QLatin1String("hicolor")) {
274
parents.append(QLatin1String("hicolor"));
277
theme = QIconTheme(dirList, parents);
281
QPixmap QtIconLoaderImplementation::findIconHelper(int size, const QString &themeName,
282
const QString &iconName, QStringList &visited) const
286
if (!themeName.isEmpty()) {
287
visited << themeName;
288
QIconTheme theme = themeList.value(themeName);
290
if (!theme.isValid()) {
291
theme = parseIndexFile(themeName);
292
themeList.insert(themeName, theme);
295
if (!theme.isValid())
298
QList <QString> subDirs = theme.dirList().values(size);
300
for ( int i = 0 ; i < iconDirs.size() ; ++i) {
301
for ( int j = 0 ; j < subDirs.size() ; ++j) {
302
QString contentDir = (iconDirs[i].startsWith(QDir::homePath())) ?
303
QLatin1String("/.icons/") : QLatin1String("/icons/");
304
QString fileName = iconDirs[i] + contentDir + themeName + QLatin1Char('/') + subDirs[j] + QLatin1Char('/') + iconName;
305
QFile file(fileName);
307
pixmap.load(fileName);
308
if (!pixmap.isNull())
313
if (pixmap.isNull()) {
314
QStringList parents = theme.parents();
315
//search recursively through inherited themes
316
for (int i = 0 ; pixmap.isNull() && i < parents.size() ; ++i) {
317
QString parentTheme = parents[i].trimmed();
318
if (!visited.contains(parentTheme)) //guard against endless recursion
319
pixmap = findIconHelper(size, parentTheme, iconName, visited);
326
QPixmap QtIconLoaderImplementation::findIcon(int size, const QString &name) const
329
QString pixmapName = QLatin1String("$qt") + name + QString::number(size);
330
if (QPixmapCache::find(pixmapName, pixmap))
333
if (!themeName.isEmpty()) {
335
pixmap = findIconHelper(size, themeName, name, visited);
337
QPixmapCache::insert(pixmapName, pixmap);