00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "kicontheme.h"
00025 #include "k3icon_p.h"
00026
00027 #include <sys/stat.h>
00028 #include <unistd.h>
00029 #include <stdlib.h>
00030
00031 #include <QtGui/QAction>
00032 #include <QtCore/QCharRef>
00033 #include <QtCore/QMutableStringListIterator>
00034 #include <QtCore/QMap>
00035 #include <QtGui/QPixmap>
00036 #include <QtGui/QPixmapCache>
00037 #include <QtGui/QImage>
00038 #include <QtCore/QFileInfo>
00039 #include <QtCore/QDir>
00040
00041 #include <kdebug.h>
00042 #include <kicon.h>
00043 #include <kstandarddirs.h>
00044 #include <kglobal.h>
00045 #include <ksharedconfig.h>
00046 #include <kconfig.h>
00047 #include <kcomponentdata.h>
00048 #include <klocale.h>
00049
00050 #include <kconfiggroup.h>
00051
00052
00053
00054 #undef KDE_QT_SVG_RENDERER_FIXED
00055
00056 class KIconTheme::KIconThemePrivate
00057 {
00058 public:
00059 QString example, screenshot;
00060 QString linkOverlay, lockOverlay, zipOverlay, shareOverlay;
00061 bool hidden;
00062 KSharedConfig::Ptr sharedConfig;
00063
00064 int mDefSize[6];
00065 QList<int> mSizes[6];
00066
00067 int mDepth;
00068 QString mDir, mName, mInternalName, mDesc;
00069 QStringList mInherits;
00070 QList<KIconThemeDir *> mDirs;
00071 };
00072 K_GLOBAL_STATIC(QString, _theme)
00073 K_GLOBAL_STATIC(QStringList, _theme_list)
00074
00078 class KIconThemeDir
00079 {
00080 public:
00081 KIconThemeDir(const QString& basedir, const QString &themedir, const KConfigGroup &config);
00082
00083 bool isValid() const { return mbValid; }
00084 QString iconPath(const QString& name) const;
00085 QStringList iconList() const;
00086 QString dir() const { return mBaseDir + mThemeDir; }
00087
00088 KIconLoader::Context context() const { return mContext; }
00089 KIconLoader::Type type() const { return mType; }
00090 int size() const { return mSize; }
00091 int minSize() const { return mMinSize; }
00092 int maxSize() const { return mMaxSize; }
00093 int threshold() const { return mThreshold; }
00094
00095 private:
00096 bool mbValid;
00097 KIconLoader::Type mType;
00098 KIconLoader::Context mContext;
00099 int mSize, mMinSize, mMaxSize;
00100 int mThreshold;
00101
00102 QString mBaseDir;
00103 QString mThemeDir;
00104 };
00105
00106
00107
00108
00109 K3Icon::K3Icon()
00110 {
00111 size = 0;
00112 }
00113
00114 K3Icon::~K3Icon()
00115 {
00116 }
00117
00118 bool K3Icon::isValid() const
00119 {
00120 return size != 0;
00121 }
00122
00123
00124
00125
00126 KIconTheme::KIconTheme(const QString& name, const QString& appName)
00127 :d(new KIconThemePrivate)
00128 {
00129
00130 d->mInternalName = name;
00131
00132 QStringList icnlibs;
00133 QStringList::ConstIterator it, itDir;
00134 QStringList themeDirs;
00135 QString cDir;
00136
00137
00138
00139
00140
00141 if (!appName.isEmpty() &&
00142 ( name == defaultThemeName() || name== "hicolor" || name == "locolor" ) )
00143 {
00144 icnlibs = KGlobal::dirs()->resourceDirs("data");
00145 for (it=icnlibs.constBegin(); it!=icnlibs.constEnd(); ++it)
00146 {
00147 cDir = *it + appName + "/icons/" + name;
00148 if (QFile::exists( cDir ))
00149 themeDirs += cDir + '/';
00150 }
00151 }
00152
00153
00154 icnlibs = KGlobal::dirs()->resourceDirs("icon")
00155 << KGlobal::dirs()->resourceDirs("xdgdata-icon")
00156 << "/usr/share/pixmaps"
00157
00158 << KGlobal::dirs()->resourceDirs("xdgdata-pixmap");
00159 for (it=icnlibs.constBegin(); it!=icnlibs.constEnd(); ++it)
00160 {
00161 cDir = *it + name + '/';
00162 if (KStandardDirs::exists(cDir))
00163 {
00164 themeDirs += cDir;
00165 if (d->mDir.isEmpty() &&
00166 (KStandardDirs::exists( cDir + "index.desktop") || KStandardDirs::exists( cDir + "index.theme")))
00167 d->mDir = cDir;
00168 }
00169 }
00170
00171 if (d->mDir.isEmpty())
00172 {
00173 kDebug(264) << "Icon theme " << name << " not found.\n";
00174 return;
00175 }
00176
00177 QString fileName, mainSection;
00178 if(QFile::exists(d->mDir + "index.desktop")) {
00179 fileName = d->mDir + "index.desktop";
00180 mainSection="KDE Icon Theme";
00181 } else {
00182 fileName = d->mDir + "index.theme";
00183 mainSection="Icon Theme";
00184 }
00185
00186
00187 d->sharedConfig = KSharedConfig::openConfig( fileName );
00188
00189 KConfigGroup cfg(d->sharedConfig, mainSection);
00190 d->mName = cfg.readEntry("Name");
00191 d->mDesc = cfg.readEntry("Comment");
00192 d->mDepth = cfg.readEntry("DisplayDepth", 32);
00193 d->mInherits = cfg.readEntry("Inherits", QStringList());
00194 if ( name != defaultThemeName() ) {
00195 for ( QStringList::Iterator it = d->mInherits.begin(); it != d->mInherits.end(); ++it ) {
00196 if ( *it == "default" || *it == "hicolor" ) {
00197 *it = defaultThemeName();
00198 }
00199 }
00200 }
00201
00202 d->hidden = cfg.readEntry("Hidden", false);
00203 d->example = cfg.readPathEntry("Example", QString());
00204 d->screenshot = cfg.readPathEntry("ScreenShot", QString());
00205
00206 const QStringList dirs = cfg.readPathEntry("Directories", QStringList());
00207 for (it=dirs.begin(); it!=dirs.end(); ++it)
00208 {
00209 KConfigGroup cg(d->sharedConfig, *it);
00210 for (itDir=themeDirs.constBegin(); itDir!=themeDirs.constEnd(); ++itDir)
00211 {
00212 if (KStandardDirs::exists(*itDir + *it + '/'))
00213 {
00214 KIconThemeDir *dir = new KIconThemeDir(*itDir, *it, cg);
00215 if (!dir->isValid()) {
00216 delete dir;
00217 }
00218 else
00219 d->mDirs.append(dir);
00220 }
00221 }
00222 }
00223
00224
00225 int i;
00226 QMap<int,QList<int> > scIcons;
00227 foreach(KIconThemeDir *dir, d->mDirs)
00228 {
00229 if(!dir) break;
00230 if ((dir->type() == KIconLoader::Scalable) && !scIcons.contains(dir->size()))
00231 {
00232 QList<int> lst;
00233 for (i=dir->minSize(); i<=dir->maxSize(); i++)
00234 lst += i;
00235 scIcons[dir->size()] = lst;
00236 }
00237 }
00238
00239 QStringList groups;
00240 groups += "Desktop";
00241 groups += "Toolbar";
00242 groups += "MainToolbar";
00243 groups += "Small";
00244 groups += "Panel";
00245 groups += "Dialog";
00246 const int defDefSizes[] = { 32, 22, 22, 16, 32, 32 };
00247 KConfigGroup cg(d->sharedConfig, mainSection);
00248 for (it=groups.constBegin(), i=0; it!=groups.constEnd(); ++it, i++)
00249 {
00250 d->mDefSize[i] = cg.readEntry(*it + "Default", defDefSizes[i]);
00251 const QList<int> lst = cg.readEntry(*it + "Sizes", QList<int>());
00252 QList<int> exp;
00253 QList<int>::ConstIterator it2;
00254 for (it2=lst.begin(); it2!=lst.end(); ++it2)
00255 {
00256 if (scIcons.contains(*it2))
00257 exp += scIcons[*it2];
00258 else
00259 exp += *it2;
00260 }
00261 d->mSizes[i] = exp;
00262 }
00263
00264 }
00265
00266 KIconTheme::~KIconTheme()
00267 {
00268 qDeleteAll(d->mDirs);
00269 delete d;
00270 }
00271
00272 QString KIconTheme::name() const
00273 {
00274 return d->mName;
00275 }
00276
00277 QString KIconTheme::internalName() const
00278 {
00279 return d->mInternalName;
00280 }
00281
00282 QString KIconTheme::description() const
00283 {
00284 return d->mDesc;
00285 }
00286
00287 QString KIconTheme::example() const
00288 {
00289 return d->example;
00290 }
00291
00292 QString KIconTheme::screenshot() const
00293 {
00294 return d->screenshot;
00295 }
00296
00297 QString KIconTheme::dir() const
00298 {
00299 return d->mDir;
00300 }
00301
00302 QStringList KIconTheme::inherits() const
00303 {
00304 return d->mInherits;
00305 }
00306
00307 bool KIconTheme::isValid() const
00308 {
00309 return !d->mDirs.isEmpty();
00310 }
00311
00312 bool KIconTheme::isHidden() const
00313 {
00314 return d->hidden;
00315 }
00316
00317 int KIconTheme::depth() const
00318 {
00319 return d->mDepth;
00320 }
00321
00322 int KIconTheme::defaultSize(KIconLoader::Group group) const
00323 {
00324 if ((group < 0) || (group >= KIconLoader::LastGroup))
00325 {
00326 kDebug(264) << "Illegal icon group: " << group << "\n";
00327 return -1;
00328 }
00329 return d->mDefSize[group];
00330 }
00331
00332 QList<int> KIconTheme::querySizes(KIconLoader::Group group) const
00333 {
00334 QList<int> empty;
00335 if ((group < 0) || (group >= KIconLoader::LastGroup))
00336 {
00337 kDebug(264) << "Illegal icon group: " << group << "\n";
00338 return empty;
00339 }
00340 return d->mSizes[group];
00341 }
00342
00343 QStringList KIconTheme::queryIcons(int size, KIconLoader::Context context) const
00344 {
00345 int delta = 1000, dw;
00346
00347 KIconThemeDir *dir;
00348
00349
00350 QStringList result;
00351 for(int i=0; i<d->mDirs.size(); ++i)
00352 {
00353 dir = d->mDirs.at(i);
00354 if ((context != KIconLoader::Any) && (context != dir->context()))
00355 continue;
00356 if ((dir->type() == KIconLoader::Fixed) && (dir->size() == size))
00357 {
00358 result += dir->iconList();
00359 continue;
00360 }
00361 if ((dir->type() == KIconLoader::Scalable) &&
00362 (size >= dir->minSize()) && (size <= dir->maxSize()))
00363 {
00364 result += dir->iconList();
00365 continue;
00366 }
00367 if ((dir->type() == KIconLoader::Threshold) &&
00368 (abs(size-dir->size())<dir->threshold()))
00369 result+=dir->iconList();
00370 }
00371
00372 return result;
00373
00374
00375 KIconThemeDir *best = 0L;
00376 for(int i=0; i<d->mDirs.size(); ++i)
00377 {
00378 dir = d->mDirs.at(i);
00379 if ((context != KIconLoader::Any) && (context != dir->context()))
00380 continue;
00381 dw = dir->size() - size;
00382 if ((dw > 6) || (abs(dw) >= abs(delta)))
00383 continue;
00384 delta = dw;
00385 best = dir;
00386 }
00387 if (best == 0L)
00388 return QStringList();
00389
00390 return best->iconList();
00391 }
00392
00393 QStringList KIconTheme::queryIconsByContext(int size, KIconLoader::Context context) const
00394 {
00395 int dw;
00396 KIconThemeDir *dir;
00397
00398
00399
00400
00401 QStringList iconlist[128];
00402
00403
00404
00405
00406 for(int i=0;i<d->mDirs.size();++i)
00407 {
00408 dir = d->mDirs.at(i);
00409 if ((context != KIconLoader::Any) && (context != dir->context()))
00410 continue;
00411 dw = abs(dir->size() - size);
00412 iconlist[(dw<127)?dw:127]+=dir->iconList();
00413 }
00414
00415 QStringList iconlistResult;
00416 for (int i=0; i<128; i++) iconlistResult+=iconlist[i];
00417
00418 return iconlistResult;
00419 }
00420
00421 bool KIconTheme::hasContext(KIconLoader::Context context) const
00422 {
00423 foreach(KIconThemeDir *dir, d->mDirs)
00424 if ((context == KIconLoader::Any) || (context == dir->context()))
00425 return true;
00426 return false;
00427 }
00428
00429 K3Icon KIconTheme::iconPath(const QString& name, int size, KIconLoader::MatchType match) const
00430 {
00431 K3Icon icon;
00432 QString path;
00433 int delta = -1000, dw;
00434 KIconThemeDir *dir;
00435
00436 dw = 1000;
00437
00438 for(int i=0;i<d->mDirs.size();++i)
00439 {
00440 dir = d->mDirs.at(i);
00441
00442 if (match == KIconLoader::MatchExact)
00443 {
00444 if ((dir->type() == KIconLoader::Fixed) && (dir->size() != size))
00445 continue;
00446 if ((dir->type() == KIconLoader::Scalable) &&
00447 ((size < dir->minSize()) || (size > dir->maxSize())))
00448 continue;
00449 if ((dir->type() == KIconLoader::Threshold) &&
00450 (abs(dir->size()-size) > dir->threshold()))
00451 continue;
00452 } else
00453 {
00454
00455 if (dir->type() == KIconLoader::Fixed)
00456 {
00457 dw = dir->size() - size;
00458 } else if (dir->type() == KIconLoader::Scalable)
00459 {
00460 if (size < dir->minSize())
00461 dw = dir->minSize() - size;
00462 else if (size > dir->maxSize())
00463 dw = dir->maxSize() - size;
00464 else
00465 dw = 0;
00466 } else if (dir->type() == KIconLoader::Threshold)
00467 {
00468 if (size < dir->size() - dir->threshold())
00469 dw = dir->size() - dir->threshold() - size;
00470 else if (size > dir->size() + dir->threshold())
00471 dw = dir->size() + dir->threshold() - size;
00472 else
00473 dw = 0;
00474 }
00475
00476
00477
00478
00479 if ((abs(dw) >= abs(delta)) || (delta > 0 && dw < 0))
00480 continue;
00481 }
00482
00483 path = dir->iconPath(name);
00484 if (path.isEmpty())
00485 continue;
00486 icon.path = path;
00487
00488
00489
00490 #ifdef KDE_QT_SVG_RENDERER_FIXED
00491 icon.size = size;
00492 #else
00493 icon.size = dir->size();
00494 #endif
00495 icon.type = dir->type();
00496 icon.threshold = dir->threshold();
00497 icon.context = dir->context();
00498
00499
00500 if (match == KIconLoader::MatchExact)
00501 return icon;
00502 else
00503 {
00504 delta = dw;
00505 if (delta==0) return icon;
00506 }
00507 }
00508 return icon;
00509 }
00510
00511
00512 QString KIconTheme::current()
00513 {
00514
00515 if (!_theme->isEmpty())
00516 return *_theme;
00517
00518 KConfigGroup cg(KGlobal::config(), "Icons");
00519 *_theme = cg.readEntry("Theme", defaultThemeName());
00520 if ( *_theme == QLatin1String("hicolor") ) *_theme = defaultThemeName();
00521
00522
00523
00524
00525
00526
00527
00528 return *_theme;
00529 }
00530
00531
00532 QStringList KIconTheme::list()
00533 {
00534
00535 if (!_theme_list->isEmpty())
00536 return *_theme_list;
00537
00538 const QStringList icnlibs = KGlobal::dirs()->resourceDirs("icon")
00539 << KGlobal::dirs()->resourceDirs("xdgdata-icon")
00540 << "/usr/share/pixmaps"
00541
00542 << KGlobal::dirs()->resourceDirs("xdgdata-pixmap");
00543
00544 QStringList::ConstIterator it;
00545 for (it=icnlibs.begin(); it!=icnlibs.end(); ++it)
00546 {
00547 QDir dir(*it);
00548 if (!dir.exists())
00549 continue;
00550 const QStringList lst = dir.entryList(QDir::Dirs);
00551 QStringList::ConstIterator it2;
00552 for (it2=lst.begin(); it2!=lst.end(); ++it2)
00553 {
00554 if ((*it2 == ".") || (*it2 == "..") || (*it2).startsWith("default.") )
00555 continue;
00556 if (!KStandardDirs::exists(*it + *it2 + "/index.desktop") && !KStandardDirs::exists(*it + *it2 + "/index.theme"))
00557 continue;
00558 KIconTheme oink(*it2);
00559 if (!oink.isValid()) continue;
00560
00561 if (!_theme_list->contains(*it2))
00562 _theme_list->append(*it2);
00563 }
00564 }
00565 return *_theme_list;
00566 }
00567
00568
00569 void KIconTheme::reconfigure()
00570 {
00571 _theme->clear();
00572 _theme_list->clear();
00573
00574 }
00575
00576
00577 QString KIconTheme::defaultThemeName()
00578 {
00579 return QLatin1String("oxygen");
00580 }
00581
00582 void KIconTheme::assignIconsToContextMenu( ContextMenus type,
00583 QList<QAction*> actions )
00584 {
00585 switch (type) {
00586
00587 case TextEditor:
00588 enum { UndoAct, RedoAct, Separator1, CutAct, CopyAct, PasteAct, DeleteAct, ClearAct,
00589 Separator2, SelectAllAct, NCountActs };
00590
00591 if ( actions.count() < NCountActs ) {
00592 return;
00593 }
00594
00595 actions[UndoAct]->setIcon( KIcon("edit-undo") );
00596 actions[RedoAct]->setIcon( KIcon("edit-redo") );
00597 actions[CutAct]->setIcon( KIcon("edit-cut") );
00598 actions[CopyAct]->setIcon( KIcon("edit-copy") );
00599 actions[PasteAct]->setIcon( KIcon("edit-paste") );
00600 actions[ClearAct]->setIcon( KIcon("edit-clear") );
00601 actions[DeleteAct]->setIcon( KIcon("edit-delete") );
00602 actions[SelectAllAct]->setIcon( KIcon("edit-select-all") );
00603 break;
00604
00605 case ReadOnlyText:
00606 if ( actions.count() < 1 ) {
00607 return;
00608 }
00609
00610 actions[0]->setIcon( KIcon("edit-copy") );
00611 break;
00612 }
00613 }
00614
00615
00616
00617 KIconThemeDir::KIconThemeDir(const QString& basedir, const QString &themedir, const KConfigGroup &config)
00618 {
00619 mbValid = false;
00620 mBaseDir = basedir;
00621 mThemeDir = themedir;
00622 mSize = config.readEntry("Size", 0);
00623 mMinSize = 1;
00624 mMaxSize = 50;
00625 mType = KIconLoader::Fixed;
00626
00627 if (mSize == 0)
00628 return;
00629
00630 QString tmp = config.readEntry("Context");
00631 if (tmp == "Devices")
00632 mContext = KIconLoader::Device;
00633 else if (tmp == "MimeTypes")
00634 mContext = KIconLoader::MimeType;
00635 else if (tmp == "FileSystems")
00636 mContext = KIconLoader::FileSystem;
00637 else if (tmp == "Applications")
00638 mContext = KIconLoader::Application;
00639 else if (tmp == "Actions")
00640 mContext = KIconLoader::Action;
00641 else if (tmp == "Animations")
00642 mContext = KIconLoader::Animation;
00643 else if (tmp == "Categories")
00644 mContext = KIconLoader::Category;
00645 else if (tmp == "Emblems")
00646 mContext = KIconLoader::Emblem;
00647 else if (tmp == "Emotes")
00648 mContext = KIconLoader::Emote;
00649 else if (tmp == "International")
00650 mContext = KIconLoader::International;
00651 else if (tmp == "Places")
00652 mContext = KIconLoader::Place;
00653 else if (tmp == "Status")
00654 mContext = KIconLoader::StatusIcon;
00655 else if (tmp == "Stock")
00656 return;
00657 else {
00658 kDebug(264) << "Invalid Context=" << tmp << "line for icon theme: " << dir() << "\n";
00659 return;
00660 }
00661 tmp = config.readEntry("Type");
00662 if (tmp == "Fixed")
00663 mType = KIconLoader::Fixed;
00664 else if (tmp == "Scalable")
00665 mType = KIconLoader::Scalable;
00666 else if (tmp == "Threshold")
00667 mType = KIconLoader::Threshold;
00668 else {
00669 kDebug(264) << "Invalid Type=" << tmp << "line for icon theme: " << dir() << "\n";
00670 return;
00671 }
00672 if (mType == KIconLoader::Scalable)
00673 {
00674 mMinSize = config.readEntry("MinSize", mSize);
00675 mMaxSize = config.readEntry("MaxSize", mSize);
00676 } else if (mType == KIconLoader::Threshold)
00677 mThreshold = config.readEntry("Threshold", 2);
00678 mbValid = true;
00679 }
00680
00681 QString KIconThemeDir::iconPath(const QString& name) const
00682 {
00683 if (!mbValid)
00684 return QString();
00685 QString file = dir() + '/' + name;
00686
00687 if (access(QFile::encodeName(file), R_OK) == 0)
00688 return KGlobal::hasLocale() ? KGlobal::locale()->localizedFilePath(file) : file;
00689
00690 return QString();
00691 }
00692
00693 QStringList KIconThemeDir::iconList() const
00694 {
00695 const QDir icondir = dir();
00696
00697 const QStringList formats = QStringList() << "*.png" << "*.svg" << "*.svgz" << "*.xpm";
00698 const QStringList lst = icondir.entryList( formats, QDir::Files);
00699
00700 QStringList result;
00701 QStringList::ConstIterator it;
00702 for (it=lst.begin(); it!=lst.end(); ++it)
00703 result += dir() + '/' + *it;
00704 return result;
00705 }