/* * * This file is part of the KDE project. * Copyright (C) 2007 Rivo Laks * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kiconcache.h" #include #include #include #include #include #include #include #include #include #include #include #define KDE_ICONCACHE_NAME "kde-icon-cache" #define KDE_ICONCACHE_VERSION 0x000100 class KIconCache::Private { public: Private(KIconCache* _q) { q = _q; mUpdatesCheckedTime = 0; } bool themeDirsChanged() { if (q->existingIconThemeDirs(mThemeNames) != mThemeDirs || q->mostRecentMTime(mThemeDirs) != mThemesMTime) { kDebug(264) << "Theme directory has been modified"; return true; } else { return false; } } void checkForThemeUpdates() { if (!q->isEnabled()) { return; } // Don't check more often than every 5 secs const quint32 now = ::time(0); if (now < mUpdatesCheckedTime + 5) { return; } mUpdatesCheckedTime = now; // Perhaps another process has already checked for updates in last 5 secs const QFileInfo fi(mUpdatesFile); if (fi.exists() && fi.lastModified().toTime_t() + 5 > now) { return; } // Check for theme updates if (themeDirsChanged()) { // Update themes info and discard the cache mThemeDirs = q->existingIconThemeDirs(mThemeNames); mThemesMTime = q->mostRecentMTime(mThemeDirs); q->discard(); } // Update timestamp file QFile f(mUpdatesFile); f.open(QIODevice::WriteOnly); } KIconCache* q; qint32 mDefaultIconSize[6]; QStringList mThemeNames; QSet mThemeDirs; quint32 mThemesMTime; QString mUpdatesFile; quint32 mUpdatesCheckedTime; QString* mLoadPath; QString mSavePath; }; KIconCache::KIconCache() : KPixmapCache(KDE_ICONCACHE_NAME), d(new Private(this)) { d->mUpdatesFile = KGlobal::dirs()->locateLocal("cache", "kpc/"KDE_ICONCACHE_NAME".updated"); // Set limit to 10 MB setCacheLimit(10 * 1024); } KIconCache::~KIconCache() { delete d; } void KIconCache::deleteCache() { KPixmapCache::deleteCache(KDE_ICONCACHE_NAME); } bool KIconCache::loadCustomIndexHeader(QDataStream& stream) { if (stream.atEnd()) { return false; } // Load version quint32 version; stream >> version; if (version != KDE_ICONCACHE_VERSION) { kDebug(264) << "Obsolete iconcache version" << version << "will recreate"; return false; } // Load default sizes of icons for (int i = 0; i < 6; i++) { stream >> d->mDefaultIconSize[i]; } stream >> d->mThemeNames; stream >> d->mThemeDirs; // TODO: use KPixmapCache's timestamp instead stream >> d->mThemesMTime; if (stream.status() != QDataStream::Ok) { kWarning() << "Failed to read index file's header"; recreateCacheFiles(); return false; } // Make sure at least one theme was read if (!d->mThemeNames.count()) { kDebug(264) << "Empty themes list"; return false; } // Make sure the theme dirs haven't changed if (d->themeDirsChanged()) { return false; } d->mUpdatesCheckedTime= ::time(0); return true; } void KIconCache::writeCustomIndexHeader(QDataStream& stream) { setValid(false); stream << (quint32)KDE_ICONCACHE_VERSION; for (int i = 0; i < 6; i++) { stream << d->mDefaultIconSize[i]; } // Save iconthemes info stream << d->mThemeNames; stream << d->mThemeDirs; stream << d->mThemesMTime; // Cache is valid if header was successfully written and we actually have // the icontheme name(s) if (stream.status() == QDataStream::Ok && d->mThemeNames.count()) { setValid(true); } } QSet KIconCache::existingIconThemeDirs(const QStringList& themeNames) const { // Find all possible icontheme dirs // This has been taken from kicontheme.cpp QStringList icondirs = KGlobal::dirs()->resourceDirs("icon") << KGlobal::dirs()->resourceDirs("xdgdata-icon") << "/usr/share/pixmaps/" // These are not in the icon spec, but e.g. GNOME puts some icons there anyway. << KGlobal::dirs()->resourceDirs("xdgdata-pixmap"); icondirs.removeDuplicates(); // Check which of theme actually contain existing dir of one of the // given themes QSet dirs; foreach (const QString it, icondirs) { foreach (const QString themeIt, themeNames) { QString dirName = it + themeIt + '/'; if (KGlobal::dirs()->exists(dirName)) { dirs.insert(dirName); } } } return dirs; } unsigned int KIconCache::mostRecentMTime(const QSet& dirNames) const { unsigned int timestamp = 0; foreach (const QString &dir, dirNames) { unsigned int mtime = QFileInfo(dir).lastModified().toTime_t(); if (timestamp < mtime) { timestamp = mtime; } } return timestamp; } int KIconCache::defaultIconSize(KIconLoader::Group group) const { if ((group < 0) || (group >= KIconLoader::LastGroup)) { kDebug(264) << "Illegal icon group:" << group; return -1; } return d->mDefaultIconSize[group]; } void KIconCache::setThemeInfo(const QList& themes) { if (themes.isEmpty()) { for (KIconLoader::Group i = KIconLoader::FirstGroup; i < KIconLoader::LastGroup; ++i) { d->mDefaultIconSize[i] = 0; } return; } // This as to be done always, even if the cache itself is disabled for (KIconLoader::Group i = KIconLoader::FirstGroup; i < KIconLoader::LastGroup; ++i) { d->mDefaultIconSize[i] = themes.first()->defaultSize(i); } if (!isEnabled()) { return; } setValid(false); // Save internal names and dirs of all themes d->mThemeNames.clear(); foreach (KIconTheme* theme, themes) { d->mThemeNames.append(theme->internalName()); } d->mThemeDirs = existingIconThemeDirs(d->mThemeNames); d->mThemesMTime = mostRecentMTime(d->mThemeDirs); d->mUpdatesCheckedTime= ::time(0); // Recreate the cache recreateCacheFiles(); } bool KIconCache::find(const QString& key, QPixmap& pix, QString* path) { d->checkForThemeUpdates(); d->mLoadPath = path; // We can use QPixmapCache only if we don't need the path setUseQPixmapCache(!path); //kDebug(264) << "use QPC = " << useQPixmapCache(); bool ret = find(key, pix); d->mLoadPath = 0; return ret; } void KIconCache::insert(const QString& key, const QPixmap& pix, const QString& path) { d->mSavePath = path; insert(key, pix); d->mSavePath.clear(); } bool KIconCache::find(const QString& key, QPixmap& pix) { // TODO: make sure that cache is still valid return KPixmapCache::find(key, pix); } void KIconCache::insert(const QString& key, const QPixmap& pix) { // TODO: make sure that cache is still valid KPixmapCache::insert(key, pix); } bool KIconCache::loadCustomData(QDataStream& stream) { QString path; stream >> path; if (d->mLoadPath) { *d->mLoadPath = path; } return true; } bool KIconCache::writeCustomData(QDataStream& stream) { stream << d->mSavePath; return true; }