kdecore Library API Documentation

kicontheme.cpp

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