]> git.sur5r.net Git - minitube/blob - src/iconloader/qticonloader.cpp
Always rely on native icons on X11
[minitube] / src / iconloader / qticonloader.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: Qt Software Information (qt-info@nokia.com)
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** Commercial Usage
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.
13 **
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.
21 **
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
25 ** package.
26 **
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.
34 **
35 ** If you are unsure which license is appropriate for your use, please
36 ** contact the sales department at qt-sales@nokia.com.
37 **
38 ****************************************************************************/
39
40
41 #include "qticonloader.h"
42 #include <QtGui/QPixmapCache>
43
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>
51
52 #ifdef Q_WS_X11
53
54 class QIconTheme
55 {
56 public:
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;}
63
64 private:
65     QHash <int, QString> _dirList;
66     QStringList _parents;
67     bool _valid;
68 };
69
70 class QtIconLoaderImplementation
71 {
72 public:
73     QtIconLoaderImplementation();
74     QPixmap findIcon(int size, const QString &name) const;
75
76 private:
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;
86 };
87
88 Q_GLOBAL_STATIC(QtIconLoaderImplementation, iconLoaderInstance)
89 #endif
90
91         /*!
92
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
96
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
99     crossplatform code.
100
101 */
102         QIcon QtIconLoader::icon(const QString &name)
103 {
104     QIcon icon;
105
106 #ifdef Q_WS_X11
107 #if QT_VERSION < 0x040600
108     QString pngExtension(QLatin1String(".png"));
109     QList<int> iconSizes;
110     iconSizes << 16 << 22 << 24 << 32 << 48;
111     Q_FOREACH (int size, iconSizes) {
112         icon.addPixmap(iconLoaderInstance()->findIcon(size, name));
113     }
114 #else
115     icon = QIcon::fromTheme(name);
116 #endif
117 #else
118         icon = QIcon(QString(":/images/%1.png").arg(name));
119 #endif
120
121     return icon;
122 }
123
124 #ifdef Q_WS_X11
125
126 QtIconLoaderImplementation::QtIconLoaderImplementation()
127 {
128     lookupIconTheme();
129 }
130
131 extern "C" {
132     struct GConfClient;
133     struct GError;
134     typedef void (*Ptr_g_type_init)();
135     typedef GConfClient* (*Ptr_gconf_client_get_default)();
136     typedef char* (*Ptr_gconf_client_get_string)(GConfClient*, const char*, GError **);
137     typedef void (*Ptr_g_object_unref)(void *);
138     typedef void (*Ptr_g_error_free)(GError *);
139     typedef void (*Ptr_g_free)(void*);
140     static Ptr_g_type_init p_g_type_init = 0;
141     static Ptr_gconf_client_get_default p_gconf_client_get_default = 0;
142     static Ptr_gconf_client_get_string p_gconf_client_get_string = 0;
143     static Ptr_g_object_unref p_g_object_unref = 0;
144     static Ptr_g_error_free p_g_error_free = 0;
145     static Ptr_g_free p_g_free = 0;
146 }
147
148
149 static int kdeVersion()
150 {
151     static int version = qgetenv("KDE_SESSION_VERSION").toInt();
152     return version;
153 }
154
155 static QString kdeHome()
156 {
157     static QString kdeHomePath;
158     if (kdeHomePath.isEmpty()) {
159         kdeHomePath = QFile::decodeName(qgetenv("KDEHOME"));
160         if (kdeHomePath.isEmpty()) {
161             int kdeSessionVersion = kdeVersion();
162             QDir homeDir(QDir::homePath());
163             QString kdeConfDir(QLatin1String("/.kde"));
164             if (4 == kdeSessionVersion && homeDir.exists(QLatin1String(".kde4")))
165                 kdeConfDir = QLatin1String("/.kde4");
166             kdeHomePath = QDir::homePath() + kdeConfDir;
167         }
168     }
169     return kdeHomePath;
170 }
171
172 void QtIconLoaderImplementation::lookupIconTheme() const
173 {
174     
175 #ifdef Q_WS_X11
176     QString dataDirs = QFile::decodeName(getenv("XDG_DATA_DIRS"));
177     if (dataDirs.isEmpty())
178         dataDirs = QLatin1String("/usr/local/share/:/usr/share/");
179     
180     dataDirs.prepend(QDir::homePath() + QLatin1String("/:"));
181     iconDirs = dataDirs.split(QLatin1Char(':'));
182     
183     // If we are running GNOME we resolve and use GConf. In all other
184     // cases we currently use the KDE icon theme
185     
186     if (qgetenv("DESKTOP_SESSION") == "gnome" ||
187         !qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty()) {
188         
189         if (themeName.isEmpty()) {
190             // Resolve glib and gconf
191             
192             p_g_type_init =              (Ptr_g_type_init)QLibrary::resolve(QLatin1String("gobject-2.0"), 0, "g_type_init");
193             p_gconf_client_get_default = (Ptr_gconf_client_get_default)QLibrary::resolve(QLatin1String("gconf-2"), 4, "gconf_client_get_default");
194             p_gconf_client_get_string =  (Ptr_gconf_client_get_string)QLibrary::resolve(QLatin1String("gconf-2"), 4, "gconf_client_get_string");
195             p_g_object_unref =           (Ptr_g_object_unref)QLibrary::resolve(QLatin1String("gobject-2.0"), 0, "g_object_unref");
196             p_g_error_free =             (Ptr_g_error_free)QLibrary::resolve(QLatin1String("glib-2.0"), 0, "g_error_free");
197             p_g_free =                   (Ptr_g_free)QLibrary::resolve(QLatin1String("glib-2.0"), 0, "g_free");
198             
199             if (p_g_type_init && p_gconf_client_get_default &&
200                 p_gconf_client_get_string && p_g_object_unref &&
201                 p_g_error_free && p_g_free) {
202                 
203                 p_g_type_init();
204                 GConfClient* client = p_gconf_client_get_default();
205                 GError *err = 0;
206                 
207                 char *str = p_gconf_client_get_string(client, "/desktop/gnome/interface/icon_theme", &err);
208                 if (!err) {
209                     themeName = QString::fromUtf8(str);
210                     p_g_free(str);
211                 }
212                 
213                 p_g_object_unref(client);
214                 if (err)
215                     p_g_error_free (err);
216             }
217             if (themeName.isEmpty())
218                 themeName = QLatin1String("gnome");
219         }
220         
221         if (!themeName.isEmpty())
222             return;
223     }
224     
225     // KDE (and others)
226     if (dataDirs.isEmpty())
227         dataDirs = QLatin1String("/usr/local/share/:/usr/share/");
228     
229     dataDirs += QLatin1Char(':') + kdeHome() + QLatin1String("/share");
230     dataDirs.prepend(QDir::homePath() + QLatin1String("/:"));
231     QStringList kdeDirs = QFile::decodeName(getenv("KDEDIRS")).split(QLatin1Char(':'));
232     Q_FOREACH (const QString dirName, kdeDirs)
233             dataDirs.append(QLatin1Char(':') + dirName + QLatin1String("/share"));
234     iconDirs = dataDirs.split(QLatin1Char(':'));
235     
236     QFileInfo fileInfo(QLatin1String("/usr/share/icons/default.kde"));
237     QDir dir(fileInfo.canonicalFilePath());
238     QString kdeDefault = kdeVersion() >= 4 ? QString::fromLatin1("oxygen") : QString::fromLatin1("crystalsvg");
239     QString defaultTheme = fileInfo.exists() ? dir.dirName() : kdeDefault;
240     QSettings settings(kdeHome() + QLatin1String("/share/config/kdeglobals"), QSettings::IniFormat);
241     settings.beginGroup(QLatin1String("Icons"));
242     themeName = settings.value(QLatin1String("Theme"), defaultTheme).toString();
243 #endif
244 }
245
246 QIconTheme QtIconLoaderImplementation::parseIndexFile(const QString &themeName) const
247 {
248     QIconTheme theme;
249     QFile themeIndex;
250     QStringList parents;
251     QHash <int, QString> dirList;
252
253     for ( int i = 0 ; i < iconDirs.size() && !themeIndex.exists() ; ++i) {
254         const QString &contentDir = QLatin1String(iconDirs[i].startsWith(QDir::homePath()) ? "/.icons/" : "/icons/");
255         themeIndex.setFileName(iconDirs[i] + contentDir + themeName + QLatin1String("/index.theme"));
256     }
257
258     if (themeIndex.exists()) {
259         QSettings indexReader(themeIndex.fileName(), QSettings::IniFormat);
260         Q_FOREACH (const QString &key, indexReader.allKeys()) {
261             if (key.endsWith("/Size")) {
262                 if (int size = indexReader.value(key).toInt())
263                     dirList.insertMulti(size, key.left(key.size() - 5));
264             }
265         }
266
267         // Parent themes provide fallbacks for missing icons
268         // parents = indexReader.value(QLatin1String("Icon Theme/Inherits")).toString().split(QLatin1Char(','));
269         parents = indexReader.value(QLatin1String("Icon Theme/Inherits")).toStringList();
270
271
272     }
273
274     if (kdeVersion() >= 3) {
275         QFileInfo fileInfo(QLatin1String("/usr/share/icons/default.kde"));
276         QDir dir(fileInfo.canonicalFilePath());
277         QString defaultKDETheme = dir.exists() ? dir.dirName() : kdeVersion() == 3 ?
278                                   QString::fromLatin1("crystalsvg") : QString::fromLatin1("oxygen");
279         if (!parents.contains(defaultKDETheme) && themeName != defaultKDETheme)
280             parents.append(defaultKDETheme);
281     } else if (parents.isEmpty() && themeName != QLatin1String("hicolor")) {
282         parents.append(QLatin1String("hicolor"));
283     }
284     
285     theme = QIconTheme(dirList, parents);
286     return theme;
287 }
288
289 QPixmap QtIconLoaderImplementation::findIconHelper(int size, const QString &themeName,
290                                                    const QString &iconName, QStringList &visited) const
291 {
292     QPixmap pixmap;
293     
294     if (!themeName.isEmpty()) {
295         visited << themeName;
296         QIconTheme theme = themeList.value(themeName);
297         
298         if (!theme.isValid()) {
299             theme = parseIndexFile(themeName);
300             themeList.insert(themeName, theme);
301         }
302         
303         if (!theme.isValid())
304             return QPixmap();
305         
306         QList <QString> subDirs = theme.dirList().values(size);
307         
308         for ( int i = 0 ; i < iconDirs.size() ; ++i) {
309             for ( int j = 0 ; j < subDirs.size() ; ++j) {
310                 QString contentDir = (iconDirs[i].startsWith(QDir::homePath())) ?
311                                      QLatin1String("/.icons/") : QLatin1String("/icons/");
312                 QString fileName = iconDirs[i] + contentDir + themeName + QLatin1Char('/') + subDirs[j] + QLatin1Char('/') + iconName;
313
314                 QString svgExtension(QLatin1String(".svg"));
315                 QString svgFilename = fileName + svgExtension;
316                 QFile svgFile(svgFilename);
317                 if (false && svgFile.exists()) {
318                     // qDebug() << "Found svg";
319                     pixmap.load(svgFilename);
320                 } else {
321                     QString pngExtension(QLatin1String(".png"));
322                     QString pngFilename = fileName + pngExtension;
323                     QFile pngFile(pngFilename);
324                     if (pngFile.exists()) {
325                         // qDebug() << "Found png";
326                         pixmap.load(pngFilename);
327                     }
328                 }
329
330                 if (!pixmap.isNull())
331                     break;
332             }
333         }
334         
335         if (pixmap.isNull()) {
336             QStringList parents = theme.parents();
337             //search recursively through inherited themes
338             for (int i = 0 ; pixmap.isNull() && i < parents.size() ; ++i) {
339                 QString parentTheme = parents[i].trimmed();
340                 if (!visited.contains(parentTheme)) //guard against endless recursion
341                     pixmap = findIconHelper(size, parentTheme, iconName, visited);
342             }
343         }
344     }
345     return pixmap;
346 }
347
348 QPixmap QtIconLoaderImplementation::findIcon(int size, const QString &name) const
349 {
350     QPixmap pixmap;
351     QString pixmapName = QLatin1String("$qt") + name + QString::number(size);
352     if (QPixmapCache::find(pixmapName, pixmap))
353         return pixmap;
354     
355     if (!themeName.isEmpty()) {
356         QStringList visited;
357         pixmap = findIconHelper(size, themeName, name, visited);
358     }
359     QPixmapCache::insert(pixmapName, pixmap);
360     return pixmap;
361 }
362 #endif //Q_WS_X11