~hendrik-grewe/transmission/private-patch

« back to all changes in this revision

Viewing changes to qt/qticonloader.cc

  • Committer: charles
  • Date: 2009-04-09 17:55:47 UTC
  • Revision ID: svn-v4:f4695dd4-2c0a-0410-b89c-da849a56a58e:trunk:8188
(trunk) add the Qt beta into svn 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
#ifdef Q_WS_X11
 
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));
 
111
    }
 
112
#endif
 
113
    if (icon.isNull())
 
114
        icon = fallback;
 
115
    Q_UNUSED(name);
 
116
    return icon;
 
117
}
 
118
 
 
119
#ifdef Q_WS_X11
 
120
 
 
121
QtIconLoaderImplementation::QtIconLoaderImplementation()
 
122
{
 
123
    lookupIconTheme();
 
124
}
 
125
 
 
126
extern "C" {
 
127
    struct GConfClient;
 
128
    struct GError;
 
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;
 
141
}
 
142
 
 
143
 
 
144
static int kdeVersion()
 
145
{
 
146
    static int version = qgetenv("KDE_SESSION_VERSION").toInt();
 
147
    return version;
 
148
}
 
149
 
 
150
static QString kdeHome()
 
151
{
 
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;
 
162
        }
 
163
    }
 
164
    return kdeHomePath;
 
165
}
 
166
 
 
167
void QtIconLoaderImplementation::lookupIconTheme() const
 
168
{
 
169
    
 
170
#ifdef Q_WS_X11
 
171
    QString dataDirs = QFile::decodeName(getenv("XDG_DATA_DIRS"));
 
172
    if (dataDirs.isEmpty())
 
173
        dataDirs = QLatin1String("/usr/local/share/:/usr/share/");
 
174
    
 
175
    dataDirs.prepend(QDir::homePath() + QLatin1String("/:"));
 
176
    iconDirs = dataDirs.split(QLatin1Char(':'));
 
177
    
 
178
    // If we are running GNOME we resolve and use GConf. In all other
 
179
    // cases we currently use the KDE icon theme
 
180
    
 
181
    if (qgetenv("DESKTOP_SESSION") == "gnome" ||
 
182
        !qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty()) {
 
183
        
 
184
        if (themeName.isEmpty()) {
 
185
            // Resolve glib and gconf
 
186
            
 
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");
 
193
            
 
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) {
 
197
                
 
198
                p_g_type_init();
 
199
                GConfClient* client = p_gconf_client_get_default();
 
200
                GError *err = 0;
 
201
                
 
202
                char *str = p_gconf_client_get_string(client, "/desktop/gnome/interface/icon_theme", &err);
 
203
                if (!err) {
 
204
                    themeName = QString::fromUtf8(str);
 
205
                    p_g_free(str);
 
206
                }
 
207
                
 
208
                p_g_object_unref(client);
 
209
                if (err)
 
210
                    p_g_error_free (err);
 
211
            }
 
212
            if (themeName.isEmpty())
 
213
                themeName = QLatin1String("gnome");
 
214
        }
 
215
        
 
216
        if (!themeName.isEmpty())
 
217
            return;
 
218
    }
 
219
    
 
220
    // KDE (and others)
 
221
    if (dataDirs.isEmpty())
 
222
        dataDirs = QLatin1String("/usr/local/share/:/usr/share/");
 
223
    
 
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(':'));
 
230
    
 
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();
 
238
#endif
 
239
}
 
240
 
 
241
QIconTheme QtIconLoaderImplementation::parseIndexFile(const QString &themeName) const
 
242
{
 
243
    QIconTheme theme;
 
244
    QFile themeIndex;
 
245
    QStringList parents;
 
246
    QHash <int, QString> dirList;
 
247
 
 
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"));
 
251
    }
 
252
 
 
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));
 
259
            }
 
260
        }
 
261
 
 
262
        // Parent themes provide fallbacks for missing icons
 
263
        parents = indexReader.value(QLatin1String("Icon Theme/Inherits")).toString().split(QLatin1Char(','));
 
264
    }
 
265
 
 
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"));
 
275
    }
 
276
    
 
277
    theme = QIconTheme(dirList, parents);
 
278
    return theme;
 
279
}
 
280
 
 
281
QPixmap QtIconLoaderImplementation::findIconHelper(int size, const QString &themeName,
 
282
                                                   const QString &iconName, QStringList &visited) const
 
283
{
 
284
    QPixmap pixmap;
 
285
    
 
286
    if (!themeName.isEmpty()) {
 
287
        visited << themeName;
 
288
        QIconTheme theme = themeList.value(themeName);
 
289
        
 
290
        if (!theme.isValid()) {
 
291
            theme = parseIndexFile(themeName);
 
292
            themeList.insert(themeName, theme);
 
293
        }
 
294
        
 
295
        if (!theme.isValid())
 
296
            return QPixmap();
 
297
        
 
298
        QList <QString> subDirs = theme.dirList().values(size);
 
299
        
 
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);
 
306
                if (file.exists())
 
307
                    pixmap.load(fileName);
 
308
                if (!pixmap.isNull())
 
309
                    break;
 
310
            }
 
311
        }
 
312
        
 
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);
 
320
            }
 
321
        }
 
322
    }
 
323
    return pixmap;
 
324
}
 
325
 
 
326
QPixmap QtIconLoaderImplementation::findIcon(int size, const QString &name) const
 
327
{
 
328
    QPixmap pixmap;
 
329
    QString pixmapName = QLatin1String("$qt") + name + QString::number(size);
 
330
    if (QPixmapCache::find(pixmapName, pixmap))
 
331
        return pixmap;
 
332
    
 
333
    if (!themeName.isEmpty()) {
 
334
        QStringList visited;
 
335
        pixmap = findIconHelper(size, themeName, name, visited);
 
336
    }
 
337
    QPixmapCache::insert(pixmapName, pixmap);
 
338
    return pixmap;
 
339
}
 
340
#endif //Q_WS_X11