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