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