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