kdecore Library API Documentation

kicontheme.cpp

00001 /* vi: ts=8 sts=4 sw=4
00002  *
00003  * $Id: kicontheme.cpp,v 1.56 2003/11/20 21:41:54 antlarr Exp $
00004  *
00005  * This file is part of the KDE project, module kdecore.
00006  * Copyright (C) 2000 Geert Jansen <jansen@kde.org>
00007  *                    Antonio Larrosa <larrosa@kde.org>
00008  *
00009  * This is free software; it comes under the GNU Library General
00010  * Public License, version 2. See the file "COPYING.LIB" for the
00011  * exact licensing terms.
00012  *
00013  * kicontheme.cpp: Lowlevel icon theme handling.
00014  */
00015 
00016 #include <sys/stat.h>
00017 #include <unistd.h>
00018 #include <stdlib.h>
00019 #include <config.h>
00020 
00021 #include <qstring.h>
00022 #include <qstringlist.h>
00023 #include <qvaluelist.h>
00024 #include <qmap.h>
00025 #include <qpixmap.h>
00026 #include <qpixmapcache.h>
00027 #include <qimage.h>
00028 #include <qfileinfo.h>
00029 #include <qdir.h>
00030 
00031 #include <kdebug.h>
00032 #include <kstandarddirs.h>
00033 #include <kglobal.h>
00034 #include <kconfig.h>
00035 #include <ksimpleconfig.h>
00036 #include <kinstance.h>
00037 
00038 #include "kicontheme.h"
00039 
00040 class KIconThemePrivate
00041 {
00042 public:
00043     QString example, screenshot;
00044     QString linkOverlay, lockOverlay, zipOverlay, shareOverlay;
00045     bool hidden;
00046 };
00047 
00051 class KIconThemeDir
00052 {
00053 public:
00054     KIconThemeDir(const QString& dir, const KConfigBase *config);
00055 
00056     bool isValid() const { return mbValid; }
00057     QString iconPath(const QString& name) const;
00058     QStringList iconList() const;
00059     QString dir() const { return mDir; }
00060 
00061     KIcon::Context context() const { return mContext; }
00062     KIcon::Type type() const { return mType; }
00063     int size() const { return mSize; }
00064     int minSize() const { return mMinSize; }
00065     int maxSize() const { return mMaxSize; }
00066     int threshold() const { return mThreshold; }
00067 
00068 private:
00069     bool mbValid;
00070     KIcon::Type mType;
00071     KIcon::Context mContext;
00072     int mSize, mMinSize, mMaxSize;
00073     int mThreshold;
00074 
00075     QString mDir;
00076 };
00077 
00078 
00079 /*** KIconTheme ***/
00080 
00081 KIconTheme::KIconTheme(const QString& name, const QString& appName)
00082 {
00083     d = new KIconThemePrivate;
00084 
00085     QStringList icnlibs;
00086     QStringList::ConstIterator it, itDir;
00087     QStringList themeDirs;
00088     QString cDir;
00089 
00090     // Applications can have local additions to the global "locolor" and
00091     // "hicolor" icon themes. For these, the _global_ theme description
00092     // files are used..
00093 
00094     if (!appName.isEmpty() &&
00095        ( name == "crystalsvg" || name== "hicolor" || name == "locolor" ) )
00096     {
00097     icnlibs = KGlobal::dirs()->resourceDirs("data");
00098     for (it=icnlibs.begin(); it!=icnlibs.end(); ++it)
00099     {
00100         cDir = *it + appName + "/icons/" + name;
00101         if (QFile::exists( cDir ))
00102         themeDirs += cDir + "/";
00103     }
00104     }
00105     // Find the theme description file. These are always global.
00106 
00107     icnlibs = KGlobal::dirs()->resourceDirs("icon");
00108     for (it=icnlibs.begin(); it!=icnlibs.end(); ++it)
00109     {
00110         cDir = *it + name + "/";
00111         if (KStandardDirs::exists(cDir))
00112         {
00113             themeDirs += cDir;
00114         if (mDir.isEmpty()
00115             && (KStandardDirs::exists( cDir + "index.desktop") || KStandardDirs::exists( cDir + "index.theme")))
00116         mDir = cDir;
00117         }
00118     }
00119 
00120     if (mDir.isEmpty())
00121     {
00122         kdDebug(264) << "Icon theme " << name << " not found.\n";
00123         return;
00124     }
00125 
00126     QString fileName, mainSection;
00127     if(QFile::exists(mDir + "index.desktop")) {
00128     fileName = mDir + "index.desktop";
00129     mainSection="KDE Icon Theme";
00130     } else {
00131     fileName = mDir + "index.theme";
00132     mainSection="Icon Theme";
00133     }
00134     KSimpleConfig cfg(fileName);
00135     cfg.setGroup(mainSection);
00136     mName = cfg.readEntry("Name");
00137     mDesc = cfg.readEntry("Comment");
00138     mDepth = cfg.readNumEntry("DisplayDepth", 32);
00139     mInherits = cfg.readListEntry("Inherits");
00140     if ( name != "crystalsvg" )
00141       for ( QStringList::Iterator it = mInherits.begin(); it != mInherits.end(); ++it )
00142          if ( *it == "default" || *it == "hicolor" ) *it="crystalsvg";
00143 
00144     d->hidden = cfg.readBoolEntry("Hidden", false);
00145     d->example = cfg.readPathEntry("Example");
00146     d->screenshot = cfg.readPathEntry("ScreenShot");
00147     d->linkOverlay = cfg.readEntry("LinkOverlay", "link");
00148     d->lockOverlay = cfg.readEntry("LockOverlay", "lock");
00149     d->zipOverlay = cfg.readEntry("ZipOverlay", "zip");
00150     d->shareOverlay = cfg.readEntry("ShareOverlay","share");
00151 
00152     QStringList dirs = cfg.readPathListEntry("Directories");
00153     mDirs.setAutoDelete(true);
00154     for (it=dirs.begin(); it!=dirs.end(); ++it)
00155     {
00156     cfg.setGroup(*it);
00157     for (itDir=themeDirs.begin(); itDir!=themeDirs.end(); ++itDir)
00158     {
00159         if (KStandardDirs::exists(*itDir + *it + "/"))
00160         {
00161             KIconThemeDir *dir = new KIconThemeDir(*itDir + *it, &cfg);
00162             if (!dir->isValid())
00163             {
00164                 kdWarning(264) << "Icon directory " << *itDir << " group " << *it << " not valid.\n";
00165                 delete dir;
00166             }
00167             else
00168                 mDirs.append(dir);
00169             }
00170         }
00171     }
00172 
00173     // Expand available sizes for scalable icons to their full range
00174     int i;
00175     QMap<int,QValueList<int> > scIcons;
00176     for (KIconThemeDir *dir=mDirs.first(); dir!=0L; dir=mDirs.next())
00177     {
00178         if ((dir->type() == KIcon::Scalable) && !scIcons.contains(dir->size()))
00179         {
00180             QValueList<int> lst;
00181             for (i=dir->minSize(); i<=dir->maxSize(); i++)
00182                 lst += i;
00183             scIcons[dir->size()] = lst;
00184         }
00185     }
00186 
00187     QStringList groups;
00188     groups += "Desktop";
00189     groups += "Toolbar";
00190     groups += "MainToolbar";
00191     groups += "Small";
00192     groups += "Panel";
00193     const int defDefSizes[] = { 32, 22, 22, 16, 32 };
00194     cfg.setGroup(mainSection);
00195     for (it=groups.begin(), i=0; it!=groups.end(); ++it, i++)
00196     {
00197         mDefSize[i] = cfg.readNumEntry(*it + "Default", defDefSizes[i]);
00198         QValueList<int> exp, lst = cfg.readIntListEntry(*it + "Sizes");
00199         QValueList<int>::ConstIterator it2;
00200         for (it2=lst.begin(); it2!=lst.end(); ++it2)
00201         {
00202             if (scIcons.contains(*it2))
00203                 exp += scIcons[*it2];
00204             else
00205                 exp += *it2;
00206         }
00207         mSizes[i] = exp;
00208     }
00209 
00210 }
00211 
00212 KIconTheme::~KIconTheme()
00213 {
00214     delete d;
00215 }
00216 
00217 bool KIconTheme::isValid() const
00218 {
00219     return !mDirs.isEmpty();
00220 }
00221 
00222 bool KIconTheme::isHidden() const
00223 {
00224     return d->hidden;
00225 }
00226 
00227 QString KIconTheme::example() const { return d->example; }
00228 QString KIconTheme::screenshot() const { return d->screenshot; }
00229 QString KIconTheme::linkOverlay() const { return d->linkOverlay; }
00230 QString KIconTheme::lockOverlay() const { return d->lockOverlay; }
00231 QString KIconTheme::zipOverlay() const { return d->zipOverlay; }
00232 QString KIconTheme::shareOverlay() const { return d->shareOverlay; }
00233 
00234 int KIconTheme::defaultSize(KIcon::Group group) const
00235 {
00236     if ((group < 0) || (group >= KIcon::LastGroup))
00237     {
00238         kdDebug(264) << "Illegal icon group: " << group << "\n";
00239         return -1;
00240     }
00241     return mDefSize[group];
00242 }
00243 
00244 QValueList<int> KIconTheme::querySizes(KIcon::Group group) const
00245 {
00246     QValueList<int> empty;
00247     if ((group < 0) || (group >= KIcon::LastGroup))
00248     {
00249         kdDebug(264) << "Illegal icon group: " << group << "\n";
00250         return empty;
00251     }
00252     return mSizes[group];
00253 }
00254 
00255 QStringList KIconTheme::queryIcons(int size, KIcon::Context context) const
00256 {
00257     int delta = 1000, dw;
00258 
00259     QPtrListIterator<KIconThemeDir> dirs(mDirs);
00260     KIconThemeDir *dir;
00261 
00262     // Try to find exact match
00263     QStringList result;
00264     for ( ; dirs.current(); ++dirs)
00265     {
00266         dir = dirs.current();
00267         if ((context != KIcon::Any) && (context != dir->context()))
00268             continue;
00269         if ((dir->type() == KIcon::Fixed) && (dir->size() == size))
00270         {
00271             result += dir->iconList();
00272             continue;
00273         }
00274         if ((dir->type() == KIcon::Scalable) &&
00275             (size >= dir->minSize()) && (size <= dir->maxSize()))
00276         {
00277             result += dir->iconList();
00278             continue;
00279         }
00280     if ((dir->type() == KIcon::Threshold) &&
00281             (abs(size-dir->size())<dir->threshold()))
00282             result+=dir->iconList();
00283     }
00284 
00285     return result;
00286 
00287     dirs.toFirst();
00288 
00289     // Find close match
00290     KIconThemeDir *best = 0L;
00291     for ( ; dirs.current(); ++dirs)
00292     {
00293         dir = dirs.current();
00294         if ((context != KIcon::Any) && (context != dir->context()))
00295             continue;
00296         dw = dir->size() - size;
00297         if ((dw > 6) || (abs(dw) >= abs(delta)))
00298             continue;
00299         delta = dw;
00300         best = dir;
00301     }
00302     if (best == 0L)
00303         return QStringList();
00304 
00305     return best->iconList();
00306 }
00307 
00308 QStringList KIconTheme::queryIconsByContext(int size, KIcon::Context context) const
00309 {
00310     QPtrListIterator<KIconThemeDir> dirs(mDirs);
00311     int dw;
00312     KIconThemeDir *dir;
00313 
00314     // We want all the icons for a given context, but we prefer icons
00315     // of size size . Note that this may (will) include duplicate icons
00316     //QStringList iconlist[34]; // 33 == 48-16+1
00317     QStringList iconlist[128]; // 33 == 48-16+1
00318     // Usually, only the 0, 6 (22-16), 10 (32-22), 16 (48-32 or 32-16),
00319     // 26 (48-22) and 32 (48-16) will be used, but who knows if someone
00320     // will make icon themes with different icon sizes.
00321 
00322     for ( ; dirs.current(); ++dirs)
00323     {
00324         dir = dirs.current();
00325         if ((context != KIcon::Any) && (context != dir->context()))
00326             continue;
00327         dw = abs(dir->size() - size);
00328         iconlist[(dw<127)?dw:127]+=dir->iconList();
00329     }
00330 
00331     QStringList iconlistResult;
00332     for (int i=0; i<128; i++) iconlistResult+=iconlist[i];
00333 
00334     return iconlistResult;
00335 }
00336 
00337 KIcon KIconTheme::iconPath(const QString& name, int size, KIcon::MatchType match) const
00338 {
00339     KIcon icon;
00340     int delta = 1000, dw;
00341     KIconThemeDir *dir;
00342 
00343     dw = 1000; // shut up, gcc
00344     QPtrListIterator<KIconThemeDir> dirs(mDirs);
00345     for ( ; dirs.current(); ++dirs)
00346     {
00347         dir = dirs.current();
00348         if (dir->iconPath(name).isEmpty())
00349             continue;
00350 
00351         if (match == KIcon::MatchExact)
00352         {
00353             if ((dir->type() == KIcon::Fixed) && (dir->size() != size))
00354                 continue;
00355             if ((dir->type() == KIcon::Scalable) &&
00356                 ((size < dir->minSize()) || (size > dir->maxSize())))
00357               continue;
00358             if ((dir->type() == KIcon::Threshold) &&
00359         (abs(dir->size()-size) > dir->threshold()))
00360                 continue;
00361         } else
00362         {
00363            // dw < 0 means need to scale up to get an icon of the requested size
00364            int dw_ = dw;
00365 
00366             if (dir->type() == KIcon::Fixed)
00367            {
00368              dw_ = dir->size() - size;
00369            } else if (dir->type() == KIcon::Scalable)
00370            {
00371              if (size < dir->minSize())
00372                dw_ = dir->minSize() - size;
00373              else if (size > dir->maxSize())
00374                dw_ = dir->maxSize() - size;
00375              else
00376                dw_ = 0;
00377            } else if (dir->type() == KIcon::Threshold)
00378            {
00379              if (size < dir->size() - dir->threshold())
00380                dw_ = dir->size() - dir->threshold() - size;
00381              else if (size > dir->size() + dir->threshold())
00382                dw_ = dir->size() + dir->threshold() - size;
00383              else
00384                dw_ = 0;
00385            }
00386 
00387            if (dw != 1000 && dw_ < dw && dw < 0)
00388              continue;
00389 
00390            /* Skip this if we've found a closer one, unless
00391               it's a downscale, and we only had upscales befores.
00392               This is to avoid scaling up unless we have to,
00393               since that looks very ugly */
00394             if (abs(dw_) >= abs(delta) && dw_<0 && delta>0)
00395              continue;
00396             if (abs(dw_) >= abs(dw) && dw>0)
00397              continue;
00398            dw = dw_;
00399         }
00400 
00401         icon.path = dir->iconPath(name);
00402         icon.size = dir->size();
00403         icon.type = dir->type();
00404     icon.threshold = dir->threshold();
00405         icon.context = dir->context();
00406 
00407         // if we got in MatchExact that far, we find no better
00408         if (match == KIcon::MatchExact)
00409             return icon;
00410     else
00411         {
00412         if (dw == 0) return icon; // We won't find a better match anyway
00413         delta = dw;
00414         }
00415     }
00416     return icon;
00417 }
00418 
00419 // static
00420 QString *KIconTheme::_theme = 0L;
00421 
00422 // static
00423 QStringList *KIconTheme::_theme_list = 0L;
00424 
00425 // static
00426 QString KIconTheme::current()
00427 {
00428     // Static pointer because of unloading problems wrt DSO's.
00429     if (_theme != 0L)
00430         return *_theme;
00431 
00432     _theme = new QString();
00433     KConfig *config = KGlobal::config();
00434     KConfigGroupSaver saver(config, "Icons");
00435     *_theme = config->readEntry("Theme",defaultThemeName());
00436     if ( *_theme == QString::fromLatin1("hicolor") ) *_theme = defaultThemeName();
00437 /*    if (_theme->isEmpty())
00438     {
00439         if (QPixmap::defaultDepth() > 8)
00440             *_theme = defaultThemeName();
00441         else
00442             *_theme = QString::fromLatin1("locolor");
00443     }*/
00444     return *_theme;
00445 }
00446 
00447 // static
00448 QStringList KIconTheme::list()
00449 {
00450     // Static pointer because of unloading problems wrt DSO's.
00451     if (_theme_list != 0L)
00452         return *_theme_list;
00453 
00454     _theme_list = new QStringList();
00455     QStringList icnlibs = KGlobal::dirs()->resourceDirs("icon");
00456     QStringList::ConstIterator it;
00457     for (it=icnlibs.begin(); it!=icnlibs.end(); ++it)
00458     {
00459         QDir dir(*it);
00460         if (!dir.exists())
00461             continue;
00462         QStringList lst = dir.entryList(QDir::Dirs);
00463         QStringList::ConstIterator it2;
00464         for (it2=lst.begin(); it2!=lst.end(); ++it2)
00465         {
00466             if ((*it2 == ".") || (*it2 == "..") || (*it2).startsWith("default.") )
00467                 continue;
00468             if (!KStandardDirs::exists(*it + *it2 + "/index.desktop") && !KStandardDirs::exists(*it + *it2 + "/index.theme"))
00469                 continue;
00470             if (!_theme_list->contains(*it2))
00471                 _theme_list->append(*it2);
00472         }
00473     }
00474     return *_theme_list;
00475 }
00476 
00477 // static
00478 void KIconTheme::reconfigure()
00479 {
00480     delete _theme;
00481     _theme=0L;
00482     delete _theme_list;
00483     _theme_list=0L;
00484 }
00485 
00486 // static
00487 QString KIconTheme::defaultThemeName()
00488 {
00489     return QString::fromLatin1("crystalsvg");
00490 }
00491 
00492 /*** KIconThemeDir ***/
00493 
00494 KIconThemeDir::KIconThemeDir(const QString& dir, const KConfigBase *config)
00495 {
00496     mbValid = false;
00497     mDir = dir;
00498     mSize = config->readNumEntry("Size");
00499     mMinSize = 1;    // just set the variables to something
00500     mMaxSize = 50;   // meaningful in case someone calls minSize or maxSize
00501     mType = KIcon::Fixed;
00502 
00503     if (mSize == 0)
00504         return;
00505 
00506     QString tmp = config->readEntry("Context");
00507     if (tmp == "Devices")
00508         mContext = KIcon::Device;
00509     else if (tmp == "MimeTypes")
00510         mContext = KIcon::MimeType;
00511     else if (tmp == "FileSystems")
00512         mContext = KIcon::FileSystem;
00513     else if (tmp == "Applications")
00514         mContext = KIcon::Application;
00515     else if (tmp == "Actions")
00516         mContext = KIcon::Action;
00517     else {
00518         kdDebug(264) << "Invalid Context= line for icon theme: " << mDir << "\n";
00519         return;
00520     }
00521     tmp = config->readEntry("Type");
00522     if (tmp == "Fixed")
00523         mType = KIcon::Fixed;
00524     else if (tmp == "Scalable")
00525         mType = KIcon::Scalable;
00526     else if (tmp == "Threshold")
00527         mType = KIcon::Threshold;
00528     else {
00529         kdDebug(264) << "Invalid Type= line for icon theme: " <<  mDir << "\n";
00530         return;
00531     }
00532     if (mType == KIcon::Scalable)
00533     {
00534         mMinSize = config->readNumEntry("MinSize", mSize);
00535         mMaxSize = config->readNumEntry("MaxSize", mSize);
00536     } else if (mType == KIcon::Threshold)
00537     mThreshold = config->readNumEntry("Threshold", 2);
00538     mbValid = true;
00539 }
00540 
00541 QString KIconThemeDir::iconPath(const QString& name) const
00542 {
00543     if (!mbValid)
00544         return QString::null;
00545     QString file = mDir + "/" + name;
00546 
00547     if (access(QFile::encodeName(file), R_OK) == 0)
00548         return file;
00549 
00550     return QString::null;
00551 }
00552 
00553 QStringList KIconThemeDir::iconList() const
00554 {
00555     QDir dir(mDir);
00556 #ifdef HAVE_LIBART
00557     QStringList lst = dir.entryList("*.png;*.svg;*.svgz;*.xpm", QDir::Files);
00558 #else
00559     QStringList lst = dir.entryList("*.png;*.xpm", QDir::Files);
00560 #endif
00561     QStringList result;
00562     QStringList::ConstIterator it;
00563     for (it=lst.begin(); it!=lst.end(); ++it)
00564         result += mDir + "/" + *it;
00565     return result;
00566 }
KDE Logo
This file is part of the documentation for kdecore Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Jan 21 09:57:09 2005 by doxygen 1.3.6 written by Dimitri van Heesch, © 1997-2003