kdecore Library: kicontheme.cpp Source File (kdecore Library)

| # kdecore Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# kicontheme.cpp

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

This file is part of the documentation for kdecore Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:18:05 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003