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