]> git.sur5r.net Git - minitube/blob - src/iconloader/qticonloader.cpp
551e1acb75d715b0b170631906ac687122d20654
[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, const QIcon &fallback)
103 {
104     QIcon icon;
105
106 #if QT_VERSION < 0x040600
107
108 #ifdef Q_WS_X11
109     QString pngExtension(QLatin1String(".png"));
110     QList<int> iconSizes;
111     iconSizes << 16 << 24 << 32 << 48 << 64;
112     Q_FOREACH (int size, iconSizes) {
113         icon.addPixmap(iconLoaderInstance()->findIcon(size, name + pngExtension));
114     }
115 #endif
116
117 #else
118     icon = QIcon::fromTheme(name, fallback);
119 #endif
120
121     if (icon.isNull())
122         icon = fallback;
123     Q_UNUSED(name);
124     return icon;
125 }
126
127 #ifdef Q_WS_X11
128
129 QtIconLoaderImplementation::QtIconLoaderImplementation()
130 {
131     lookupIconTheme();
132 }
133
134 extern "C" {
135     struct GConfClient;
136     struct GError;
137     typedef void (*Ptr_g_type_init)();
138     typedef GConfClient* (*Ptr_gconf_client_get_default)();
139     typedef char* (*Ptr_gconf_client_get_string)(GConfClient*, const char*, GError **);
140     typedef void (*Ptr_g_object_unref)(void *);
141     typedef void (*Ptr_g_error_free)(GError *);
142     typedef void (*Ptr_g_free)(void*);
143     static Ptr_g_type_init p_g_type_init = 0;
144     static Ptr_gconf_client_get_default p_gconf_client_get_default = 0;
145     static Ptr_gconf_client_get_string p_gconf_client_get_string = 0;
146     static Ptr_g_object_unref p_g_object_unref = 0;
147     static Ptr_g_error_free p_g_error_free = 0;
148     static Ptr_g_free p_g_free = 0;
149 }
150
151
152 static int kdeVersion()
153 {
154     static int version = qgetenv("KDE_SESSION_VERSION").toInt();
155     return version;
156 }
157
158 static QString kdeHome()
159 {
160     static QString kdeHomePath;
161     if (kdeHomePath.isEmpty()) {
162         kdeHomePath = QFile::decodeName(qgetenv("KDEHOME"));
163         if (kdeHomePath.isEmpty()) {
164             int kdeSessionVersion = kdeVersion();
165             QDir homeDir(QDir::homePath());
166             QString kdeConfDir(QLatin1String("/.kde"));
167             if (4 == kdeSessionVersion && homeDir.exists(QLatin1String(".kde4")))
168                 kdeConfDir = QLatin1String("/.kde4");
169             kdeHomePath = QDir::homePath() + kdeConfDir;
170         }
171     }
172     return kdeHomePath;
173 }
174
175 void QtIconLoaderImplementation::lookupIconTheme() const
176 {
177     
178 #ifdef Q_WS_X11
179     QString dataDirs = QFile::decodeName(getenv("XDG_DATA_DIRS"));
180     if (dataDirs.isEmpty())
181         dataDirs = QLatin1String("/usr/local/share/:/usr/share/");
182     
183     dataDirs.prepend(QDir::homePath() + QLatin1String("/:"));
184     iconDirs = dataDirs.split(QLatin1Char(':'));
185     
186     // If we are running GNOME we resolve and use GConf. In all other
187     // cases we currently use the KDE icon theme
188     
189     if (qgetenv("DESKTOP_SESSION") == "gnome" ||
190         !qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty()) {
191         
192         if (themeName.isEmpty()) {
193             // Resolve glib and gconf
194             
195             p_g_type_init =              (Ptr_g_type_init)QLibrary::resolve(QLatin1String("gobject-2.0"), 0, "g_type_init");
196             p_gconf_client_get_default = (Ptr_gconf_client_get_default)QLibrary::resolve(QLatin1String("gconf-2"), 4, "gconf_client_get_default");
197             p_gconf_client_get_string =  (Ptr_gconf_client_get_string)QLibrary::resolve(QLatin1String("gconf-2"), 4, "gconf_client_get_string");
198             p_g_object_unref =           (Ptr_g_object_unref)QLibrary::resolve(QLatin1String("gobject-2.0"), 0, "g_object_unref");
199             p_g_error_free =             (Ptr_g_error_free)QLibrary::resolve(QLatin1String("glib-2.0"), 0, "g_error_free");
200             p_g_free =                   (Ptr_g_free)QLibrary::resolve(QLatin1String("glib-2.0"), 0, "g_free");
201             
202             if (p_g_type_init && p_gconf_client_get_default &&
203                 p_gconf_client_get_string && p_g_object_unref &&
204                 p_g_error_free && p_g_free) {
205                 
206                 p_g_type_init();
207                 GConfClient* client = p_gconf_client_get_default();
208                 GError *err = 0;
209                 
210                 char *str = p_gconf_client_get_string(client, "/desktop/gnome/interface/icon_theme", &err);
211                 if (!err) {
212                     themeName = QString::fromUtf8(str);
213                     p_g_free(str);
214                 }
215                 
216                 p_g_object_unref(client);
217                 if (err)
218                     p_g_error_free (err);
219             }
220             if (themeName.isEmpty())
221                 themeName = QLatin1String("gnome");
222         }
223         
224         if (!themeName.isEmpty())
225             return;
226     }
227     
228     // KDE (and others)
229     if (dataDirs.isEmpty())
230         dataDirs = QLatin1String("/usr/local/share/:/usr/share/");
231     
232     dataDirs += QLatin1Char(':') + kdeHome() + QLatin1String("/share");
233     dataDirs.prepend(QDir::homePath() + QLatin1String("/:"));
234     QStringList kdeDirs = QFile::decodeName(getenv("KDEDIRS")).split(QLatin1Char(':'));
235     Q_FOREACH (const QString dirName, kdeDirs)
236         dataDirs.append(QLatin1Char(':') + dirName + QLatin1String("/share"));
237     iconDirs = dataDirs.split(QLatin1Char(':'));
238     
239     QFileInfo fileInfo(QLatin1String("/usr/share/icons/default.kde"));
240     QDir dir(fileInfo.canonicalFilePath());
241     QString kdeDefault = kdeVersion() >= 4 ? QString::fromLatin1("oxygen") : QString::fromLatin1("crystalsvg");
242     QString defaultTheme = fileInfo.exists() ? dir.dirName() : kdeDefault;
243     QSettings settings(kdeHome() + QLatin1String("/share/config/kdeglobals"), QSettings::IniFormat);
244     settings.beginGroup(QLatin1String("Icons"));
245     themeName = settings.value(QLatin1String("Theme"), defaultTheme).toString();
246 #endif
247 }
248
249 QIconTheme QtIconLoaderImplementation::parseIndexFile(const QString &themeName) const
250 {
251     QIconTheme theme;
252     QFile themeIndex;
253     QStringList parents;
254     QHash <int, QString> dirList;
255
256     for ( int i = 0 ; i < iconDirs.size() && !themeIndex.exists() ; ++i) {
257         const QString &contentDir = QLatin1String(iconDirs[i].startsWith(QDir::homePath()) ? "/.icons/" : "/icons/");
258         themeIndex.setFileName(iconDirs[i] + contentDir + themeName + QLatin1String("/index.theme"));
259     }
260
261     if (themeIndex.exists()) {
262         QSettings indexReader(themeIndex.fileName(), QSettings::IniFormat);
263         Q_FOREACH (const QString &key, indexReader.allKeys()) {
264             if (key.endsWith("/Size")) {
265                 if (int size = indexReader.value(key).toInt())
266                     dirList.insertMulti(size, key.left(key.size() - 5));
267             }
268         }
269
270         // Parent themes provide fallbacks for missing icons
271         // parents = indexReader.value(QLatin1String("Icon Theme/Inherits")).toString().split(QLatin1Char(','));
272         parents = indexReader.value(QLatin1String("Icon Theme/Inherits")).toStringList();
273
274
275     }
276
277     if (kdeVersion() >= 3) {
278         QFileInfo fileInfo(QLatin1String("/usr/share/icons/default.kde"));
279         QDir dir(fileInfo.canonicalFilePath());
280         QString defaultKDETheme = dir.exists() ? dir.dirName() : kdeVersion() == 3 ?
281                                   QString::fromLatin1("crystalsvg") : QString::fromLatin1("oxygen");
282         if (!parents.contains(defaultKDETheme) && themeName != defaultKDETheme)
283             parents.append(defaultKDETheme);
284     } else if (parents.isEmpty() && themeName != QLatin1String("hicolor")) {
285         parents.append(QLatin1String("hicolor"));
286     }
287     
288     theme = QIconTheme(dirList, parents);
289     return theme;
290 }
291
292 QPixmap QtIconLoaderImplementation::findIconHelper(int size, const QString &themeName,
293                                                    const QString &iconName, QStringList &visited) const
294 {
295     QPixmap pixmap;
296     
297     if (!themeName.isEmpty()) {
298         visited << themeName;
299         QIconTheme theme = themeList.value(themeName);
300         
301         if (!theme.isValid()) {
302             theme = parseIndexFile(themeName);
303             themeList.insert(themeName, theme);
304         }
305         
306         if (!theme.isValid())
307             return QPixmap();
308         
309         QList <QString> subDirs = theme.dirList().values(size);
310         
311         for ( int i = 0 ; i < iconDirs.size() ; ++i) {
312             for ( int j = 0 ; j < subDirs.size() ; ++j) {
313                 QString contentDir = (iconDirs[i].startsWith(QDir::homePath())) ?
314                                      QLatin1String("/.icons/") : QLatin1String("/icons/");
315                 QString fileName = iconDirs[i] + contentDir + themeName + QLatin1Char('/') + subDirs[j] + QLatin1Char('/') + iconName;
316                 QFile file(fileName);
317                 if (file.exists())
318                     pixmap.load(fileName);
319                 if (!pixmap.isNull())
320                     break;
321             }
322         }
323         
324         if (pixmap.isNull()) {
325             QStringList parents = theme.parents();
326             //search recursively through inherited themes
327             for (int i = 0 ; pixmap.isNull() && i < parents.size() ; ++i) {
328                 QString parentTheme = parents[i].trimmed();
329                 if (!visited.contains(parentTheme)) //guard against endless recursion
330                     pixmap = findIconHelper(size, parentTheme, iconName, visited);
331             }
332         }
333     }
334     return pixmap;
335 }
336
337 QPixmap QtIconLoaderImplementation::findIcon(int size, const QString &name) const
338 {
339     QPixmap pixmap;
340     QString pixmapName = QLatin1String("$qt") + name + QString::number(size);
341     if (QPixmapCache::find(pixmapName, pixmap))
342         return pixmap;
343     
344     if (!themeName.isEmpty()) {
345         QStringList visited;
346         pixmap = findIconHelper(size, themeName, name, visited);
347     }
348     QPixmapCache::insert(pixmapName, pixmap);
349     return pixmap;
350 }
351 #endif //Q_WS_X11