qticonloader.cpp
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041 #include "qticonloader.h"
00042 #include <QtGui/QPixmapCache>
00043
00044 #include <QtCore/QList>
00045 #include <QtCore/QHash>
00046 #include <QtCore/QDir>
00047 #include <QtCore/QString>
00048 #include <QtCore/QLibrary>
00049 #include <QtCore/QSettings>
00050 #include <QtCore/QTextStream>
00051
00052 #ifdef Q_WS_X11
00053
00054 extern "C" {
00055 struct GConfClient;
00056 struct GError;
00057 typedef void (*Ptr_g_type_init)();
00058 typedef GConfClient* (*Ptr_gconf_client_get_default)();
00059 typedef char* (*Ptr_gconf_client_get_string)(GConfClient*, const char*, GError **);
00060 typedef void (*Ptr_g_object_unref)(void *);
00061 typedef void (*Ptr_g_error_free)(GError *);
00062 typedef void (*Ptr_g_free)(void*);
00063 static Ptr_g_type_init p_g_type_init = 0;
00064 static Ptr_gconf_client_get_default p_gconf_client_get_default = 0;
00065 static Ptr_gconf_client_get_string p_gconf_client_get_string = 0;
00066 static Ptr_g_object_unref p_g_object_unref = 0;
00067 static Ptr_g_error_free p_g_error_free = 0;
00068 static Ptr_g_free p_g_free = 0;
00069 }
00070
00071
00072 static int kdeVersion()
00073 {
00074 static int version = qgetenv("KDE_SESSION_VERSION").toInt();
00075 return version;
00076 }
00077
00078 static QString kdeHome()
00079 {
00080 static QString kdeHomePath;
00081 if (kdeHomePath.isEmpty()) {
00082 kdeHomePath = QFile::decodeName(qgetenv("KDEHOME"));
00083 if (kdeHomePath.isEmpty()) {
00084 int kdeSessionVersion = kdeVersion();
00085 QDir homeDir(QDir::homePath());
00086 QString kdeConfDir(QLatin1String("/.kde"));
00087 if (4 == kdeSessionVersion && homeDir.exists(QLatin1String(".kde4")))
00088 kdeConfDir = QLatin1String("/.kde4");
00089 kdeHomePath = QDir::homePath() + kdeConfDir;
00090 }
00091 }
00092 return kdeHomePath;
00093 }
00094
00095
00096 static QString systemThemeName()
00097 {
00098 QString themeName;
00099 #ifdef Q_WS_X11
00100
00101
00102 if (qgetenv("DESKTOP_SESSION") == "gnome" ||
00103 !qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty()) {
00104
00105 if (themeName.isEmpty()) {
00106
00107
00108 p_g_type_init = (Ptr_g_type_init)QLibrary::resolve(QLatin1String("gobject-2.0"), 0, "g_type_init");
00109 p_gconf_client_get_default = (Ptr_gconf_client_get_default)QLibrary::resolve(QLatin1String("gconf-2"), 4, "gconf_client_get_default");
00110 p_gconf_client_get_string = (Ptr_gconf_client_get_string)QLibrary::resolve(QLatin1String("gconf-2"), 4, "gconf_client_get_string");
00111 p_g_object_unref = (Ptr_g_object_unref)QLibrary::resolve(QLatin1String("gobject-2.0"), 0, "g_object_unref");
00112 p_g_error_free = (Ptr_g_error_free)QLibrary::resolve(QLatin1String("glib-2.0"), 0, "g_error_free");
00113 p_g_free = (Ptr_g_free)QLibrary::resolve(QLatin1String("glib-2.0"), 0, "g_free");
00114
00115 if (p_g_type_init && p_gconf_client_get_default &&
00116 p_gconf_client_get_string && p_g_object_unref &&
00117 p_g_error_free && p_g_free) {
00118
00119 p_g_type_init();
00120 GConfClient* client = p_gconf_client_get_default();
00121 GError *err = 0;
00122
00123 char *str = p_gconf_client_get_string(client, "/desktop/gnome/interface/icon_theme", &err);
00124 if (!err) {
00125 themeName = QString::fromUtf8(str);
00126 p_g_free(str);
00127 }
00128
00129 p_g_object_unref(client);
00130 if (err)
00131 p_g_error_free (err);
00132
00133 }
00134 if (themeName.isEmpty())
00135 themeName = QLatin1String("gnome");
00136 }
00137
00138 if (!themeName.isEmpty())
00139 return themeName;
00140 }
00141
00142 QStringList kdeDirs = QFile::decodeName(getenv("KDEDIRS")).split(QLatin1Char(':'));
00143
00144 bool kde4 = kdeVersion() >= 4;
00145 QString defaultPath = kde4 ?
00146 QLatin1String("/usr/share/icons/default.kde4") :
00147 QLatin1String("/usr/share/icons/default.kde");
00148
00149 QFileInfo fileInfo(defaultPath);
00150 QDir dir(fileInfo.canonicalFilePath());
00151 QString kdeDefault = kde4 ?
00152 QString::fromLatin1("oxygen") :
00153 QString::fromLatin1("crystalsvg");
00154 QString defaultTheme = fileInfo.exists() ? dir.dirName() : kdeDefault;
00155
00156 QSettings settings(kdeHome() + QLatin1String("/share/config/kdeglobals"), QSettings::IniFormat);
00157 settings.beginGroup(QLatin1String("Icons"));
00158 themeName = settings.value(QLatin1String("Theme"), defaultTheme).toString();
00159
00160 #endif
00161 return themeName;
00162 }
00163
00164 struct QIconDirInfo
00165 {
00166 enum Type { Fixed, Scalable, Threshold };
00167 QIconDirInfo(const QString &_path = QString()) :
00168 path(_path),
00169 size(0),
00170 maxSize(0),
00171 minSize(0),
00172 threshold(0),
00173 type(Threshold) {}
00174 QString path;
00175 short size;
00176 short maxSize;
00177 short minSize;
00178 short threshold;
00179 Type type : 4;
00180 };
00181
00182 class QIconLoaderEngineEntry
00183 {
00184 public:
00185 virtual ~QIconLoaderEngineEntry() {}
00186 QString filename;
00187 QIconDirInfo dir;
00188 static int count;
00189 };
00190
00191 struct ScalableEntry : public QIconLoaderEngineEntry
00192 {
00193 QIcon svgIcon;
00194 };
00195
00196 struct PixmapEntry : public QIconLoaderEngineEntry
00197 {
00198 QPixmap basePixmap;
00199 };
00200
00201 typedef QList<QIconLoaderEngineEntry*> QThemeIconEntries;
00202
00203 class QIconTheme
00204 {
00205 public:
00206 QIconTheme(const QString &name);
00207 QIconTheme() : m_valid(false) {};
00208 QStringList parents() { return m_parents; }
00209 QList <QIconDirInfo> keyList() { return m_keyList; }
00210 QString contentDir() { return m_contentDir; }
00211 bool isValid() { return m_valid; }
00212
00213 private:
00214 QString m_contentDir;
00215 QList <QIconDirInfo> m_keyList;
00216 QStringList m_parents;
00217 bool m_valid;
00218 };
00219
00220
00221 class QtIconLoaderImplementation
00222 {
00223 public:
00224 QtIconLoaderImplementation();
00225 QIcon loadIcon(const QString &name);
00226
00227 private:
00228 QString themeName() const { return m_userTheme.isEmpty() ? m_systemTheme : m_userTheme; }
00229
00230 QThemeIconEntries findIconHelper(const QString &themeName,
00231 const QString &iconName,
00232 QStringList &visited) const;
00233
00234 uint m_themeKey;
00235 bool m_supportsSvg;
00236 mutable QString m_userTheme;
00237 mutable QString m_systemTheme;
00238 mutable QStringList m_iconDirs;
00239 mutable QHash <QString, QIconTheme> themeList;
00240 };
00241
00242 Q_GLOBAL_STATIC(QtIconLoaderImplementation, iconLoaderInstance)
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255 QIcon QtIconLoaderImplementation::loadIcon(const QString &name)
00256 {
00257 QIcon icon;
00258 QString theme = themeName();
00259 if (!theme.isEmpty()) {
00260 QStringList visited;
00261 QThemeIconEntries entries = findIconHelper(theme, name, visited);
00262 for (int i = 0; i < entries.size() ; ++i) {
00263 int size = entries.at(i)->dir.size;
00264 icon.addFile(entries.at(i)->filename,
00265 QSize(size, size),
00266 QIcon::Normal, QIcon::Off);
00267 }
00268 }
00269 return icon;
00270 }
00271
00272 QtIconLoaderImplementation::QtIconLoaderImplementation()
00273 {
00274 m_systemTheme = systemThemeName();
00275
00276
00277
00278
00279
00280
00281 m_supportsSvg = false;
00282 }
00283
00284 static QString fallbackTheme()
00285 {
00286 QString defaultTheme = systemThemeName();
00287 if (defaultTheme.isEmpty())
00288 defaultTheme = QLatin1String("hicolor");
00289 return defaultTheme;
00290 }
00291
00292 static QStringList themeSearchPaths()
00293 {
00294 QStringList iconDirs;
00295 if (iconDirs.isEmpty()) {
00296
00297 #if defined(Q_WS_X11)
00298
00299 QString xdgDirString = QFile::decodeName(getenv("XDG_DATA_DIRS"));
00300 if (xdgDirString.isEmpty())
00301 xdgDirString = QLatin1String("/usr/local/share/:/usr/share/");
00302
00303 QStringList xdgDirs = xdgDirString.split(QLatin1Char(':'));
00304
00305 for (int i = 0 ; i < xdgDirs.size() ; ++i) {
00306 QDir dir(xdgDirs[i]);
00307 if (dir.exists())
00308 iconDirs.append(dir.path() +
00309 QLatin1String("/icons"));
00310 }
00311
00312 if (kdeVersion() != 0) {
00313
00314 iconDirs << QLatin1Char(':') +
00315 kdeHome() + QLatin1String("/share/icons");
00316 QStringList kdeDirs =
00317 QFile::decodeName(getenv("KDEDIRS")).split(QLatin1Char(':'));
00318
00319 for (int i = 0 ; i< kdeDirs.count() ; ++i) {
00320 QDir dir(QLatin1Char(':') + kdeDirs.at(i) +
00321 QLatin1String("/share/icons"));
00322 if (dir.exists())
00323 iconDirs.append(dir.path());
00324 }
00325 }
00326
00327
00328 QDir homeDir(QDir::homePath() + QLatin1String("/.icons"));
00329 if (homeDir.exists())
00330 iconDirs.prepend(homeDir.path());
00331
00332 #elif defined(Q_WS_WIN)
00333 m_iconDirs.append(qApp->applicationDirPath() +
00334 QLatin1String("/icons"));
00335 #elif defined(Q_WS_MAC)
00336 m_iconDirs.append(qApp->applicationDirPath() +
00337 QLatin1String("/../Resources/icons"));
00338 #endif
00339 }
00340 return iconDirs;
00341 }
00342
00343
00344 QIconTheme::QIconTheme(const QString &themeName)
00345 : m_valid(false)
00346 {
00347 QFile themeIndex;
00348
00349 QList <QIconDirInfo> keyList;
00350 QStringList iconDirs = themeSearchPaths();
00351 for ( int i = 0 ; i < iconDirs.size() ; ++i) {
00352 QDir iconDir(iconDirs[i]);
00353 QString themeDir = iconDir.path() + QLatin1Char('/') + themeName;
00354 themeIndex.setFileName(themeDir + QLatin1String("/index.theme"));
00355 if (themeIndex.exists()) {
00356 m_contentDir = themeDir;
00357 m_valid = true;
00358 break;
00359 }
00360 }
00361
00362 if (themeIndex.exists()) {
00363 const QSettings indexReader(themeIndex.fileName(), QSettings::IniFormat);
00364 QStringListIterator keyIterator(indexReader.allKeys());
00365 while (keyIterator.hasNext()) {
00366
00367 const QString key = keyIterator.next();
00368 if (key.endsWith(QLatin1String("/Size"))) {
00369
00370
00371 if (int size = indexReader.value(key).toInt()) {
00372 QString directoryKey = key.left(key.size() - 5);
00373 QIconDirInfo dirInfo(directoryKey);
00374 dirInfo.size = size;
00375 QString type = indexReader.value(directoryKey +
00376 QLatin1String("/Type")
00377 ).toString();
00378
00379 if (type == QLatin1String("Fixed"))
00380 dirInfo.type = QIconDirInfo::Fixed;
00381 else if (type == QLatin1String("Scalable"))
00382 dirInfo.type = QIconDirInfo::Scalable;
00383 else
00384 dirInfo.type = QIconDirInfo::Threshold;
00385
00386 dirInfo.threshold = indexReader.value(directoryKey +
00387 QLatin1String("/Threshold"),
00388 2).toInt();
00389
00390 dirInfo.minSize = indexReader.value(directoryKey +
00391 QLatin1String("/MinSize"),
00392 size).toInt();
00393
00394 dirInfo.maxSize = indexReader.value(directoryKey +
00395 QLatin1String("/MaxSize"),
00396 size).toInt();
00397 m_keyList.append(dirInfo);
00398 }
00399 }
00400 }
00401
00402
00403 m_parents = indexReader.value(
00404 QLatin1String("Icon Theme/Inherits")).toStringList();
00405
00406
00407 if (m_parents.isEmpty())
00408 m_parents.append(fallbackTheme());
00409
00410
00411 if (!m_parents.isEmpty())
00412 m_parents.append(QLatin1String("hicolor"));
00413 }
00414 }
00415
00416
00417 QThemeIconEntries QtIconLoaderImplementation::findIconHelper(const QString &themeName,
00418 const QString &iconName,
00419 QStringList &visited) const
00420 {
00421 QThemeIconEntries entries;
00422 Q_ASSERT(!themeName.isEmpty());
00423
00424 QPixmap pixmap;
00425
00426
00427 visited << themeName;
00428
00429 QIconTheme theme = themeList.value(themeName);
00430 if (!theme.isValid()) {
00431 theme = QIconTheme(themeName);
00432 if (!theme.isValid())
00433 theme = fallbackTheme();
00434
00435 themeList.insert(themeName, theme);
00436 }
00437
00438 QString contentDir = theme.contentDir() + QLatin1Char('/');
00439 QList<QIconDirInfo> subDirs = theme.keyList();
00440
00441 const QString svgext(QLatin1String(".svg"));
00442 const QString pngext(QLatin1String(".png"));
00443
00444
00445 for (int i = 0; i < subDirs.size() ; ++i) {
00446 const QIconDirInfo &dirInfo = subDirs.at(i);
00447 QString subdir = dirInfo.path;
00448 QDir currentDir(contentDir + subdir);
00449
00450 if (dirInfo.type == QIconDirInfo::Scalable && m_supportsSvg &&
00451 currentDir.exists(iconName + svgext)) {
00452 ScalableEntry *iconEntry = new ScalableEntry;
00453 iconEntry->dir = dirInfo;
00454 iconEntry->filename = currentDir.filePath(iconName + svgext);
00455 entries.append(iconEntry);
00456
00457 } else if (currentDir.exists(iconName + pngext)) {
00458 PixmapEntry *iconEntry = new PixmapEntry;
00459 iconEntry->dir = dirInfo;
00460 iconEntry->filename = currentDir.filePath(iconName + pngext);
00461
00462
00463 entries.prepend(iconEntry);
00464 }
00465 }
00466
00467 if (entries.isEmpty()) {
00468 const QStringList parents = theme.parents();
00469
00470 for (int i = 0 ; i < parents.size() ; ++i) {
00471
00472 const QString parentTheme = parents.at(i).trimmed();
00473
00474 if (!visited.contains(parentTheme))
00475 entries = findIconHelper(parentTheme, iconName, visited);
00476
00477 if (!entries.isEmpty())
00478 break;
00479 }
00480 }
00481 return entries;
00482 }
00483
00484 #endif //Q_WS_X11
00485
00486 QIcon QtIconLoader::icon(const QString &name, const QIcon &fallback)
00487 {
00488 QIcon icon;
00489 #ifdef Q_WS_X11
00490 icon = iconLoaderInstance()->loadIcon(name);
00491 #endif //Q_WS_X11
00492 if (icon.isNull())
00493 icon = fallback;
00494 Q_UNUSED(name);
00495 return icon;
00496 }