]> git.sur5r.net Git - minitube/blob - src/iconloader/qticonloader.cpp
Imported Upstream version 1.6
[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 #ifndef Q_WS_X11
52 #include "extra.h"
53 #endif
54
55 #ifdef Q_WS_X11
56
57 class QIconTheme
58 {
59 public:
60     QIconTheme(QHash <int, QString> dirList, QStringList parents) :
61             _dirList(dirList), _parents(parents), _valid(true){ }
62     QIconTheme() : _valid(false){ }
63     QHash <int, QString> dirList() {return _dirList;}
64     QStringList parents() {return _parents;}
65     bool isValid() {return _valid;}
66
67 private:
68     QHash <int, QString> _dirList;
69     QStringList _parents;
70     bool _valid;
71 };
72
73 class QtIconLoaderImplementation
74 {
75 public:
76     QtIconLoaderImplementation();
77     QPixmap findIcon(int size, const QString &name) const;
78
79 private:
80     QIconTheme parseIndexFile(const QString &themeName) const;
81     void lookupIconTheme() const;
82     QPixmap findIconHelper(int size,
83                            const QString &themeName,
84                            const QString &iconName,
85                            QStringList &visited) const;
86     mutable QString themeName;
87     mutable QStringList iconDirs;
88     mutable QHash <QString, QIconTheme> themeList;
89 };
90
91 Q_GLOBAL_STATIC(QtIconLoaderImplementation, iconLoaderInstance)
92 #endif
93
94         /*!
95
96     Returns the standard icon for the given icon /a name
97     as specified in the freedesktop icon spec
98     http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
99
100     /a fallback is an optional argument to specify the icon to be used if
101     no icon is found on the platform. This is particularily useful for
102     crossplatform code.
103
104 */
105         QIcon QtIconLoader::icon(const QString &name)
106 {
107     QIcon icon;
108
109 #ifdef Q_WS_X11
110 #if QT_VERSION < 0x040600
111     QString pngExtension(QLatin1String(".png"));
112     QList<int> iconSizes;
113     iconSizes << 16 << 22 << 24 << 32 << 48;
114     Q_FOREACH (int size, iconSizes) {
115         icon.addPixmap(iconLoaderInstance()->findIcon(size, name));
116     }
117 #else
118     icon = QIcon::fromTheme(name);
119 #endif
120 #else
121         icon = Extra::getIcon(name);
122 #endif
123
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
317                 QString svgExtension(QLatin1String(".svg"));
318                 QString svgFilename = fileName + svgExtension;
319                 QFile svgFile(svgFilename);
320                 if (false && svgFile.exists()) {
321                     // qDebug() << "Found svg";
322                     pixmap.load(svgFilename);
323                 } else {
324                     QString pngExtension(QLatin1String(".png"));
325                     QString pngFilename = fileName + pngExtension;
326                     QFile pngFile(pngFilename);
327                     if (pngFile.exists()) {
328                         // qDebug() << "Found png";
329                         pixmap.load(pngFilename);
330                     }
331                 }
332
333                 if (!pixmap.isNull())
334                     break;
335             }
336         }
337         
338         if (pixmap.isNull()) {
339             QStringList parents = theme.parents();
340             //search recursively through inherited themes
341             for (int i = 0 ; pixmap.isNull() && i < parents.size() ; ++i) {
342                 QString parentTheme = parents[i].trimmed();
343                 if (!visited.contains(parentTheme)) //guard against endless recursion
344                     pixmap = findIconHelper(size, parentTheme, iconName, visited);
345             }
346         }
347     }
348     return pixmap;
349 }
350
351 QPixmap QtIconLoaderImplementation::findIcon(int size, const QString &name) const
352 {
353     QPixmap pixmap;
354     QString pixmapName = QLatin1String("$qt") + name + QString::number(size);
355     if (QPixmapCache::find(pixmapName, pixmap))
356         return pixmap;
357     
358     if (!themeName.isEmpty()) {
359         QStringList visited;
360         pixmap = findIconHelper(size, themeName, name, visited);
361     }
362     QPixmapCache::insert(pixmapName, pixmap);
363     return pixmap;
364 }
365 #endif //Q_WS_X11