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