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 "kiconloader.h"
00025 #include "kicontheme.h"
00026 #include "kiconeffect.h"
00027 #include "kiconcache.h"
00028 #include "k3icon_p.h"
00029
00030 #include <QtCore/QCharRef>
00031 #include <QtCore/QMutableStringListIterator>
00032 #include <QtGui/QPixmap>
00033 #include <QtGui/QPixmapCache>
00034 #include <QtGui/QImage>
00035 #include <QtCore/QFileInfo>
00036 #include <QtCore/QDir>
00037 #include <QtGui/QIcon>
00038 #include <QtGui/QBitmap>
00039 #include <QHash>
00040 #include <QPainter>
00041 #include <QMovie>
00042
00043 #include <kapplication.h>
00044 #include <kconfig.h>
00045 #include <kdebug.h>
00046 #include <kstandarddirs.h>
00047 #include <kglobal.h>
00048 #include <kglobalsettings.h>
00049 #include <kcomponentdata.h>
00050 #include <ksvgrenderer.h>
00051 #include <kde_file.h>
00052
00053 #include <sys/types.h>
00054 #include <stdlib.h>
00055 #include <unistd.h>
00056 #include <dirent.h>
00057 #include <assert.h>
00058 #include <kconfiggroup.h>
00059
00060
00061
00062 #undef KDE_QT_SVG_RENDERER_FIXED
00063
00064
00065
00066
00067
00068
00069 class KIconThemeNode
00070 {
00071 public:
00072
00073 KIconThemeNode(KIconTheme *_theme);
00074 ~KIconThemeNode();
00075
00076 void queryIcons(QStringList *lst, int size, KIconLoader::Context context) const;
00077 void queryIconsByContext(QStringList *lst, int size, KIconLoader::Context context) const;
00078 K3Icon findIcon(const QString& name, int size, KIconLoader::MatchType match) const;
00079 void printTree(QString& dbgString) const;
00080
00081 KIconTheme *theme;
00082 };
00083
00084 KIconThemeNode::KIconThemeNode(KIconTheme *_theme)
00085 {
00086 theme = _theme;
00087 }
00088
00089 KIconThemeNode::~KIconThemeNode()
00090 {
00091 delete theme;
00092 }
00093
00094 void KIconThemeNode::printTree(QString& dbgString) const
00095 {
00096
00097
00098 dbgString += '(';
00099 dbgString += theme->name();
00100 dbgString += ')';
00101 }
00102
00103 void KIconThemeNode::queryIcons(QStringList *result,
00104 int size, KIconLoader::Context context) const
00105 {
00106
00107 *result += theme->queryIcons(size, context);
00108 }
00109
00110 void KIconThemeNode::queryIconsByContext(QStringList *result,
00111 int size, KIconLoader::Context context) const
00112 {
00113
00114 *result += theme->queryIconsByContext(size, context);
00115 }
00116
00117 K3Icon KIconThemeNode::findIcon(const QString& name, int size,
00118 KIconLoader::MatchType match) const
00119 {
00120 return theme->iconPath(name, size, match);
00121 }
00122
00123
00124
00125
00126 struct KIconGroup
00127 {
00128 int size;
00129 bool dblPixels;
00130 bool alphaBlending;
00131 };
00132
00133
00134 static const int MAX_SVG_RENDERERS = 100;
00135
00136 class KIconLoaderPrivate
00137 {
00138 public:
00139 KIconLoaderPrivate(KIconLoader *q)
00140 : q(q)
00141 , mpGroups(0)
00142 , mIconCache(0)
00143 {
00144 }
00145
00146 ~KIconLoaderPrivate()
00147 {
00148
00149
00150 qDeleteAll(imgDict);
00151 qDeleteAll(links);
00152 qDeleteAll(svgRenderers);
00153 delete[] mpGroups;
00154 delete mIconCache;
00155 }
00156
00160 void init( const QString& _appname, KStandardDirs *_dirs );
00161
00165 bool initIconThemes();
00166
00172 K3Icon findMatchingIcon(const QString& name, int size) const;
00173
00178 void addAppThemes(const QString& appname);
00179
00185 void addBaseThemes(KIconThemeNode *node, const QString &appname);
00186
00192 void addInheritedThemes(KIconThemeNode *node, const QString &appname);
00193
00200 void addThemeByName(const QString &themename, const QString &appname);
00201
00206 QString unknownIconPath( int size ) const;
00207
00212 QString removeIconExtension(const QString &name) const;
00213
00214 KIconLoader *q;
00215
00216 QStringList mThemesInTree;
00217 KIconGroup *mpGroups;
00218 KIconThemeNode *mpThemeRoot;
00219 KStandardDirs *mpDirs;
00220 KIconEffect mpEffect;
00221 QHash<QString, QImage*> imgDict;
00222 QImage lastImage;
00223 QString lastImageKey;
00224 int lastIconType;
00225 int lastIconThreshold;
00226 QList<KIconThemeNode *> links;
00227 QHash<QString, KSvgRenderer*> svgRenderers;
00228 KIconCache* mIconCache;
00229 bool extraDesktopIconsLoaded :1;
00230
00231
00232 bool mIconThemeInited :1;
00233 bool lastWasUnknown :1;
00234 QString appname;
00235
00236 void drawOverlays(const KIconLoader *loader, KIconLoader::Group group, int state, QPixmap& pix, const QStringList& overlays);
00237 };
00238
00239 void KIconLoaderPrivate::drawOverlays(const KIconLoader *iconLoader, KIconLoader::Group group, int state, QPixmap& pix, const QStringList& overlays)
00240 {
00241 if (overlays.isEmpty()) {
00242 return;
00243 }
00244
00245 int iconSize = pix.size().width();
00246 int overlaySize;
00247
00248 if (iconSize < 32) {
00249 overlaySize = 8;
00250 } else if (iconSize <= 48) {
00251 overlaySize = 16;
00252 } else if (iconSize <= 96) {
00253 overlaySize = 22;
00254 } else if (iconSize < 256) {
00255 overlaySize = 32;
00256 } else {
00257 overlaySize = 64;
00258 }
00259
00260 QPainter painter(&pix);
00261
00262 int count = 0;
00263 foreach (const QString& overlay, overlays) {
00264
00265
00266
00267 if (overlay.isEmpty()) {
00268 ++count;
00269 continue;
00270 }
00271
00272
00273
00274
00275 QPixmap pixmap = iconLoader->loadIcon(overlay, group, overlaySize, state, QStringList(), 0, true);
00276
00277 if (pixmap.isNull()) {
00278 continue;
00279 }
00280
00281 QPoint startPoint;
00282 switch (count) {
00283 case 0:
00284
00285 startPoint = QPoint(2, iconSize - overlaySize - 2);
00286 break;
00287 case 1:
00288
00289 startPoint = QPoint(iconSize - overlaySize - 2,
00290 iconSize - overlaySize - 2);
00291 break;
00292 case 2:
00293
00294 startPoint = QPoint(iconSize - overlaySize - 2, 2);
00295 break;
00296 case 3:
00297
00298 startPoint = QPoint(2, 2);
00299 break;
00300 }
00301
00302 painter.drawPixmap(startPoint, pixmap);
00303
00304 ++count;
00305 if (count > 3) {
00306 break;
00307 }
00308 }
00309 }
00310
00311 #define KICONLOADER_CHECKS
00312 #ifdef KICONLOADER_CHECKS
00313
00314
00315 struct KIconLoaderDebug
00316 {
00317 KIconLoaderDebug( KIconLoader* l, const QString& a )
00318 : loader( l ), appname( a ), valid( true )
00319 {}
00320 KIconLoader* loader;
00321 QString appname;
00322 bool valid;
00323 QString delete_bt;
00324 };
00325
00326 static QList< KIconLoaderDebug > *kiconloaders;
00327
00328 static void registerIconLoader( KIconLoader* iconloader, const QString& _appname )
00329 {
00330 if( kiconloaders == NULL )
00331 kiconloaders = new QList< KIconLoaderDebug>();
00332
00333
00334 for( QList< KIconLoaderDebug >::Iterator it = kiconloaders->begin();
00335 it != kiconloaders->end();
00336 )
00337 {
00338 if( (*it).loader == iconloader )
00339 it = kiconloaders->erase( it );
00340 else
00341 ++it;
00342 }
00343 kiconloaders->append( KIconLoaderDebug( iconloader, _appname ));
00344 }
00345
00346 #endif
00347
00348 KIconLoader::KIconLoader(const QString& _appname, KStandardDirs *_dirs, QObject* parent)
00349 : QObject(parent)
00350 {
00351 setObjectName(_appname);
00352 d = new KIconLoaderPrivate(this);
00353
00354 connect(KGlobalSettings::self(), SIGNAL(iconChanged(int)),
00355 this, SLOT(newIconLoader()));
00356 #ifdef KICONLOADER_CHECKS
00357 registerIconLoader(this, _appname);
00358 #endif
00359 d->init( _appname, _dirs );
00360 }
00361
00362 KIconLoader::KIconLoader(const KComponentData &componentData, QObject* parent)
00363 : QObject(parent)
00364 {
00365 setObjectName(componentData.componentName());
00366 d = new KIconLoaderPrivate(this);
00367
00368 connect(KGlobalSettings::self(), SIGNAL(iconChanged(int)),
00369 this, SLOT(newIconLoader()));
00370 #ifdef KICONLOADER_CHECKS
00371 registerIconLoader(this, componentData.componentName());
00372 #endif
00373 d->init(componentData.componentName(), componentData.dirs());
00374 }
00375
00376 void KIconLoader::reconfigure( const QString& _appname, KStandardDirs *_dirs )
00377 {
00378 delete d;
00379 d = new KIconLoaderPrivate(this);
00380 d->init( _appname, _dirs );
00381 }
00382
00383 void KIconLoaderPrivate::init( const QString& _appname, KStandardDirs *_dirs )
00384 {
00385 extraDesktopIconsLoaded=false;
00386 mIconThemeInited = false;
00387 mpThemeRoot = 0;
00388
00389 if (_dirs)
00390 mpDirs = _dirs;
00391 else
00392 mpDirs = KGlobal::dirs();
00393
00394 appname = _appname;
00395 if (appname.isEmpty())
00396 appname = KGlobal::mainComponent().componentName();
00397
00398
00399 mIconCache = new KIconCache;
00400 if (!mIconCache->isValid()) {
00401 initIconThemes();
00402 QList<KIconTheme*> allThemes;
00403 foreach (KIconThemeNode* node, links) {
00404 allThemes.append(node->theme);
00405 }
00406 mIconCache->setThemeInfo(allThemes);
00407 }
00408
00409
00410 static const char * const groups[] = { "Desktop", "Toolbar", "MainToolbar", "Small", "Panel", "Dialog", 0L };
00411 KSharedConfig::Ptr config = KGlobal::config();
00412
00413
00414 mpGroups = new KIconGroup[(int) KIconLoader::LastGroup];
00415 for (KIconLoader::Group i=KIconLoader::FirstGroup; i<KIconLoader::LastGroup; i++)
00416 {
00417 if (groups[i] == 0L)
00418 break;
00419
00420 KConfigGroup cg(config, QLatin1String(groups[i]) + "Icons");
00421 mpGroups[i].size = cg.readEntry("Size", 0);
00422 mpGroups[i].dblPixels = cg.readEntry("DoublePixels", false);
00423 if (QPixmap::defaultDepth()>8)
00424 mpGroups[i].alphaBlending = cg.readEntry("AlphaBlending", true);
00425 else
00426 mpGroups[i].alphaBlending = false;
00427
00428 if (!mpGroups[i].size)
00429 mpGroups[i].size = mIconCache->defaultIconSize(i);
00430 }
00431
00432 #ifdef NO_LAZYLOAD_ICONTHEME
00433 initIconThemes();
00434 #endif
00435 }
00436
00437 bool KIconLoaderPrivate::initIconThemes()
00438 {
00439 if (mIconThemeInited) {
00440
00441 return (mpThemeRoot != 0);
00442 }
00443
00444 mIconThemeInited = true;
00445
00446
00447 KIconTheme *def = new KIconTheme(KIconTheme::current(), appname);
00448 if (!def->isValid())
00449 {
00450 delete def;
00451
00452 kDebug(264) << "Couldn't find current icon theme, falling back to default.";
00453 def = new KIconTheme(KIconTheme::defaultThemeName(), appname);
00454 if (!def->isValid())
00455 {
00456 kError(264) << "Error: standard icon theme" << KIconTheme::defaultThemeName() << "not found!" << endl;
00457 delete def;
00458 return false;
00459 }
00460 }
00461 mpThemeRoot = new KIconThemeNode(def);
00462 mThemesInTree.append(def->internalName());
00463 links.append(mpThemeRoot);
00464 addBaseThemes(mpThemeRoot, appname);
00465
00466
00467 mpDirs->addResourceType("appicon", "data", appname + "/pics/");
00468
00469 mpDirs->addResourceType("appicon", "data", appname + "/toolbar/");
00470
00471
00472 QStringList dirs;
00473 dirs += mpDirs->resourceDirs("icon");
00474 dirs += mpDirs->resourceDirs("pixmap");
00475 dirs += mpDirs->resourceDirs("xdgdata-icon");
00476 dirs += "/usr/share/pixmaps";
00477
00478 dirs += mpDirs->resourceDirs("xdgdata-pixmap");
00479 for (QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it)
00480 mpDirs->addResourceDir("appicon", *it);
00481
00482 #ifndef NDEBUG
00483 QString dbgString = "Theme tree: ";
00484 mpThemeRoot->printTree(dbgString);
00485 kDebug(264) << dbgString;
00486 #endif
00487
00488 return true;
00489 }
00490
00491 KIconLoader::~KIconLoader()
00492 {
00493 #ifdef KICONLOADER_CHECKS
00494 for( QList< KIconLoaderDebug >::Iterator it = kiconloaders->begin();
00495 it != kiconloaders->end();
00496 ++it )
00497 {
00498 if( (*it).loader == this )
00499 {
00500 (*it).valid = false;
00501 (*it).delete_bt = kBacktrace();
00502 break;
00503 }
00504 }
00505 #endif
00506 delete d;
00507 }
00508
00509 void KIconLoader::addAppDir(const QString& appname)
00510 {
00511 d->initIconThemes();
00512
00513 d->mpDirs->addResourceType("appicon", "data", appname + "/pics/");
00514
00515 d->mpDirs->addResourceType("appicon", "data", appname + "/toolbar/");
00516 d->addAppThemes(appname);
00517 }
00518
00519 void KIconLoaderPrivate::addAppThemes(const QString& appname)
00520 {
00521 initIconThemes();
00522
00523 KIconTheme *def = new KIconTheme(KIconTheme::current(), appname);
00524 if (!def->isValid()) {
00525 delete def;
00526 def = new KIconTheme(KIconTheme::defaultThemeName(), appname);
00527 }
00528 KIconThemeNode* node = new KIconThemeNode(def);
00529 bool addedToLinks = false;
00530
00531 if (!mThemesInTree.contains(node->theme->internalName())) {
00532 mThemesInTree.append(node->theme->internalName());
00533 links.append(node);
00534 addedToLinks = true;
00535 }
00536 addBaseThemes(node, appname);
00537
00538 if (!addedToLinks) {
00539
00540 delete node;
00541 }
00542 }
00543
00544 void KIconLoaderPrivate::addBaseThemes(KIconThemeNode *node, const QString &appname)
00545 {
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556 addInheritedThemes(node, appname);
00557 addThemeByName(KIconTheme::defaultThemeName(), appname);
00558 addThemeByName("hicolor", appname);
00559 }
00560
00561 void KIconLoaderPrivate::addInheritedThemes(KIconThemeNode *node, const QString &appname)
00562 {
00563 const QStringList lst = node->theme->inherits();
00564
00565 for (QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it) {
00566 if ((*it) == "hicolor") {
00567
00568
00569
00570 continue;
00571 }
00572 addThemeByName(*it, appname);
00573 }
00574 }
00575
00576 void KIconLoaderPrivate::addThemeByName(const QString &themename, const QString &appname)
00577 {
00578 if (mThemesInTree.contains(themename + appname)) {
00579 return;
00580 }
00581 KIconTheme *theme = new KIconTheme(themename, appname);
00582 if (!theme->isValid()) {
00583 delete theme;
00584 return;
00585 }
00586 KIconThemeNode *n = new KIconThemeNode(theme);
00587 mThemesInTree.append(themename + appname);
00588 links.append(n);
00589 addInheritedThemes(n, appname);
00590 }
00591
00592 void KIconLoader::addExtraDesktopThemes()
00593 {
00594 if ( d->extraDesktopIconsLoaded ) return;
00595
00596 d->initIconThemes();
00597
00598 QStringList list;
00599 const QStringList icnlibs = KGlobal::dirs()->resourceDirs("icon");
00600 QStringList::ConstIterator it;
00601 char buf[1000];
00602 int r;
00603 for (it=icnlibs.begin(); it!=icnlibs.end(); ++it)
00604 {
00605 QDir dir(*it);
00606 if (!dir.exists())
00607 continue;
00608 const QStringList lst = dir.entryList(QStringList( "default.*" ), QDir::Dirs);
00609 QStringList::ConstIterator it2;
00610 for (it2=lst.begin(); it2!=lst.end(); ++it2)
00611 {
00612 if (!KStandardDirs::exists(*it + *it2 + "/index.desktop")
00613 && !KStandardDirs::exists(*it + *it2 + "/index.theme"))
00614 continue;
00615 r=readlink( QFile::encodeName(*it + *it2) , buf, sizeof(buf)-1);
00616 if ( r>0 )
00617 {
00618 buf[r]=0;
00619 QDir dir2( buf );
00620 QString themeName=dir2.dirName();
00621
00622 if (!list.contains(themeName))
00623 list.append(themeName);
00624 }
00625 }
00626 }
00627
00628 for (it = list.begin(); it != list.end(); ++it)
00629 {
00630
00631 if (*it == QLatin1String("default.kde")
00632 || *it == QLatin1String("default.kde4")) {
00633 continue;
00634 }
00635 d->addThemeByName(*it, "");
00636 }
00637
00638 d->extraDesktopIconsLoaded=true;
00639
00640 }
00641
00642 bool KIconLoader::extraDesktopThemesAdded() const
00643 {
00644 return d->extraDesktopIconsLoaded;
00645 }
00646
00647 QString KIconLoaderPrivate::removeIconExtension(const QString &name) const
00648 {
00649 if (name.endsWith(".png") || name.endsWith(".xpm") || name.endsWith(".svg")) {
00650 return name.left(name.length() - 4);
00651 } else if (name.endsWith(".svgz")) {
00652 return name.left(name.length() - 5);
00653 }
00654
00655 return name;
00656 }
00657
00658
00659 K3Icon KIconLoaderPrivate::findMatchingIcon(const QString& name, int size) const
00660 {
00661 const_cast<KIconLoaderPrivate*>(this)->initIconThemes();
00662
00663 K3Icon icon;
00664
00665
00666
00667
00668 #ifdef KDE_QT_SVG_RENDERER_FIXED
00669 const char * ext1[4] = { ".png", ".svgz", ".svg", ".xpm" };
00670 const char * ext2[4] = { ".svgz", ".svg", ".png", ".xpm" };
00671 const char ** ext;
00672
00673 if (size == KIconLoader::SizeSmall ||
00674 size == KIconLoader::SizeSmallMedium ||
00675 size == KIconLoader::SizeMedium ||
00676 size == KIconLoader::SizeLarge ||
00677 size == KIconLoader::SizeHuge ||
00678 size == KIconLoader::SizeEnormous)
00679 {
00680 ext = ext1;
00681 }
00682 else
00683 {
00684 ext = ext2;
00685 }
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742 #else
00743 const char * const ext[4] = { ".png", ".svgz", ".svg", ".xpm" };
00744 #endif
00745
00746 foreach(KIconThemeNode *themeNode, links)
00747 {
00748 QStringList nameParts = name.split("-");
00749 QString currentName = name;
00750
00751 while (!nameParts.isEmpty())
00752 {
00753
00754
00755
00756 #ifdef KDE_QT_SVG_RENDERER_FIXED
00757 for (int i = 0 ; i < 4 ; i++)
00758 {
00759 icon = themeNode->theme->iconPath(currentName + ext[i], size, KIconLoader::MatchExact);
00760 if (icon.isValid())
00761 return icon;
00762 }
00763
00764 for (int i = 0 ; i < 4 ; i++)
00765 {
00766 icon = themeNode->theme->iconPath(currentName + ext[i], size, KIconLoader::MatchBest);
00767 if (icon.isValid())
00768 return icon;
00769 }
00770 #else
00771 for (int i = 0 ; i < 4 ; i++)
00772 {
00773 icon = themeNode->theme->iconPath(currentName + ext[i], size, KIconLoader::MatchExact);
00774 if (icon.isValid())
00775 return icon;
00776
00777 icon = themeNode->theme->iconPath(currentName + ext[i], size, KIconLoader::MatchBest);
00778 if (icon.isValid())
00779 return icon;
00780 }
00781 #endif
00782
00783 nameParts.removeLast();
00784 currentName = nameParts.join("-");
00785 }
00786 }
00787 return icon;
00788 }
00789
00790 inline QString KIconLoaderPrivate::unknownIconPath( int size ) const
00791 {
00792 static const QString &str_unknown = KGlobal::staticQString("unknown");
00793
00794 K3Icon icon = findMatchingIcon(str_unknown, size);
00795 if (!icon.isValid())
00796 {
00797 kDebug(264) << "Warning: could not find \"Unknown\" icon for size = "
00798 << size << endl;
00799 return QString();
00800 }
00801 return icon.path;
00802 }
00803
00804
00805
00806 QString KIconLoader::iconPath(const QString& _name, int group_or_size,
00807 bool canReturnNull) const
00808 {
00809 if (!d->initIconThemes()) {
00810 return QString();
00811 }
00812
00813 if (_name.isEmpty()
00814 #ifdef Q_OS_WIN
00815 || (_name.length() > 1 &&
00816 (_name[0].isLetter() && _name[1] == QLatin1Char(':') ||
00817 _name[0] == '/' && _name[1] == '/' ||
00818 _name[0] == '\\' && _name[1] == '\\')))
00819 #else
00820 || _name[0] == '/')
00821 #endif
00822 {
00823
00824 return _name;
00825 }
00826
00827 QString name = d->removeIconExtension( _name );
00828
00829 QString path;
00830 if (group_or_size == KIconLoader::User)
00831 {
00832 static const QString &png_ext = KGlobal::staticQString(".png");
00833 static const QString &xpm_ext = KGlobal::staticQString(".xpm");
00834 path = d->mpDirs->findResource("appicon", name + png_ext);
00835
00836 static const QString &svgz_ext = KGlobal::staticQString(".svgz");
00837 static const QString &svg_ext = KGlobal::staticQString(".svg");
00838 if (path.isEmpty())
00839 path = d->mpDirs->findResource("appicon", name + svgz_ext);
00840 if (path.isEmpty())
00841 path = d->mpDirs->findResource("appicon", name + svg_ext);
00842 if (path.isEmpty())
00843 path = d->mpDirs->findResource("appicon", name + xpm_ext);
00844 return path;
00845 }
00846
00847 if (group_or_size >= KIconLoader::LastGroup)
00848 {
00849 kDebug(264) << "Illegal icon group: " << group_or_size;
00850 return path;
00851 }
00852
00853 int size;
00854 if (group_or_size >= 0)
00855 size = d->mpGroups[group_or_size].size;
00856 else
00857 size = -group_or_size;
00858
00859 if (_name.isEmpty()) {
00860 if (canReturnNull)
00861 return QString();
00862 else
00863 return d->unknownIconPath(size);
00864 }
00865
00866 K3Icon icon = d->findMatchingIcon(name, size);
00867
00868 if (!icon.isValid())
00869 {
00870
00871 path = iconPath(name, KIconLoader::User, true);
00872 if (!path.isEmpty() || canReturnNull)
00873 return path;
00874
00875 return d->unknownIconPath(size);
00876 }
00877 return icon.path;
00878 }
00879
00880 QPixmap KIconLoader::loadMimeTypeIcon( const QString& iconName, KIconLoader::Group group, int size,
00881 int state, const QStringList& overlays, QString *path_store ) const
00882 {
00883 if ( !d->extraDesktopIconsLoaded )
00884 {
00885 QPixmap pixmap = loadIcon( iconName, group, size, state, overlays, path_store, true );
00886 if (!pixmap.isNull() ) return pixmap;
00887 const_cast<KIconLoader *>(this)->addExtraDesktopThemes();
00888 }
00889 return loadIcon( iconName, group, size, state, overlays, path_store, false );
00890 }
00891
00892 QPixmap KIconLoader::loadIcon(const QString& _name, KIconLoader::Group group, int size,
00893 int state, const QStringList& overlays,
00894 QString *path_store, bool canReturnNull) const
00895 {
00896 QString name = _name;
00897 QString path;
00898 QPixmap pix;
00899 bool unknownIcon = false;
00900 bool absolutePath = false;
00901 bool favIconOverlay = false;
00902
00903 if (size < 0)
00904 return pix;
00905
00906
00907 if (name.startsWith("favicons/"))
00908 {
00909 favIconOverlay = true;
00910 name = KStandardDirs::locateLocal("cache", name+".png");
00911 }
00912
00913 if (!name.isEmpty()
00914 #ifdef Q_WS_WIN
00915 && !QDir::isRelativePath(name))
00916 #else
00917 && name[0] == '/')
00918 #endif
00919 {
00920 absolutePath = true;
00921 }
00922
00923 static const QString &str_unknown = KGlobal::staticQString("unknown");
00924
00925
00926 if (group == KIconLoader::User)
00927 {
00928 QString key;
00929 key.reserve(200);
00930 key.append("$kicou_");
00931 key.append(name).append('_').append(QString::number(size));
00932 key.append(overlays.join("_"));
00933
00934 if (d->mIconCache->find(key, pix, path_store)) {
00935
00936 return pix;
00937 } else if (!d->initIconThemes()) {
00938 return pix;
00939 }
00940
00941 path = (absolutePath) ? name :
00942 iconPath(name, KIconLoader::User, canReturnNull);
00943 if (path.isEmpty())
00944 {
00945 if (!canReturnNull) {
00946 #ifndef NDEBUG
00947 kWarning(264) << "No such icon" << _name;
00948 #endif
00949 unknownIcon = true;
00950 } else {
00951 return pix;
00952 }
00953
00954 path = iconPath(str_unknown, KIconLoader::Small, true);
00955 if (path.isEmpty())
00956 {
00957 kWarning(264) << "Warning: Cannot find \"unknown\" icon.";
00958 return pix;
00959 }
00960 }
00961
00962 if (path_store != 0L)
00963 *path_store = path;
00964
00965
00966 QImage img(path);
00967 if (size != 0)
00968 img=img.scaled(size,size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
00969
00970 pix = QPixmap::fromImage(img);
00971 d->drawOverlays(this, KIconLoader::Desktop, state, pix, overlays);
00972 if (!unknownIcon)
00973 d->mIconCache->insert(key, pix, path);
00974 return pix;
00975 }
00976
00977
00978
00979 if ((group < -1) || (group >= KIconLoader::LastGroup))
00980 {
00981 kDebug(264) << "Illegal icon group: " << group;
00982 group = KIconLoader::Desktop;
00983 }
00984
00985 if ((state < 0) || (state >= KIconLoader::LastState))
00986 {
00987 kDebug(264) << "Illegal icon state: " << state;
00988 state = KIconLoader::DefaultState;
00989 }
00990
00991 if (size == 0 && group < 0)
00992 {
00993 kDebug(264) << "Neither size nor group specified!";
00994 group = KIconLoader::Desktop;
00995 }
00996
00997 if (!absolutePath)
00998 {
00999 if (!canReturnNull && name.isEmpty())
01000 name = str_unknown;
01001 else
01002 name = d->removeIconExtension(name);
01003 }
01004
01005
01006 if (size == 0)
01007 {
01008 size = d->mpGroups[group].size;
01009 }
01010 favIconOverlay = favIconOverlay && size > 22;
01011
01012
01013
01014 QString key;
01015 key.reserve(100);
01016 key.append("$kico_");
01017 key.append(name).append('_').append(QString::number(size));
01018
01019 QString overlayKey = overlays.join("_");
01020 QString noEffectKey = key + overlayKey;
01021
01022 if (group >= 0)
01023 {
01024 key.append(d->mpEffect.fingerprint(group, state));
01025 if (d->mpGroups[group].dblPixels)
01026 key.append(QLatin1String(":dblsize"));
01027 } else {
01028 key.append(QLatin1String("noeffect"));
01029 }
01030 key.append(overlayKey);
01031
01032
01033 if (d->mIconCache->find(key, pix, path_store)) {
01034
01035 if (!pix.isNull() || canReturnNull) {
01036 return pix;
01037 } else {
01038 return loadIcon(str_unknown, group, size, state,
01039 overlays, path_store, canReturnNull);
01040 }
01041 }
01042 if (!d->initIconThemes()) {
01043 return pix;
01044 }
01045
01046 QImage *img = 0;
01047 int iconType;
01048 int iconThreshold;
01049
01050 if ( ( path_store != 0 ) ||
01051 ( noEffectKey != d->lastImageKey ) ||
01052 ( d->lastWasUnknown && canReturnNull ) )
01053 {
01054
01055 K3Icon icon;
01056 if (absolutePath && !favIconOverlay)
01057 {
01058 icon.context=KIconLoader::Any;
01059 icon.type=KIconLoader::Scalable;
01060 icon.path=name;
01061 }
01062 else
01063 {
01064 if (!name.isEmpty())
01065 icon = d->findMatchingIcon(favIconOverlay ? QString("text-html") : name, size);
01066
01067 if (!icon.isValid())
01068 {
01069
01070 if (!name.isEmpty()) {
01071 pix = loadIcon(name, KIconLoader::User, size, state, overlays, path_store, true);
01072 }
01073 if (!pix.isNull()) {
01074 d->mIconCache->insert(key, pix, path);
01075 return pix;
01076 }
01077 if (canReturnNull) {
01078 return pix;
01079 }
01080
01081 #ifndef NDEBUG
01082 kWarning(264) << "No such icon" << _name;
01083 #endif
01084 unknownIcon = true;
01085 icon = d->findMatchingIcon(str_unknown, size);
01086 if (!icon.isValid())
01087 {
01088 kDebug(264)
01089 << "Warning: could not find \"Unknown\" icon for size = "
01090 << size << endl;
01091 return pix;
01092 }
01093 }
01094 }
01095
01096 if (path_store != 0)
01097 *path_store = icon.path;
01098
01099
01100 QString ext = icon.path.right(3).toUpper();
01101 if(ext != "SVG" && ext != "VGZ")
01102 {
01103 img = new QImage(icon.path, ext.toLatin1());
01104 if (img->isNull()) {
01105 delete img;
01106 return pix;
01107 }
01108 }
01109 else
01110 {
01111 KSvgRenderer *renderer = d->svgRenderers[icon.path];
01112 if (!renderer) {
01113 renderer = new KSvgRenderer(icon.path);
01114 if (renderer->isValid()) {
01115 if (d->svgRenderers.count() >= MAX_SVG_RENDERERS) {
01116 QList<QString> keys = d->svgRenderers.keys();
01117 for (int i = 0; i < MAX_SVG_RENDERERS/2; ++i) {
01118 KSvgRenderer *oldRenderer = d->svgRenderers.take(keys[i]);
01119 delete oldRenderer;
01120 }
01121 }
01122 d->svgRenderers.insert(icon.path, renderer);
01123 }
01124 }
01125
01126
01127 if (renderer && renderer->isValid()) {
01128 img = new QImage(size, size, QImage::Format_ARGB32_Premultiplied);
01129 img->fill(0);
01130 QPainter p(img);
01131 renderer->render(&p);
01132 } else {
01133 delete renderer;
01134 return pix;
01135 }
01136 }
01137
01138 iconType = icon.type;
01139 iconThreshold = icon.threshold;
01140 path = icon.path;
01141
01142 d->lastImage = img->copy();
01143 d->lastImageKey = noEffectKey;
01144 d->lastIconType = iconType;
01145 d->lastIconThreshold = iconThreshold;
01146 d->lastWasUnknown = unknownIcon;
01147 }
01148 else
01149 {
01150 img = new QImage( d->lastImage.copy() );
01151 iconType = d->lastIconType;
01152 iconThreshold = d->lastIconThreshold;
01153 unknownIcon = d->lastWasUnknown;
01154 }
01155
01156
01157 #ifndef KDE_QT_SVG_RENDERER_FIXED
01158
01159
01160
01161 if (iconType == KIconLoader::Scalable && size != img->width())
01162 {
01163 *img = img->scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
01164 }
01165 #endif
01166 if (iconType == KIconLoader::Threshold && size != img->width())
01167 {
01168 if ( abs(size-img->width())>iconThreshold )
01169 *img = img->scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
01170 }
01171 if (group >= 0 && d->mpGroups[group].dblPixels)
01172 {
01173 *img = d->mpEffect.doublePixels(*img);
01174 }
01175 if (group >= 0)
01176 {
01177 *img = d->mpEffect.apply(*img, group, state);
01178 }
01179
01180 bool addToCache = true;
01181 if (favIconOverlay)
01182 {
01183 QImage favIcon(name, "PNG");
01184 if (favIcon.isNull())
01185 {
01186
01187
01188 addToCache = false;
01189 }
01190 else
01191 {
01192
01193
01194
01195 int x = img->width() - favIcon.width() - 1,
01196 y = img->height() - favIcon.height() - 1;
01197 favIcon = favIcon.convertToFormat(QImage::Format_ARGB32);
01198 *img = img->convertToFormat(QImage::Format_ARGB32);
01199 for( int line = 0;
01200 line < favIcon.height();
01201 ++line )
01202 {
01203 QRgb* fpos = reinterpret_cast< QRgb* >( favIcon.scanLine( line ));
01204 QRgb* ipos = reinterpret_cast< QRgb* >( img->scanLine( line + y )) + x;
01205 for( int i = 0;
01206 i < favIcon.width();
01207 ++i, ++fpos, ++ipos )
01208 *ipos = qRgba( ( qRed( *ipos ) * ( 255 - qAlpha( *fpos )) + qRed( *fpos ) * qAlpha( *fpos )) / 255,
01209 ( qGreen( *ipos ) * ( 255 - qAlpha( *fpos )) + qGreen( *fpos ) * qAlpha( *fpos )) / 255,
01210 ( qBlue( *ipos ) * ( 255 - qAlpha( *fpos )) + qBlue( *fpos ) * qAlpha( *fpos )) / 255,
01211 ( qAlpha( *ipos ) * ( 255 - qAlpha( *fpos )) + qAlpha( *fpos ) * qAlpha( *fpos )) / 255 );
01212 }
01213 }
01214 }
01215
01216 pix = QPixmap::fromImage(*img);
01217
01218 d->drawOverlays(this, group, state, pix, overlays);
01219
01220 delete img;
01221
01222 if (unknownIcon && addToCache)
01223 {
01224 d->mIconCache->insert(key, QPixmap(), QString());
01225 }
01226 else if (addToCache)
01227 {
01228 d->mIconCache->insert(key, pix, path);
01229 }
01230 return pix;
01231 }
01232
01233 QMovie *KIconLoader::loadMovie(const QString& name, KIconLoader::Group group, int size, QObject *parent) const
01234 {
01235 QString file = moviePath( name, group, size );
01236 if (file.isEmpty())
01237 return 0;
01238 int dirLen = file.lastIndexOf('/');
01239 QString icon = iconPath(name, size ? -size : group, true);
01240 if (!icon.isEmpty() && file.left(dirLen) != icon.left(dirLen))
01241 return 0;
01242 QMovie *movie = new QMovie(file, QByteArray(), parent);
01243 if (!movie->isValid())
01244 {
01245 delete movie;
01246 return 0;
01247 }
01248 return movie;
01249 }
01250
01251 QString KIconLoader::moviePath(const QString& name, KIconLoader::Group group, int size) const
01252 {
01253 if (!d->mpGroups) return QString();
01254
01255 d->initIconThemes();
01256
01257 if ( (group < -1 || group >= KIconLoader::LastGroup) && group != KIconLoader::User )
01258 {
01259 kDebug(264) << "Illegal icon group: " << group;
01260 group = KIconLoader::Desktop;
01261 }
01262 if (size == 0 && group < 0)
01263 {
01264 kDebug(264) << "Neither size nor group specified!";
01265 group = KIconLoader::Desktop;
01266 }
01267
01268 QString file = name + ".mng";
01269 if (group == KIconLoader::User)
01270 {
01271 file = d->mpDirs->findResource("appicon", file);
01272 }
01273 else
01274 {
01275 if (size == 0)
01276 size = d->mpGroups[group].size;
01277
01278 K3Icon icon;
01279
01280 foreach(KIconThemeNode *themeNode, d->links)
01281 {
01282 icon = themeNode->theme->iconPath(file, size, KIconLoader::MatchExact);
01283 if (icon.isValid())
01284 break;
01285 }
01286
01287 if ( !icon.isValid() )
01288 {
01289 foreach(KIconThemeNode *themeNode, d->links)
01290 {
01291 icon = themeNode->theme->iconPath(file, size, KIconLoader::MatchBest);
01292 if (icon.isValid())
01293 break;
01294 }
01295 }
01296
01297 file = icon.isValid() ? icon.path : QString();
01298 }
01299 return file;
01300 }
01301
01302
01303 QStringList KIconLoader::loadAnimated(const QString& name, KIconLoader::Group group, int size) const
01304 {
01305 QStringList lst;
01306
01307 if (!d->mpGroups) return lst;
01308
01309 d->initIconThemes();
01310
01311 if ((group < -1) || (group >= KIconLoader::LastGroup))
01312 {
01313 kDebug(264) << "Illegal icon group: " << group;
01314 group = KIconLoader::Desktop;
01315 }
01316 if ((size == 0) && (group < 0))
01317 {
01318 kDebug(264) << "Neither size nor group specified!";
01319 group = KIconLoader::Desktop;
01320 }
01321
01322 QString file = name + "/0001";
01323 if (group == KIconLoader::User)
01324 {
01325 file = d->mpDirs->findResource("appicon", file + ".png");
01326 } else
01327 {
01328 if (size == 0)
01329 size = d->mpGroups[group].size;
01330 K3Icon icon = d->findMatchingIcon(file, size);
01331 file = icon.isValid() ? icon.path : QString();
01332
01333 }
01334 if (file.isEmpty())
01335 return lst;
01336
01337 QString path = file.left(file.length()-8);
01338 DIR* dp = opendir( QFile::encodeName(path) );
01339 if(!dp)
01340 return lst;
01341
01342 KDE_struct_dirent* ep;
01343 while( ( ep = KDE_readdir( dp ) ) != 0L )
01344 {
01345 QString fn(QFile::decodeName(ep->d_name));
01346 if(!(fn.left(4)).toUInt())
01347 continue;
01348
01349 lst += path + fn;
01350 }
01351 closedir ( dp );
01352 lst.sort();
01353 return lst;
01354 }
01355
01356 KIconTheme *KIconLoader::theme() const
01357 {
01358 d->initIconThemes();
01359 if (d->mpThemeRoot) return d->mpThemeRoot->theme;
01360 return 0L;
01361 }
01362
01363 int KIconLoader::currentSize(KIconLoader::Group group) const
01364 {
01365 if (!d->mpGroups) return -1;
01366
01367 if (group < 0 || group >= KIconLoader::LastGroup)
01368 {
01369 kDebug(264) << "Illegal icon group: " << group;
01370 return -1;
01371 }
01372 return d->mpGroups[group].size;
01373 }
01374
01375 QStringList KIconLoader::queryIconsByDir( const QString& iconsDir ) const
01376 {
01377 QDir dir(iconsDir);
01378 QStringList formats;
01379 formats << "*.png" << "*.xpm" << "*.svg" << "*.svgz";
01380 const QStringList lst = dir.entryList(formats, QDir::Files);
01381 QStringList result;
01382 QStringList::ConstIterator it;
01383 for (it=lst.begin(); it!=lst.end(); ++it)
01384 result += iconsDir + '/' + *it;
01385 return result;
01386 }
01387
01388 QStringList KIconLoader::queryIconsByContext(int group_or_size,
01389 KIconLoader::Context context) const
01390 {
01391 d->initIconThemes();
01392
01393 QStringList result;
01394 if (group_or_size >= KIconLoader::LastGroup)
01395 {
01396 kDebug(264) << "Illegal icon group: " << group_or_size;
01397 return result;
01398 }
01399 int size;
01400 if (group_or_size >= 0)
01401 size = d->mpGroups[group_or_size].size;
01402 else
01403 size = -group_or_size;
01404
01405 foreach(KIconThemeNode *themeNode, d->links)
01406 themeNode->queryIconsByContext(&result, size, context);
01407
01408
01409 QString name;
01410 QStringList res2, entries;
01411 QStringList::ConstIterator it;
01412 for (it=result.begin(); it!=result.end(); ++it)
01413 {
01414 int n = (*it).lastIndexOf('/');
01415 if (n == -1)
01416 name = *it;
01417 else
01418 name = (*it).mid(n+1);
01419 name = d->removeIconExtension(name);
01420 if (!entries.contains(name))
01421 {
01422 entries += name;
01423 res2 += *it;
01424 }
01425 }
01426 return res2;
01427
01428 }
01429
01430 QStringList KIconLoader::queryIcons(int group_or_size, KIconLoader::Context context) const
01431 {
01432 d->initIconThemes();
01433
01434 QStringList result;
01435 if (group_or_size >= KIconLoader::LastGroup)
01436 {
01437 kDebug(264) << "Illegal icon group: " << group_or_size;
01438 return result;
01439 }
01440 int size;
01441 if (group_or_size >= 0)
01442 size = d->mpGroups[group_or_size].size;
01443 else
01444 size = -group_or_size;
01445
01446 foreach(KIconThemeNode *themeNode, d->links)
01447 themeNode->queryIcons(&result, size, context);
01448
01449
01450 QString name;
01451 QStringList res2, entries;
01452 QStringList::ConstIterator it;
01453 for (it=result.begin(); it!=result.end(); ++it)
01454 {
01455 int n = (*it).lastIndexOf('/');
01456 if (n == -1)
01457 name = *it;
01458 else
01459 name = (*it).mid(n+1);
01460 name = d->removeIconExtension(name);
01461 if (!entries.contains(name))
01462 {
01463 entries += name;
01464 res2 += *it;
01465 }
01466 }
01467 return res2;
01468 }
01469
01470
01471 bool KIconLoader::hasContext(KIconLoader::Context context) const
01472 {
01473 foreach(KIconThemeNode *themeNode, d->links)
01474 if( themeNode->theme->hasContext( context ))
01475 return true;
01476 return false;
01477 }
01478
01479 KIconEffect * KIconLoader::iconEffect() const
01480 {
01481 return &d->mpEffect;
01482 }
01483
01484 bool KIconLoader::alphaBlending(KIconLoader::Group group) const
01485 {
01486 if (!d->mpGroups) return false;
01487
01488 if (group < 0 || group >= KIconLoader::LastGroup)
01489 {
01490 kDebug(264) << "Illegal icon group: " << group;
01491 return false;
01492 }
01493 return d->mpGroups[group].alphaBlending;
01494 }
01495
01496
01497 QIcon KIconLoader::loadIconSet( const QString& name, KIconLoader::Group g, int s,
01498 bool canReturnNull )
01499 {
01500 QIcon iconset;
01501 QPixmap tmp = loadIcon(name, g, s, KIconLoader::ActiveState, QStringList(), NULL, canReturnNull);
01502 iconset.addPixmap( tmp, QIcon::Active, QIcon::On );
01503
01504 tmp = loadIcon(name, g, s, KIconLoader::DisabledState, QStringList(), NULL, canReturnNull);
01505 iconset.addPixmap( tmp, QIcon::Disabled, QIcon::On );
01506 tmp = loadIcon(name, g, s, KIconLoader::DefaultState, QStringList(), NULL, canReturnNull);
01507 iconset.addPixmap( tmp, QIcon::Normal, QIcon::On );
01508 return iconset;
01509 }
01510
01511
01512
01513 QPixmap DesktopIcon(const QString& name, int force_size, int state, const QStringList &overlays)
01514 {
01515 KIconLoader *loader = KIconLoader::global();
01516 return loader->loadIcon(name, KIconLoader::Desktop, force_size, state, overlays);
01517 }
01518
01519
01520 QIcon DesktopIconSet(const QString& name, int force_size)
01521 {
01522 KIconLoader *loader = KIconLoader::global();
01523 return loader->loadIconSet(name, KIconLoader::Desktop, force_size);
01524 }
01525
01526 QPixmap BarIcon(const QString& name, int force_size, int state, const QStringList &overlays)
01527 {
01528 KIconLoader *loader = KIconLoader::global();
01529 return loader->loadIcon(name, KIconLoader::Toolbar, force_size, state, overlays);
01530 }
01531
01532
01533 QIcon BarIconSet(const QString& name, int force_size)
01534 {
01535 KIconLoader *loader = KIconLoader::global();
01536 return loader->loadIconSet( name, KIconLoader::Toolbar, force_size );
01537 }
01538
01539 QPixmap SmallIcon(const QString& name, int force_size, int state, const QStringList &overlays)
01540 {
01541 KIconLoader *loader = KIconLoader::global();
01542 return loader->loadIcon(name, KIconLoader::Small, force_size, state, overlays);
01543 }
01544
01545
01546 QIcon SmallIconSet(const QString& name, int force_size)
01547 {
01548 KIconLoader *loader = KIconLoader::global();
01549 return loader->loadIconSet( name, KIconLoader::Small, force_size );
01550 }
01551
01552 QPixmap MainBarIcon(const QString& name, int force_size, int state, const QStringList &overlays)
01553 {
01554 KIconLoader *loader = KIconLoader::global();
01555 return loader->loadIcon(name, KIconLoader::MainToolbar, force_size, state, overlays);
01556 }
01557
01558
01559 QIcon MainBarIconSet(const QString& name, int force_size)
01560 {
01561 KIconLoader *loader = KIconLoader::global();
01562 return loader->loadIconSet( name, KIconLoader::MainToolbar, force_size );
01563 }
01564
01565 QPixmap UserIcon(const QString& name, int state, const QStringList &overlays)
01566 {
01567 KIconLoader *loader = KIconLoader::global();
01568 return loader->loadIcon(name, KIconLoader::User, 0, state, overlays);
01569 }
01570
01571
01572 QIcon UserIconSet(const QString& name)
01573 {
01574 KIconLoader *loader = KIconLoader::global();
01575 return loader->loadIconSet( name, KIconLoader::User );
01576 }
01577
01578 int IconSize(KIconLoader::Group group)
01579 {
01580 KIconLoader *loader = KIconLoader::global();
01581 return loader->currentSize(group);
01582 }
01583
01584 QPixmap KIconLoader::unknown()
01585 {
01586 QPixmap pix;
01587 if ( QPixmapCache::find("unknown", pix) )
01588 return pix;
01589
01590 QString path = global()->iconPath("unknown", KIconLoader::Small, true);
01591 if (path.isEmpty())
01592 {
01593 kDebug(264) << "Warning: Cannot find \"unknown\" icon.";
01594 pix = QPixmap(32,32);
01595 } else
01596 {
01597 pix.load(path);
01598 QPixmapCache::insert("unknown", pix);
01599 }
01600
01601 return pix;
01602 }
01603
01604
01605 K_GLOBAL_STATIC_WITH_ARGS(KIconLoader, globalIconLoader, (KGlobal::mainComponent(), 0))
01606
01607 KIconLoader *KIconLoader::global()
01608 {
01609 return globalIconLoader;
01610 }
01611
01612 void KIconLoader::newIconLoader()
01613 {
01614 if ( global() == this) {
01615 KIconTheme::reconfigure();
01616 }
01617
01618 reconfigure( objectName(), d->mpDirs );
01619 }
01620
01621 #include "kiconloader.moc"
01622