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