KDEUI
kiconloader.cpp
Go to the documentation of this file.
00001 /* vi: ts=8 sts=4 sw=4 00002 * 00003 * kiconloader.cpp: An icon loader for KDE with theming functionality. 00004 * 00005 * This file is part of the KDE project, module kdeui. 00006 * Copyright (C) 2000 Geert Jansen <jansen@kde.org> 00007 * Antonio Larrosa <larrosa@kde.org> 00008 * 2010 Michael Pyne <mpyne@kde.org> 00009 * 00010 * This library is free software; you can redistribute it and/or 00011 * modify it under the terms of the GNU Library General Public 00012 * License version 2 as published by the Free Software Foundation. 00013 * 00014 * This library is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 * Library General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU Library General Public License 00020 * along with this library; see the file COPYING.LIB. If not, write to 00021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00022 * Boston, MA 02110-1301, USA. 00023 */ 00024 00025 #include "kiconloader.h" 00026 #include "kicontheme.h" 00027 #include "kiconeffect.h" 00028 #include "kiconcache.h" 00029 #include "k3icon_p.h" 00030 00031 #include <QtCore/QCache> 00032 #include <QtGui/QPixmap> 00033 #include <QtGui/QPixmapCache> 00034 #include <QtGui/QImage> 00035 #include <QtCore/QFileInfo> 00036 #include <QtCore/QDir> 00037 #include <QtCore/QBuffer> 00038 #include <QtCore/QDataStream> 00039 #include <QtCore/QByteArray> 00040 #include <QtCore/QStringBuilder> // % operator for QString 00041 #include <QtGui/QIcon> 00042 #include <QtGui/QPainter> 00043 #include <QMovie> 00044 00045 #include <kapplication.h> 00046 #include <kconfig.h> 00047 #include <kdebug.h> 00048 #include <kstandarddirs.h> 00049 #include <kglobal.h> 00050 #include <kglobalsettings.h> 00051 #include <kcomponentdata.h> 00052 #ifndef _WIN32_WCE 00053 #include <ksvgrenderer.h> 00054 #endif 00055 #include <kde_file.h> 00056 #include <kshareddatacache.h> 00057 00058 #include <sys/types.h> 00059 #include <stdlib.h> //for abs 00060 #include <unistd.h> //for readlink 00061 #include <dirent.h> 00062 #include <assert.h> 00063 #include <kconfiggroup.h> 00064 00065 // Used to make cache keys for icons with no group. Result type is QString* 00066 K_GLOBAL_STATIC_WITH_ARGS(QString, NULL_EFFECT_FINGERPRINT, (QString::fromLatin1("noeffect"))) 00067 00068 // Qt implements Tiny SVG specification. This specification does not cover important elements 00069 // that are pretty globally used on our icons, like blurring (and other filters). TT seems to have 00070 // no interest in supporting the full SVG specification (it would be slower, even with JS, CSS 00071 // support...). So, we have no chance for now. Let's disable svg rendering unconditionally. 00072 // (ereslibre) 00073 #undef KDE_QT_SVG_RENDERER_FIXED 00074 00075 //#define NO_LAZYLOAD_ICONTHEME 00076 00080 static bool pathIsRelative(const QString &path) 00081 { 00082 #ifdef Q_OS_UNIX 00083 return (!path.isEmpty() && path[0] != QChar('/')); 00084 #else 00085 return QDir::isRelativePath(path); 00086 #endif 00087 } 00088 00092 struct PixmapWithPath 00093 { 00094 QPixmap pixmap; 00095 QString path; 00096 }; 00097 00098 /*** KIconThemeNode: A node in the icon theme dependancy tree. ***/ 00099 00100 class KIconThemeNode 00101 { 00102 public: 00103 00104 KIconThemeNode(KIconTheme *_theme); 00105 ~KIconThemeNode(); 00106 00107 void queryIcons(QStringList *lst, int size, KIconLoader::Context context) const; 00108 void queryIconsByContext(QStringList *lst, int size, KIconLoader::Context context) const; 00109 K3Icon findIcon(const QString& name, int size, KIconLoader::MatchType match) const; 00110 void printTree(QString& dbgString) const; 00111 00112 KIconTheme *theme; 00113 }; 00114 00115 KIconThemeNode::KIconThemeNode(KIconTheme *_theme) 00116 { 00117 theme = _theme; 00118 } 00119 00120 KIconThemeNode::~KIconThemeNode() 00121 { 00122 delete theme; 00123 } 00124 00125 void KIconThemeNode::printTree(QString& dbgString) const 00126 { 00127 /* This method doesn't have much sense anymore, so maybe it should 00128 be removed in the (near?) future */ 00129 dbgString += '('; 00130 dbgString += theme->name(); 00131 dbgString += ')'; 00132 } 00133 00134 void KIconThemeNode::queryIcons(QStringList *result, 00135 int size, KIconLoader::Context context) const 00136 { 00137 // add the icons of this theme to it 00138 *result += theme->queryIcons(size, context); 00139 } 00140 00141 void KIconThemeNode::queryIconsByContext(QStringList *result, 00142 int size, KIconLoader::Context context) const 00143 { 00144 // add the icons of this theme to it 00145 *result += theme->queryIconsByContext(size, context); 00146 } 00147 00148 K3Icon KIconThemeNode::findIcon(const QString& name, int size, 00149 KIconLoader::MatchType match) const 00150 { 00151 return theme->iconPath(name, size, match); 00152 } 00153 00154 00155 /*** KIconGroup: Icon type description. ***/ 00156 00157 struct KIconGroup 00158 { 00159 int size; 00160 bool alphaBlending; 00161 }; 00162 00163 00164 /*** d pointer for KIconLoader. ***/ 00165 class KIconLoaderPrivate 00166 { 00167 public: 00168 KIconLoaderPrivate(KIconLoader *q) 00169 : q(q) 00170 , mpGroups(0) 00171 , mIconThemeCache(0) 00172 , mIconCache(0) 00173 { 00174 } 00175 00176 ~KIconLoaderPrivate() 00177 { 00178 /* antlarr: There's no need to delete d->mpThemeRoot as it's already 00179 deleted when the elements of d->links are deleted */ 00180 qDeleteAll(links); 00181 delete[] mpGroups; 00182 delete mIconThemeCache; 00183 delete mIconCache; 00184 } 00185 00189 void init( const QString& _appname, KStandardDirs *_dirs ); 00190 00194 bool initIconThemes(); 00195 00201 K3Icon findMatchingIcon(const QString& name, int size) const; 00202 00209 K3Icon findMatchingIconWithGenericFallbacks(const QString& name, int size) const; 00210 00215 void addAppThemes(const QString& appname); 00216 00222 void addBaseThemes(KIconThemeNode *node, const QString &appname); 00223 00229 void addInheritedThemes(KIconThemeNode *node, const QString &appname); 00230 00237 void addThemeByName(const QString &themename, const QString &appname); 00238 00243 QString unknownIconPath( int size ) const; 00244 00249 QString removeIconExtension(const QString &name) const; 00250 00257 void normalizeIconMetadata(KIconLoader::Group &group, int &size, int &state) const; 00258 00264 QString makeCacheKey(const QString &name, KIconLoader::Group group, const QStringList &overlays, 00265 int size, int state) const; 00266 00273 QImage createIconImage(const QString &path, int size = 0); 00274 00279 void insertCachedPixmapWithPath(const QString &key, const QPixmap &data, const QString &path); 00280 00286 bool findCachedPixmapWithPath(const QString &key, QPixmap &data, QString &path); 00287 00288 KIconLoader *const q; 00289 00290 QStringList mThemesInTree; 00291 KIconGroup *mpGroups; 00292 KIconThemeNode *mpThemeRoot; 00293 KStandardDirs *mpDirs; 00294 KIconEffect mpEffect; 00295 QList<KIconThemeNode *> links; 00296 KIconCache* mIconThemeCache; 00297 00298 // This shares the icons across all processes 00299 KSharedDataCache* mIconCache; 00300 00301 // This caches rendered QPixmaps in just this process. 00302 QCache<QString, PixmapWithPath> mPixmapCache; 00303 00304 bool extraDesktopIconsLoaded :1; 00305 // lazy loading: initIconThemes() is only needed when the "links" list is needed 00306 // mIconThemeInited is used inside initIconThemes() to init only once 00307 bool mIconThemeInited :1; 00308 QString appname; 00309 00310 void drawOverlays(const KIconLoader *loader, KIconLoader::Group group, int state, QPixmap& pix, const QStringList& overlays); 00311 }; 00312 00313 class KIconLoaderGlobalData 00314 { 00315 public: 00316 KIconLoaderGlobalData() { 00317 const QStringList genericIconsFiles = KGlobal::dirs()->findAllResources("xdgdata-mime", "generic-icons"); 00318 //kDebug() << genericIconsFiles; 00319 Q_FOREACH(const QString& file, genericIconsFiles) { 00320 parseGenericIconsFiles(file); 00321 } 00322 } 00323 00324 QString genericIconFor(const QString& icon) const { 00325 return m_genericIcons.value(icon); 00326 } 00327 00328 private: 00329 void parseGenericIconsFiles(const QString& fileName); 00330 QHash<QString, QString> m_genericIcons; 00331 }; 00332 00333 void KIconLoaderGlobalData::parseGenericIconsFiles(const QString& fileName) 00334 { 00335 QFile file(fileName); 00336 if (file.open(QIODevice::ReadOnly)) { 00337 QTextStream stream(&file); 00338 stream.setCodec("ISO 8859-1"); 00339 while (!stream.atEnd()) { 00340 const QString line = stream.readLine(); 00341 if (line.isEmpty() || line[0] == '#') 00342 continue; 00343 const int pos = line.indexOf(':'); 00344 if (pos == -1) // syntax error 00345 continue; 00346 QString mimeIcon = line.left(pos); 00347 const int slashindex = mimeIcon.indexOf(QLatin1Char('/')); 00348 if (slashindex != -1) { 00349 mimeIcon[slashindex] = QLatin1Char('-'); 00350 } 00351 00352 const QString genericIcon = line.mid(pos+1); 00353 m_genericIcons.insert(mimeIcon, genericIcon); 00354 //kDebug(264) << mimeIcon << "->" << genericIcon; 00355 } 00356 } 00357 } 00358 00359 K_GLOBAL_STATIC(KIconLoaderGlobalData, s_globalData) 00360 00361 void KIconLoaderPrivate::drawOverlays(const KIconLoader *iconLoader, KIconLoader::Group group, int state, QPixmap& pix, const QStringList& overlays) 00362 { 00363 if (overlays.isEmpty()) { 00364 return; 00365 } 00366 00367 const int iconSize = pix.size().width(); 00368 int overlaySize; 00369 00370 if (iconSize < 32) { 00371 overlaySize = 8; 00372 } else if (iconSize <= 48) { 00373 overlaySize = 16; 00374 } else if (iconSize <= 96) { 00375 overlaySize = 22; 00376 } else if (iconSize < 256) { 00377 overlaySize = 32; 00378 } else { 00379 overlaySize = 64; 00380 } 00381 00382 QPainter painter(&pix); 00383 00384 int count = 0; 00385 foreach (const QString& overlay, overlays) { 00386 // Ensure empty strings fill up a emblem spot 00387 // Needed when you have several emblems to ensure they're always painted 00388 // at the same place, even if one is not here 00389 if (overlay.isEmpty()) { 00390 ++count; 00391 continue; 00392 } 00393 00394 //TODO: should we pass in the kstate? it results in a slower 00395 // path, and perhaps emblems should remain in the default state 00396 // anyways? 00397 const QPixmap pixmap = iconLoader->loadIcon(overlay, group, overlaySize, state, QStringList(), 0, true); 00398 00399 if (pixmap.isNull()) { 00400 continue; 00401 } 00402 00403 QPoint startPoint; 00404 switch (count) { 00405 case 0: 00406 // bottom left corner 00407 startPoint = QPoint(2, iconSize - overlaySize - 2); 00408 break; 00409 case 1: 00410 // bottom right corner 00411 startPoint = QPoint(iconSize - overlaySize - 2, 00412 iconSize - overlaySize - 2); 00413 break; 00414 case 2: 00415 // top right corner 00416 startPoint = QPoint(iconSize - overlaySize - 2, 2); 00417 break; 00418 case 3: 00419 // top left corner 00420 startPoint = QPoint(2, 2); 00421 break; 00422 } 00423 00424 painter.drawPixmap(startPoint, pixmap); 00425 00426 ++count; 00427 if (count > 3) { 00428 break; 00429 } 00430 } 00431 } 00432 00433 KIconLoader::KIconLoader(const QString& _appname, KStandardDirs *_dirs, QObject* parent) 00434 : QObject(parent) 00435 { 00436 setObjectName(_appname); 00437 d = new KIconLoaderPrivate(this); 00438 00439 connect(KGlobalSettings::self(), SIGNAL(iconChanged(int)), 00440 this, SLOT(newIconLoader())); 00441 d->init( _appname, _dirs ); 00442 } 00443 00444 KIconLoader::KIconLoader(const KComponentData &componentData, QObject* parent) 00445 : QObject(parent) 00446 { 00447 setObjectName(componentData.componentName()); 00448 d = new KIconLoaderPrivate(this); 00449 00450 connect(KGlobalSettings::self(), SIGNAL(iconChanged(int)), 00451 this, SLOT(newIconLoader())); 00452 d->init(componentData.componentName(), componentData.dirs()); 00453 } 00454 00455 void KIconLoader::reconfigure( const QString& _appname, KStandardDirs *_dirs ) 00456 { 00457 delete d; 00458 d = new KIconLoaderPrivate(this); 00459 d->init( _appname, _dirs ); 00460 } 00461 00462 void KIconLoaderPrivate::init( const QString& _appname, KStandardDirs *_dirs ) 00463 { 00464 extraDesktopIconsLoaded=false; 00465 mIconThemeInited = false; 00466 mpThemeRoot = 0; 00467 00468 if (_dirs) 00469 mpDirs = _dirs; 00470 else 00471 mpDirs = KGlobal::dirs(); 00472 00473 appname = _appname; 00474 if (appname.isEmpty()) 00475 appname = KGlobal::mainComponent().componentName(); 00476 00477 // Initialize icon cache 00478 mIconCache = new KSharedDataCache("icon-cache", 10 * 1024 * 1024); 00479 // Cost here is number of pixels, not size. So this is actually a bit 00480 // smaller. 00481 mPixmapCache.setMaxCost(10 * 1024 * 1024); 00482 00483 // Initialize icon theme cache 00484 mIconThemeCache = new KIconCache; 00485 if (!mIconThemeCache->isValid()) { 00486 initIconThemes(); 00487 QList<KIconTheme*> allThemes; 00488 foreach (KIconThemeNode* node, links) { 00489 allThemes.append(node->theme); 00490 } 00491 mIconThemeCache->setThemeInfo(allThemes); 00492 } 00493 00494 // These have to match the order in kicontheme.h 00495 static const char * const groups[] = { "Desktop", "Toolbar", "MainToolbar", "Small", "Panel", "Dialog", 0L }; 00496 KSharedConfig::Ptr config = KGlobal::config(); 00497 00498 // loading config and default sizes 00499 mpGroups = new KIconGroup[(int) KIconLoader::LastGroup]; 00500 for (KIconLoader::Group i=KIconLoader::FirstGroup; i<KIconLoader::LastGroup; ++i) 00501 { 00502 if (groups[i] == 0L) 00503 break; 00504 00505 KConfigGroup cg(config, QLatin1String(groups[i]) + "Icons"); 00506 mpGroups[i].size = cg.readEntry("Size", 0); 00507 if (QPixmap::defaultDepth()>8) 00508 mpGroups[i].alphaBlending = cg.readEntry("AlphaBlending", true); 00509 else 00510 mpGroups[i].alphaBlending = false; 00511 00512 if (!mpGroups[i].size) 00513 mpGroups[i].size = mIconThemeCache->defaultIconSize(i); 00514 } 00515 00516 #ifdef NO_LAZYLOAD_ICONTHEME 00517 initIconThemes(); 00518 #endif 00519 } 00520 00521 bool KIconLoaderPrivate::initIconThemes() 00522 { 00523 if (mIconThemeInited) { 00524 // If mpThemeRoot isn't 0 then initing has succeeded 00525 return (mpThemeRoot != 0); 00526 } 00527 //kDebug(264); 00528 mIconThemeInited = true; 00529 00530 // Add the default theme and its base themes to the theme tree 00531 KIconTheme *def = new KIconTheme(KIconTheme::current(), appname); 00532 if (!def->isValid()) 00533 { 00534 delete def; 00535 // warn, as this is actually a small penalty hit 00536 kDebug(264) << "Couldn't find current icon theme, falling back to default."; 00537 def = new KIconTheme(KIconTheme::defaultThemeName(), appname); 00538 if (!def->isValid()) 00539 { 00540 kError(264) << "Error: standard icon theme" << KIconTheme::defaultThemeName() << "not found!" << endl; 00541 delete def; 00542 return false; 00543 } 00544 } 00545 mpThemeRoot = new KIconThemeNode(def); 00546 mThemesInTree.append(def->internalName()); 00547 links.append(mpThemeRoot); 00548 addBaseThemes(mpThemeRoot, appname); 00549 00550 // Insert application specific themes at the top. 00551 mpDirs->addResourceType("appicon", "data", appname + "/pics/"); 00552 // ################## KDE5: consider removing the toolbar directory 00553 mpDirs->addResourceType("appicon", "data", appname + "/toolbar/"); 00554 00555 // Add legacy icon dirs. 00556 QStringList dirs; 00557 dirs += mpDirs->resourceDirs("icon"); 00558 dirs += mpDirs->resourceDirs("pixmap"); 00559 dirs += mpDirs->resourceDirs("xdgdata-icon"); 00560 dirs += "/usr/share/pixmaps"; 00561 // These are not in the icon spec, but e.g. GNOME puts some icons there anyway. 00562 dirs += mpDirs->resourceDirs("xdgdata-pixmap"); 00563 for (QStringList::ConstIterator it = dirs.constBegin(); it != dirs.constEnd(); ++it) 00564 mpDirs->addResourceDir("appicon", *it); 00565 00566 #ifndef NDEBUG 00567 QString dbgString = "Theme tree: "; 00568 mpThemeRoot->printTree(dbgString); 00569 kDebug(264) << dbgString; 00570 #endif 00571 00572 return true; 00573 } 00574 00575 KIconLoader::~KIconLoader() 00576 { 00577 delete d; 00578 } 00579 00580 void KIconLoader::addAppDir(const QString& appname) 00581 { 00582 d->initIconThemes(); 00583 00584 d->mpDirs->addResourceType("appicon", "data", appname + "/pics/"); 00585 // ################## KDE5: consider removing the toolbar directory 00586 d->mpDirs->addResourceType("appicon", "data", appname + "/toolbar/"); 00587 d->addAppThemes(appname); 00588 } 00589 00590 void KIconLoaderPrivate::addAppThemes(const QString& appname) 00591 { 00592 initIconThemes(); 00593 00594 KIconTheme *def = new KIconTheme(KIconTheme::current(), appname); 00595 if (!def->isValid()) { 00596 delete def; 00597 def = new KIconTheme(KIconTheme::defaultThemeName(), appname); 00598 } 00599 KIconThemeNode* node = new KIconThemeNode(def); 00600 bool addedToLinks = false; 00601 00602 if (!mThemesInTree.contains(node->theme->internalName())) { 00603 mThemesInTree.append(node->theme->internalName()); 00604 links.append(node); 00605 addedToLinks = true; 00606 } 00607 addBaseThemes(node, appname); 00608 00609 if (!addedToLinks) { 00610 // Nodes in links are being deleted later - this one needs manual care. 00611 delete node; 00612 } 00613 } 00614 00615 void KIconLoaderPrivate::addBaseThemes(KIconThemeNode *node, const QString &appname) 00616 { 00617 // Quote from the icon theme specification: 00618 // The lookup is done first in the current theme, and then recursively 00619 // in each of the current theme's parents, and finally in the 00620 // default theme called "hicolor" (implementations may add more 00621 // default themes before "hicolor", but "hicolor" must be last). 00622 // 00623 // So we first make sure that all inherited themes are added, then we 00624 // add the KDE default theme as fallback for all icons that might not be 00625 // present in an inherited theme, and hicolor goes last. 00626 00627 addInheritedThemes(node, appname); 00628 addThemeByName(KIconTheme::defaultThemeName(), appname); 00629 addThemeByName("hicolor", appname); 00630 } 00631 00632 void KIconLoaderPrivate::addInheritedThemes(KIconThemeNode *node, const QString &appname) 00633 { 00634 const QStringList lst = node->theme->inherits(); 00635 00636 for (QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it) { 00637 if ((*it) == "hicolor") { 00638 // The icon theme spec says that "hicolor" must be the very last 00639 // of all inherited themes, so don't add it here but at the very end 00640 // of addBaseThemes(). 00641 continue; 00642 } 00643 addThemeByName(*it, appname); 00644 } 00645 } 00646 00647 void KIconLoaderPrivate::addThemeByName(const QString &themename, const QString &appname) 00648 { 00649 if (mThemesInTree.contains(themename + appname)) { 00650 return; 00651 } 00652 KIconTheme *theme = new KIconTheme(themename, appname); 00653 if (!theme->isValid()) { 00654 delete theme; 00655 return; 00656 } 00657 KIconThemeNode *n = new KIconThemeNode(theme); 00658 mThemesInTree.append(themename + appname); 00659 links.append(n); 00660 addInheritedThemes(n, appname); 00661 } 00662 00663 void KIconLoader::addExtraDesktopThemes() 00664 { 00665 if ( d->extraDesktopIconsLoaded ) return; 00666 00667 d->initIconThemes(); 00668 00669 QStringList list; 00670 const QStringList icnlibs = KGlobal::dirs()->resourceDirs("icon"); 00671 QStringList::ConstIterator it; 00672 char buf[1000]; 00673 int r; 00674 for (it=icnlibs.begin(); it!=icnlibs.end(); ++it) 00675 { 00676 QDir dir(*it); 00677 if (!dir.exists()) 00678 continue; 00679 const QStringList lst = dir.entryList(QStringList( "default.*" ), QDir::Dirs); 00680 QStringList::ConstIterator it2; 00681 for (it2=lst.begin(); it2!=lst.end(); ++it2) 00682 { 00683 if (!KStandardDirs::exists(*it + *it2 + "/index.desktop") 00684 && !KStandardDirs::exists(*it + *it2 + "/index.theme")) 00685 continue; 00686 r=readlink( QFile::encodeName(*it + *it2) , buf, sizeof(buf)-1); 00687 if ( r>0 ) 00688 { 00689 buf[r]=0; 00690 const QDir dir2( buf ); 00691 QString themeName=dir2.dirName(); 00692 00693 if (!list.contains(themeName)) 00694 list.append(themeName); 00695 } 00696 } 00697 } 00698 00699 for (it = list.constBegin(); it != list.constEnd(); ++it) 00700 { 00701 // Don't add the KDE defaults once more, we have them anyways. 00702 if (*it == QLatin1String("default.kde") 00703 || *it == QLatin1String("default.kde4")) { 00704 continue; 00705 } 00706 d->addThemeByName(*it, ""); 00707 } 00708 00709 d->extraDesktopIconsLoaded=true; 00710 00711 } 00712 00713 bool KIconLoader::extraDesktopThemesAdded() const 00714 { 00715 return d->extraDesktopIconsLoaded; 00716 } 00717 00718 QString KIconLoaderPrivate::removeIconExtension(const QString &name) const 00719 { 00720 if (name.endsWith(QLatin1String(".png")) 00721 || name.endsWith(QLatin1String(".xpm")) 00722 || name.endsWith(QLatin1String(".svg"))) { 00723 return name.left(name.length() - 4); 00724 } else if (name.endsWith(QLatin1String(".svgz"))) { 00725 return name.left(name.length() - 5); 00726 } 00727 00728 return name; 00729 } 00730 00731 void KIconLoaderPrivate::normalizeIconMetadata(KIconLoader::Group &group, int &size, int &state) const 00732 { 00733 if ((state < 0) || (state >= KIconLoader::LastState)) 00734 { 00735 kWarning(264) << "Illegal icon state: " << state; 00736 state = KIconLoader::DefaultState; 00737 } 00738 00739 if (size < 0) { 00740 size = 0; 00741 } 00742 00743 // For "User" icons, bail early since the size should be based on the size on disk, 00744 // which we've already checked. 00745 if (group == KIconLoader::User) { 00746 return; 00747 } 00748 00749 if ((group < -1) || (group >= KIconLoader::LastGroup)) 00750 { 00751 kWarning(264) << "Illegal icon group: " << group; 00752 group = KIconLoader::Desktop; 00753 } 00754 00755 // If size == 0, use default size for the specified group. 00756 if (size == 0) 00757 { 00758 if (group < 0) 00759 { 00760 kWarning(264) << "Neither size nor group specified!"; 00761 group = KIconLoader::Desktop; 00762 } 00763 size = mpGroups[group].size; 00764 } 00765 } 00766 00767 QString KIconLoaderPrivate::makeCacheKey(const QString &name, KIconLoader::Group group, 00768 const QStringList &overlays, int size, int state) const 00769 { 00770 // The KSharedDataCache is shared so add some namespacing. The following code 00771 // uses QStringBuilder (new in Qt 4.6) 00772 00773 return (group == KIconLoader::User 00774 ? QLatin1Literal("$kicou_") 00775 : QLatin1Literal("$kico_")) 00776 % name 00777 % QLatin1Char('_') 00778 % QString::number(size) 00779 % QLatin1Char('_') 00780 % overlays.join("_") 00781 % ( group >= 0 ? mpEffect.fingerprint(group, state) 00782 : *NULL_EFFECT_FINGERPRINT); 00783 } 00784 00785 QImage KIconLoaderPrivate::createIconImage(const QString &path, int size) 00786 { 00787 // Use the extension as the format. Works for XPM and PNG, but not for SVG. The 00788 // "VGZ" is the last 3 characters of "SVGZ" 00789 QString ext = path.right(3).toUpper(); 00790 QImage img; 00791 00792 if (ext != "SVG" && ext != "VGZ") 00793 { 00794 // Not a SVG or SVGZ 00795 img = QImage(path, ext.toLatin1()); 00796 00797 if (size != 0 && !img.isNull()) { 00798 img = img.scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 00799 } 00800 } 00801 else 00802 { 00803 #ifndef _WIN32_WCE 00804 KSvgRenderer renderer(path, q); 00805 00806 if (renderer.isValid()) { 00807 img = QImage(size, size, QImage::Format_ARGB32_Premultiplied); 00808 img.fill(0); 00809 QPainter p(&img); 00810 renderer.render(&p); 00811 } 00812 #endif 00813 } 00814 00815 return img; 00816 } 00817 00818 void KIconLoaderPrivate::insertCachedPixmapWithPath( 00819 const QString &key, 00820 const QPixmap &data, 00821 const QString &path = QString()) 00822 { 00823 // Even if the pixmap is null, we add it to the caches so that we record 00824 // the fact that whatever icon led to us getting a null pixmap doesn't 00825 // exist. 00826 00827 QBuffer output; 00828 output.open(QIODevice::WriteOnly); 00829 00830 QDataStream outputStream(&output); 00831 outputStream.setVersion(QDataStream::Qt_4_6); 00832 00833 outputStream << path; 00834 00835 // Convert the QPixmap to PNG. This is actually done by Qt's own operator. 00836 outputStream << data; 00837 00838 output.close(); 00839 00840 // The byte array contained in the QBuffer is what we want in the cache. 00841 mIconCache->insert(key, output.buffer()); 00842 00843 // Also insert the object into our process-local cache for even more 00844 // speed. 00845 PixmapWithPath *pixmapPath = new PixmapWithPath; 00846 pixmapPath->pixmap = data; 00847 pixmapPath->path = path; 00848 00849 mPixmapCache.insert(key, pixmapPath, data.width() * data.height() + 1); 00850 } 00851 00852 bool KIconLoaderPrivate::findCachedPixmapWithPath(const QString &key, QPixmap &data, QString &path) 00853 { 00854 // If the pixmap is present in our local process cache, use that since we 00855 // don't need to decompress and upload it to the X server/graphics card. 00856 const PixmapWithPath *pixmapPath = mPixmapCache.object(key); 00857 if (pixmapPath) { 00858 path = pixmapPath->path; 00859 data = pixmapPath->pixmap; 00860 00861 return true; 00862 } 00863 00864 // Otherwise try to find it in our shared memory cache since that will 00865 // be quicker than the disk, especially for SVGs. 00866 QByteArray result; 00867 00868 if (!mIconCache->find(key, &result) || result.isEmpty()) { 00869 return false; 00870 } 00871 00872 QBuffer buffer; 00873 buffer.setBuffer(&result); 00874 buffer.open(QIODevice::ReadOnly); 00875 00876 QDataStream inputStream(&buffer); 00877 inputStream.setVersion(QDataStream::Qt_4_6); 00878 00879 QString tempPath; 00880 inputStream >> tempPath; 00881 00882 if (inputStream.status() == QDataStream::Ok) { 00883 QPixmap tempPixmap; 00884 inputStream >> tempPixmap; 00885 00886 if (inputStream.status() == QDataStream::Ok) { 00887 data = tempPixmap; 00888 path = tempPath; 00889 00890 // Since we're here we didn't have a QPixmap cache entry, add one now. 00891 PixmapWithPath *newPixmapWithPath = new PixmapWithPath; 00892 newPixmapWithPath->pixmap = data; 00893 newPixmapWithPath->path = path; 00894 00895 mPixmapCache.insert(key, newPixmapWithPath, data.width() * data.height() + 1); 00896 00897 return true; 00898 } 00899 } 00900 00901 return false; 00902 } 00903 00904 K3Icon KIconLoaderPrivate::findMatchingIconWithGenericFallbacks(const QString& name, int size) const 00905 { 00906 K3Icon icon = findMatchingIcon(name, size); 00907 if (icon.isValid()) 00908 return icon; 00909 00910 const QString genericIcon = s_globalData->genericIconFor(name); 00911 if (!genericIcon.isEmpty()) { 00912 icon = findMatchingIcon(genericIcon, size); 00913 } 00914 return icon; 00915 } 00916 00917 K3Icon KIconLoaderPrivate::findMatchingIcon(const QString& name, int size) const 00918 { 00919 const_cast<KIconLoaderPrivate*>(this)->initIconThemes(); 00920 00921 K3Icon icon; 00922 00923 // The following code has been commented out because the Qt SVG renderer needs 00924 // to be improved. If you are going to change/remove some code from this part, 00925 // please contact me before (ereslibre@kde.org), or kde-core-devel@kde.org. (ereslibre) 00926 #ifdef KDE_QT_SVG_RENDERER_FIXED 00927 const char * ext1[4] = { ".png", ".svgz", ".svg", ".xpm" }; 00928 const char * ext2[4] = { ".svgz", ".svg", ".png", ".xpm" }; 00929 const char ** ext; 00930 00931 if (size == KIconLoader::SizeSmall || 00932 size == KIconLoader::SizeSmallMedium || 00933 size == KIconLoader::SizeMedium || 00934 size == KIconLoader::SizeLarge || 00935 size == KIconLoader::SizeHuge || 00936 size == KIconLoader::SizeEnormous) 00937 { 00938 ext = ext1; // size is standard, give preference to PNG over SVG when searching 00939 } 00940 else 00941 { 00942 ext = ext2; // size is non-standard, give preference to SVG over PNG when searching 00943 } 00944 00945 /* If size parameter is a standard one, that means: 00946 00947 - KIconLoader::SizeSmall 00948 - KIconLoader::SizeSmallMedium 00949 - KIconLoader::SizeMedium 00950 - KIconLoader::SizeLarge 00951 - KIconLoader::SizeHuge 00952 - KIconLoader::SizeEnormous 00953 00954 To follow the XDG icon theme and icon naming specifications, 00955 the order in which we look for an icon is: 00956 00957 png, svgz, svg, xpm exact match 00958 png, svgz, svg, xpm best match 00959 less specific fallback in this theme: png, svgz, svg, xpm exact match 00960 png, svgz, svg, xpm best match 00961 even less specific fallback in this theme: [same order] 00962 (...) 00963 00964 next theme in inheritance tree: png, svgz, svg, xpm exact match 00965 png, svgz, svg, xpm best match 00966 less specific fallbacks in this next theme 00967 (...) 00968 00969 next theme in inheritance tree: png, svgz, svg, xpm exact match 00970 png, svgz, svg, xpm best match 00971 less specific fallbacks in this next theme 00972 (...) 00973 00974 and so on. 00975 00976 If size parameter is a non-standard one, then we give more preference to 00977 SVG format since drawing SVG's gives better quality and despite being 00978 slower than resizing a PNG image, the cases where non-standard sizes are 00979 asked are very rare. For non-standard sizes what we have is: 00980 00981 svgz, svg, png, xpm exact match 00982 svgz, svg, png, xpm best match 00983 less specific fallback in this theme: svgz, svg, png, xpm exact match 00984 svgz, svg, png, xpm best match 00985 even less specific fallback in this theme: [same order] 00986 (...) 00987 00988 next theme in inheritance tree: svgz, svg, png, xpm exact match 00989 svgz, svg, png, xpm best match 00990 less specific fallbacks in this next theme 00991 (...) 00992 00993 next theme in inheritance tree: svgz, svg, png, xpm exact match 00994 svgz, svg, png, xpm best match 00995 less specific fallbacks in this next theme 00996 (...) 00997 00998 and so on. 00999 */ 01000 #else 01001 const char * const ext[4] = { ".png", ".svgz", ".svg", ".xpm" }; 01002 #endif 01003 01004 bool genericFallback = name.endsWith(QLatin1String("-x-generic")); 01005 01006 foreach(KIconThemeNode *themeNode, links) 01007 { 01008 QString currentName = name; 01009 01010 while (!currentName.isEmpty()) 01011 { 01012 01013 //kDebug(264) << "Looking up" << currentName; 01014 01015 // The following code has been commented out because the Qt SVG renderer needs 01016 // to be improved. If you are going to change/remove some code from this part, 01017 // please contact me before (ereslibre@kde.org), or kde-core-devel@kde.org. (ereslibre) 01018 #ifdef KDE_QT_SVG_RENDERER_FIXED 01019 for (int i = 0 ; i < 4 ; i++) 01020 { 01021 icon = themeNode->theme->iconPath(currentName + ext[i], size, KIconLoader::MatchExact); 01022 if (icon.isValid()) 01023 return icon; 01024 } 01025 01026 for (int i = 0 ; i < 4 ; i++) 01027 { 01028 icon = themeNode->theme->iconPath(currentName + ext[i], size, KIconLoader::MatchBest); 01029 if (icon.isValid()) 01030 return icon; 01031 } 01032 #else 01033 for (int i = 0 ; i < 4 ; i++) 01034 { 01035 icon = themeNode->theme->iconPath(currentName + ext[i], size, KIconLoader::MatchExact); 01036 if (icon.isValid()) 01037 return icon; 01038 01039 icon = themeNode->theme->iconPath(currentName + ext[i], size, KIconLoader::MatchBest); 01040 if (icon.isValid()) 01041 return icon; 01042 } 01043 #endif 01044 if (genericFallback) 01045 // we already tested the base name 01046 break; 01047 01048 int rindex = currentName.lastIndexOf('-'); 01049 if (rindex > 1) { // > 1 so that we don't split x-content or x-epoc 01050 currentName.truncate(rindex); 01051 01052 if (currentName.endsWith(QLatin1String("-x"))) 01053 currentName.chop(2); 01054 } else { 01055 // From update-mime-database.c 01056 static const QSet<QString> mediaTypes = QSet<QString>() 01057 << "text" << "application" << "image" << "audio" 01058 << "inode" << "video" << "message" << "model" << "multipart" 01059 << "x-content" << "x-epoc"; 01060 // Shared-mime-info spec says: 01061 // "If [generic-icon] is not specified then the mimetype is used to generate the 01062 // generic icon by using the top-level media type (e.g. "video" in "video/ogg") 01063 // and appending "-x-generic" (i.e. "video-x-generic" in the previous example)." 01064 if (mediaTypes.contains(currentName)) { 01065 currentName += QLatin1String("-x-generic"); 01066 genericFallback = true; 01067 } else { 01068 break; 01069 } 01070 } 01071 } 01072 } 01073 return icon; 01074 } 01075 01076 inline QString KIconLoaderPrivate::unknownIconPath( int size ) const 01077 { 01078 static const QString &str_unknown = KGlobal::staticQString("unknown"); 01079 01080 K3Icon icon = findMatchingIcon(str_unknown, size); 01081 if (!icon.isValid()) 01082 { 01083 kDebug(264) << "Warning: could not find \"Unknown\" icon for size = " 01084 << size << endl; 01085 return QString(); 01086 } 01087 return icon.path; 01088 } 01089 01090 // Finds the absolute path to an icon. 01091 01092 QString KIconLoader::iconPath(const QString& _name, int group_or_size, 01093 bool canReturnNull) const 01094 { 01095 if (!d->initIconThemes()) { 01096 return QString(); 01097 } 01098 01099 if (_name.isEmpty() || !pathIsRelative(_name)) 01100 { 01101 // we have either an absolute path or nothing to work with 01102 return _name; 01103 } 01104 01105 QString name = d->removeIconExtension( _name ); 01106 01107 QString path; 01108 if (group_or_size == KIconLoader::User) 01109 { 01110 static const QString &png_ext = KGlobal::staticQString(".png"); 01111 static const QString &xpm_ext = KGlobal::staticQString(".xpm"); 01112 path = d->mpDirs->findResource("appicon", name + png_ext); 01113 01114 static const QString &svgz_ext = KGlobal::staticQString(".svgz"); 01115 static const QString &svg_ext = KGlobal::staticQString(".svg"); 01116 if (path.isEmpty()) 01117 path = d->mpDirs->findResource("appicon", name + svgz_ext); 01118 if (path.isEmpty()) 01119 path = d->mpDirs->findResource("appicon", name + svg_ext); 01120 if (path.isEmpty()) 01121 path = d->mpDirs->findResource("appicon", name + xpm_ext); 01122 return path; 01123 } 01124 01125 if (group_or_size >= KIconLoader::LastGroup) 01126 { 01127 kDebug(264) << "Illegal icon group: " << group_or_size; 01128 return path; 01129 } 01130 01131 int size; 01132 if (group_or_size >= 0) 01133 size = d->mpGroups[group_or_size].size; 01134 else 01135 size = -group_or_size; 01136 01137 if (_name.isEmpty()) { 01138 if (canReturnNull) 01139 return QString(); 01140 else 01141 return d->unknownIconPath(size); 01142 } 01143 01144 K3Icon icon = d->findMatchingIconWithGenericFallbacks(name, size); 01145 01146 if (!icon.isValid()) 01147 { 01148 // Try "User" group too. 01149 path = iconPath(name, KIconLoader::User, true); 01150 if (!path.isEmpty() || canReturnNull) 01151 return path; 01152 01153 return d->unknownIconPath(size); 01154 } 01155 return icon.path; 01156 } 01157 01158 QPixmap KIconLoader::loadMimeTypeIcon( const QString& _iconName, KIconLoader::Group group, int size, 01159 int state, const QStringList& overlays, QString *path_store ) const 01160 { 01161 QString iconName = _iconName; 01162 const int slashindex = iconName.indexOf(QLatin1Char('/')); 01163 if (slashindex != -1) { 01164 iconName[slashindex] = QLatin1Char('-'); 01165 } 01166 01167 if ( !d->extraDesktopIconsLoaded ) 01168 { 01169 const QPixmap pixmap = loadIcon( iconName, group, size, state, overlays, path_store, true ); 01170 if (!pixmap.isNull() ) { 01171 return pixmap; 01172 } 01173 const_cast<KIconLoader *>(this)->addExtraDesktopThemes(); 01174 } 01175 const QPixmap pixmap = loadIcon(iconName, group, size, state, overlays, path_store, true); 01176 if (pixmap.isNull()) { 01177 // Icon not found, fallback to application/octet-stream 01178 return loadIcon("application-octet-stream", group, size, state, overlays, path_store, false); 01179 } 01180 return pixmap; 01181 } 01182 01183 QPixmap KIconLoader::loadIcon(const QString& _name, KIconLoader::Group group, int size, 01184 int state, const QStringList& overlays, 01185 QString *path_store, bool canReturnNull) const 01186 { 01187 QString name = _name; 01188 bool unknownIcon = false; 01189 bool favIconOverlay = false; 01190 01191 if (size < 0 || _name.isEmpty()) 01192 return QPixmap(); 01193 01194 /* 01195 * This method works in a kind of pipeline, with the following steps: 01196 * 1. Sanity checks. 01197 * 2. Convert _name, group, size, etc. to a key name. 01198 * 3. Check if the key is already cached. 01199 * 4. If not, initialize the theme and find/load the icon. 01200 * 4a Apply overlays 01201 * 4b Re-add to cache. 01202 */ 01203 01204 // Special case for absolute path icons. 01205 if (name.startsWith(QLatin1String("favicons/"))) 01206 { 01207 favIconOverlay = true; 01208 name = KStandardDirs::locateLocal("cache", name+".png"); 01209 } 01210 01211 bool absolutePath = !pathIsRelative(name); 01212 if (!absolutePath) { 01213 name = d->removeIconExtension(name); 01214 } 01215 01216 // Don't bother looking for an icon with no name. 01217 if (name.isEmpty()) { 01218 return QPixmap(); 01219 } 01220 01221 // May modify group, size, or state. This function puts them into sane 01222 // states. 01223 d->normalizeIconMetadata(group, size, state); 01224 01225 // See if the image is already cached. 01226 QString key = d->makeCacheKey(name, group, overlays, size, state); 01227 QPixmap pix; 01228 bool iconWasUnknown = false; 01229 K3Icon icon; 01230 01231 if (d->findCachedPixmapWithPath(key, pix, icon.path)) { 01232 if (path_store) { 01233 *path_store = icon.path; 01234 } 01235 01236 // We cache the pixmap for the event of trying to find an unknown icon 01237 // with canReturnNull set to false, but if we *can* return null then 01238 // we should do so when necessary. 01239 if (canReturnNull && icon.path.isEmpty()) { 01240 return QPixmap(); 01241 } 01242 return pix; 01243 } 01244 01245 // Image is not cached... go find it and apply effects. 01246 if (!d->initIconThemes()) { 01247 return QPixmap(); 01248 } 01249 01250 favIconOverlay = favIconOverlay && size > 22; 01251 01252 // First we look for non-User icons. If we don't find one we'd search in 01253 // the User space anyways... 01254 if (group != KIconLoader::User) { 01255 // K3Icon seems to hold some needed information. 01256 01257 if (absolutePath && !favIconOverlay) 01258 { 01259 icon.context = KIconLoader::Any; 01260 icon.type = KIconLoader::Scalable; 01261 icon.path = name; 01262 } 01263 else 01264 { 01265 icon = d->findMatchingIconWithGenericFallbacks(favIconOverlay ? QString("text-html") : name, size); 01266 } 01267 } 01268 01269 if (icon.path.isEmpty()) { 01270 // We do have a "User" icon, or we couldn't find the non-User one. 01271 icon.path = (absolutePath) ? name : 01272 iconPath(name, KIconLoader::User, canReturnNull); 01273 } 01274 01275 // Still can't find it? Use "unknown" if we can't return null. 01276 // We keep going in the function so we can ensure this result gets cached. 01277 if (icon.path.isEmpty() && !canReturnNull) { 01278 icon.path = d->unknownIconPath(size); 01279 iconWasUnknown = true; 01280 } 01281 01282 QImage img = d->createIconImage(icon.path, size); 01283 01284 if (group >= 0) 01285 { 01286 img = d->mpEffect.apply(img, group, state); 01287 } 01288 01289 if (favIconOverlay) 01290 { 01291 QImage favIcon(name, "PNG"); 01292 if (!favIcon.isNull()) // if favIcon not there yet, don't try to blend it 01293 { 01294 QPainter p(&img); 01295 01296 // Align the favicon overlay 01297 QRect r(favIcon.rect()); 01298 r.moveBottomRight(img.rect().bottomRight()); 01299 r.adjust(-1, -1, -1, -1); // Move off edge 01300 01301 // Blend favIcon over img. 01302 p.drawImage(r, favIcon); 01303 } 01304 } 01305 01306 pix = QPixmap::fromImage(img); 01307 01308 // TODO: If we make a loadIcon that returns the image we can convert 01309 // drawOverlays to use the image instead of pixmaps as well so we don't 01310 // have to transfer so much to the graphics card. 01311 d->drawOverlays(this, group, state, pix, overlays); 01312 01313 // Don't add the path to our unknown icon to the cache, only cache the 01314 // actual image. 01315 if (iconWasUnknown) { 01316 icon.path.clear(); 01317 } 01318 01319 d->insertCachedPixmapWithPath(key, pix, icon.path); 01320 01321 if (path_store) { 01322 *path_store = icon.path; 01323 } 01324 01325 return pix; 01326 } 01327 01328 QMovie *KIconLoader::loadMovie(const QString& name, KIconLoader::Group group, int size, QObject *parent) const 01329 { 01330 QString file = moviePath( name, group, size ); 01331 if (file.isEmpty()) 01332 return 0; 01333 int dirLen = file.lastIndexOf('/'); 01334 QString icon = iconPath(name, size ? -size : group, true); 01335 if (!icon.isEmpty() && file.left(dirLen) != icon.left(dirLen)) 01336 return 0; 01337 QMovie *movie = new QMovie(file, QByteArray(), parent); 01338 if (!movie->isValid()) 01339 { 01340 delete movie; 01341 return 0; 01342 } 01343 return movie; 01344 } 01345 01346 QString KIconLoader::moviePath(const QString& name, KIconLoader::Group group, int size) const 01347 { 01348 if (!d->mpGroups) return QString(); 01349 01350 d->initIconThemes(); 01351 01352 if ( (group < -1 || group >= KIconLoader::LastGroup) && group != KIconLoader::User ) 01353 { 01354 kDebug(264) << "Illegal icon group: " << group; 01355 group = KIconLoader::Desktop; 01356 } 01357 if (size == 0 && group < 0) 01358 { 01359 kDebug(264) << "Neither size nor group specified!"; 01360 group = KIconLoader::Desktop; 01361 } 01362 01363 QString file = name + ".mng"; 01364 if (group == KIconLoader::User) 01365 { 01366 file = d->mpDirs->findResource("appicon", file); 01367 } 01368 else 01369 { 01370 if (size == 0) 01371 size = d->mpGroups[group].size; 01372 01373 K3Icon icon; 01374 01375 foreach(KIconThemeNode *themeNode, d->links) 01376 { 01377 icon = themeNode->theme->iconPath(file, size, KIconLoader::MatchExact); 01378 if (icon.isValid()) 01379 break; 01380 } 01381 01382 if ( !icon.isValid() ) 01383 { 01384 foreach(KIconThemeNode *themeNode, d->links) 01385 { 01386 icon = themeNode->theme->iconPath(file, size, KIconLoader::MatchBest); 01387 if (icon.isValid()) 01388 break; 01389 } 01390 } 01391 01392 file = icon.isValid() ? icon.path : QString(); 01393 } 01394 return file; 01395 } 01396 01397 01398 QStringList KIconLoader::loadAnimated(const QString& name, KIconLoader::Group group, int size) const 01399 { 01400 QStringList lst; 01401 01402 if (!d->mpGroups) return lst; 01403 01404 d->initIconThemes(); 01405 01406 if ((group < -1) || (group >= KIconLoader::LastGroup)) 01407 { 01408 kDebug(264) << "Illegal icon group: " << group; 01409 group = KIconLoader::Desktop; 01410 } 01411 if ((size == 0) && (group < 0)) 01412 { 01413 kDebug(264) << "Neither size nor group specified!"; 01414 group = KIconLoader::Desktop; 01415 } 01416 01417 QString file = name + "/0001"; 01418 if (group == KIconLoader::User) 01419 { 01420 file = d->mpDirs->findResource("appicon", file + ".png"); 01421 } else 01422 { 01423 if (size == 0) 01424 size = d->mpGroups[group].size; 01425 K3Icon icon = d->findMatchingIcon(file, size); 01426 file = icon.isValid() ? icon.path : QString(); 01427 01428 } 01429 if (file.isEmpty()) 01430 return lst; 01431 01432 QString path = file.left(file.length()-8); 01433 DIR* dp = opendir( QFile::encodeName(path) ); 01434 if(!dp) 01435 return lst; 01436 01437 KDE_struct_dirent* ep; 01438 while( ( ep = KDE_readdir( dp ) ) != 0L ) 01439 { 01440 QString fn(QFile::decodeName(ep->d_name)); 01441 if(!(fn.left(4)).toUInt()) 01442 continue; 01443 01444 lst += path + fn; 01445 } 01446 closedir ( dp ); 01447 lst.sort(); 01448 return lst; 01449 } 01450 01451 KIconTheme *KIconLoader::theme() const 01452 { 01453 d->initIconThemes(); 01454 if (d->mpThemeRoot) return d->mpThemeRoot->theme; 01455 return 0L; 01456 } 01457 01458 int KIconLoader::currentSize(KIconLoader::Group group) const 01459 { 01460 if (!d->mpGroups) return -1; 01461 01462 if (group < 0 || group >= KIconLoader::LastGroup) 01463 { 01464 kDebug(264) << "Illegal icon group: " << group; 01465 return -1; 01466 } 01467 return d->mpGroups[group].size; 01468 } 01469 01470 QStringList KIconLoader::queryIconsByDir( const QString& iconsDir ) const 01471 { 01472 const QDir dir(iconsDir); 01473 const QStringList formats = QStringList() << "*.png" << "*.xpm" << "*.svg" << "*.svgz"; 01474 const QStringList lst = dir.entryList(formats, QDir::Files); 01475 QStringList result; 01476 QStringList::ConstIterator it; 01477 for (it=lst.begin(); it!=lst.end(); ++it) 01478 result += iconsDir + '/' + *it; 01479 return result; 01480 } 01481 01482 QStringList KIconLoader::queryIconsByContext(int group_or_size, 01483 KIconLoader::Context context) const 01484 { 01485 d->initIconThemes(); 01486 01487 QStringList result; 01488 if (group_or_size >= KIconLoader::LastGroup) 01489 { 01490 kDebug(264) << "Illegal icon group: " << group_or_size; 01491 return result; 01492 } 01493 int size; 01494 if (group_or_size >= 0) 01495 size = d->mpGroups[group_or_size].size; 01496 else 01497 size = -group_or_size; 01498 01499 foreach(KIconThemeNode *themeNode, d->links) 01500 themeNode->queryIconsByContext(&result, size, context); 01501 01502 // Eliminate duplicate entries (same icon in different directories) 01503 QString name; 01504 QStringList res2, entries; 01505 QStringList::ConstIterator it; 01506 for (it=result.constBegin(); it!=result.constEnd(); ++it) 01507 { 01508 int n = (*it).lastIndexOf('/'); 01509 if (n == -1) 01510 name = *it; 01511 else 01512 name = (*it).mid(n+1); 01513 name = d->removeIconExtension(name); 01514 if (!entries.contains(name)) 01515 { 01516 entries += name; 01517 res2 += *it; 01518 } 01519 } 01520 return res2; 01521 01522 } 01523 01524 QStringList KIconLoader::queryIcons(int group_or_size, KIconLoader::Context context) const 01525 { 01526 d->initIconThemes(); 01527 01528 QStringList result; 01529 if (group_or_size >= KIconLoader::LastGroup) 01530 { 01531 kDebug(264) << "Illegal icon group: " << group_or_size; 01532 return result; 01533 } 01534 int size; 01535 if (group_or_size >= 0) 01536 size = d->mpGroups[group_or_size].size; 01537 else 01538 size = -group_or_size; 01539 01540 foreach(KIconThemeNode *themeNode, d->links) 01541 themeNode->queryIcons(&result, size, context); 01542 01543 // Eliminate duplicate entries (same icon in different directories) 01544 QString name; 01545 QStringList res2, entries; 01546 QStringList::ConstIterator it; 01547 for (it=result.constBegin(); it!=result.constEnd(); ++it) 01548 { 01549 int n = (*it).lastIndexOf('/'); 01550 if (n == -1) 01551 name = *it; 01552 else 01553 name = (*it).mid(n+1); 01554 name = d->removeIconExtension(name); 01555 if (!entries.contains(name)) 01556 { 01557 entries += name; 01558 res2 += *it; 01559 } 01560 } 01561 return res2; 01562 } 01563 01564 // used by KIconDialog to find out which contexts to offer in a combobox 01565 bool KIconLoader::hasContext(KIconLoader::Context context) const 01566 { 01567 d->initIconThemes(); 01568 01569 foreach(KIconThemeNode *themeNode, d->links) 01570 if( themeNode->theme->hasContext( context )) 01571 return true; 01572 return false; 01573 } 01574 01575 KIconEffect * KIconLoader::iconEffect() const 01576 { 01577 return &d->mpEffect; 01578 } 01579 01580 bool KIconLoader::alphaBlending(KIconLoader::Group group) const 01581 { 01582 if (!d->mpGroups) return false; 01583 01584 if (group < 0 || group >= KIconLoader::LastGroup) 01585 { 01586 kDebug(264) << "Illegal icon group: " << group; 01587 return false; 01588 } 01589 return d->mpGroups[group].alphaBlending; 01590 } 01591 01592 // deprecated 01593 #ifndef KDE_NO_DEPRECATED 01594 QIcon KIconLoader::loadIconSet( const QString& name, KIconLoader::Group g, int s, 01595 bool canReturnNull ) 01596 { 01597 QIcon iconset; 01598 QPixmap tmp = loadIcon(name, g, s, KIconLoader::ActiveState, QStringList(), NULL, canReturnNull); 01599 iconset.addPixmap( tmp, QIcon::Active, QIcon::On ); 01600 // we don't use QIconSet's resizing anyway 01601 tmp = loadIcon(name, g, s, KIconLoader::DisabledState, QStringList(), NULL, canReturnNull); 01602 iconset.addPixmap( tmp, QIcon::Disabled, QIcon::On ); 01603 tmp = loadIcon(name, g, s, KIconLoader::DefaultState, QStringList(), NULL, canReturnNull); 01604 iconset.addPixmap( tmp, QIcon::Normal, QIcon::On ); 01605 return iconset; 01606 } 01607 #endif 01608 01609 // Easy access functions 01610 01611 QPixmap DesktopIcon(const QString& name, int force_size, int state, const QStringList &overlays) 01612 { 01613 KIconLoader *loader = KIconLoader::global(); 01614 return loader->loadIcon(name, KIconLoader::Desktop, force_size, state, overlays); 01615 } 01616 01617 // deprecated 01618 #ifndef KDE_NO_DEPRECATED 01619 QIcon DesktopIconSet(const QString& name, int force_size) 01620 { 01621 KIconLoader *loader = KIconLoader::global(); 01622 return loader->loadIconSet(name, KIconLoader::Desktop, force_size); 01623 } 01624 #endif 01625 01626 QPixmap BarIcon(const QString& name, int force_size, int state, const QStringList &overlays) 01627 { 01628 KIconLoader *loader = KIconLoader::global(); 01629 return loader->loadIcon(name, KIconLoader::Toolbar, force_size, state, overlays); 01630 } 01631 01632 // deprecated 01633 #ifndef KDE_NO_DEPRECATED 01634 QIcon BarIconSet(const QString& name, int force_size) 01635 { 01636 KIconLoader *loader = KIconLoader::global(); 01637 return loader->loadIconSet( name, KIconLoader::Toolbar, force_size ); 01638 } 01639 #endif 01640 01641 QPixmap SmallIcon(const QString& name, int force_size, int state, const QStringList &overlays) 01642 { 01643 KIconLoader *loader = KIconLoader::global(); 01644 return loader->loadIcon(name, KIconLoader::Small, force_size, state, overlays); 01645 } 01646 01647 // deprecated 01648 #ifndef KDE_NO_DEPRECATED 01649 QIcon SmallIconSet(const QString& name, int force_size) 01650 { 01651 KIconLoader *loader = KIconLoader::global(); 01652 return loader->loadIconSet( name, KIconLoader::Small, force_size ); 01653 } 01654 #endif 01655 01656 QPixmap MainBarIcon(const QString& name, int force_size, int state, const QStringList &overlays) 01657 { 01658 KIconLoader *loader = KIconLoader::global(); 01659 return loader->loadIcon(name, KIconLoader::MainToolbar, force_size, state, overlays); 01660 } 01661 01662 // deprecated 01663 #ifndef KDE_NO_DEPRECATED 01664 QIcon MainBarIconSet(const QString& name, int force_size) 01665 { 01666 KIconLoader *loader = KIconLoader::global(); 01667 return loader->loadIconSet( name, KIconLoader::MainToolbar, force_size ); 01668 } 01669 #endif 01670 01671 QPixmap UserIcon(const QString& name, int state, const QStringList &overlays) 01672 { 01673 KIconLoader *loader = KIconLoader::global(); 01674 return loader->loadIcon(name, KIconLoader::User, 0, state, overlays); 01675 } 01676 01677 // deprecated 01678 #ifndef KDE_NO_DEPRECATED 01679 QIcon UserIconSet(const QString& name) 01680 { 01681 KIconLoader *loader = KIconLoader::global(); 01682 return loader->loadIconSet( name, KIconLoader::User ); 01683 } 01684 #endif 01685 01686 int IconSize(KIconLoader::Group group) 01687 { 01688 KIconLoader *loader = KIconLoader::global(); 01689 return loader->currentSize(group); 01690 } 01691 01692 QPixmap KIconLoader::unknown() 01693 { 01694 QPixmap pix; 01695 if ( QPixmapCache::find("unknown", pix) ) //krazy:exclude=iconnames 01696 return pix; 01697 01698 QString path = global()->iconPath("unknown", KIconLoader::Small, true); //krazy:exclude=iconnames 01699 if (path.isEmpty()) 01700 { 01701 kDebug(264) << "Warning: Cannot find \"unknown\" icon."; 01702 pix = QPixmap(32,32); 01703 } else 01704 { 01705 pix.load(path); 01706 QPixmapCache::insert("unknown", pix); //krazy:exclude=iconnames 01707 } 01708 01709 return pix; 01710 } 01711 01712 /*** the global icon loader ***/ 01713 K_GLOBAL_STATIC_WITH_ARGS(KIconLoader, globalIconLoader, (KGlobal::mainComponent(), 0)) 01714 01715 KIconLoader *KIconLoader::global() 01716 { 01717 return globalIconLoader; 01718 } 01719 01720 void KIconLoader::newIconLoader() 01721 { 01722 if ( global() == this) { 01723 KIconTheme::reconfigure(); 01724 } 01725 01726 reconfigure( objectName(), d->mpDirs ); 01727 emit iconLoaderSettingsChanged(); 01728 } 01729 01730 #include "kiconloader.moc" 01731
KDE 4.6 API Reference