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 d->mIconCache->clear(); 00456 delete d; 00457 d = new KIconLoaderPrivate(this); 00458 d->init( _appname, _dirs ); 00459 } 00460 00461 void KIconLoaderPrivate::init( const QString& _appname, KStandardDirs *_dirs ) 00462 { 00463 extraDesktopIconsLoaded=false; 00464 mIconThemeInited = false; 00465 mpThemeRoot = 0; 00466 00467 if (_dirs) 00468 mpDirs = _dirs; 00469 else 00470 mpDirs = KGlobal::dirs(); 00471 00472 appname = _appname; 00473 if (appname.isEmpty()) 00474 appname = KGlobal::mainComponent().componentName(); 00475 00476 // Initialize icon cache 00477 mIconCache = new KSharedDataCache("icon-cache", 10 * 1024 * 1024); 00478 // Cost here is number of pixels, not size. So this is actually a bit 00479 // smaller. 00480 mPixmapCache.setMaxCost(10 * 1024 * 1024); 00481 00482 // These have to match the order in kicontheme.h 00483 static const char * const groups[] = { "Desktop", "Toolbar", "MainToolbar", "Small", "Panel", "Dialog", 0L }; 00484 KSharedConfig::Ptr config = KGlobal::config(); 00485 00486 // loading config and default sizes 00487 initIconThemes(); 00488 KIconTheme *defaultSizesTheme = links.empty() ? 0 : links.first()->theme; 00489 mpGroups = new KIconGroup[(int) KIconLoader::LastGroup]; 00490 for (KIconLoader::Group i = KIconLoader::FirstGroup; i < KIconLoader::LastGroup; ++i) { 00491 if (groups[i] == 0L) { 00492 break; 00493 } 00494 00495 KConfigGroup cg(config, QLatin1String(groups[i]) + "Icons"); 00496 mpGroups[i].size = cg.readEntry("Size", 0); 00497 if (QPixmap::defaultDepth() > 8) { 00498 mpGroups[i].alphaBlending = cg.readEntry("AlphaBlending", true); 00499 } else { 00500 mpGroups[i].alphaBlending = false; 00501 } 00502 00503 if (!mpGroups[i].size && defaultSizesTheme) { 00504 mpGroups[i].size = defaultSizesTheme->defaultSize(i); 00505 } 00506 } 00507 } 00508 00509 bool KIconLoaderPrivate::initIconThemes() 00510 { 00511 if (mIconThemeInited) { 00512 // If mpThemeRoot isn't 0 then initing has succeeded 00513 return (mpThemeRoot != 0); 00514 } 00515 //kDebug(264); 00516 mIconThemeInited = true; 00517 00518 // Add the default theme and its base themes to the theme tree 00519 KIconTheme *def = new KIconTheme(KIconTheme::current(), appname); 00520 if (!def->isValid()) 00521 { 00522 delete def; 00523 // warn, as this is actually a small penalty hit 00524 kDebug(264) << "Couldn't find current icon theme, falling back to default."; 00525 def = new KIconTheme(KIconTheme::defaultThemeName(), appname); 00526 if (!def->isValid()) 00527 { 00528 kError(264) << "Error: standard icon theme" << KIconTheme::defaultThemeName() << "not found!" << endl; 00529 delete def; 00530 return false; 00531 } 00532 } 00533 mpThemeRoot = new KIconThemeNode(def); 00534 mThemesInTree.append(def->internalName()); 00535 links.append(mpThemeRoot); 00536 addBaseThemes(mpThemeRoot, appname); 00537 00538 // Insert application specific themes at the top. 00539 mpDirs->addResourceType("appicon", "data", appname + "/pics/"); 00540 // ################## KDE5: consider removing the toolbar directory 00541 mpDirs->addResourceType("appicon", "data", appname + "/toolbar/"); 00542 00543 // Add legacy icon dirs. 00544 QStringList dirs; 00545 dirs += mpDirs->resourceDirs("icon"); 00546 dirs += mpDirs->resourceDirs("pixmap"); 00547 dirs += mpDirs->resourceDirs("xdgdata-icon"); 00548 dirs += "/usr/share/pixmaps"; 00549 // These are not in the icon spec, but e.g. GNOME puts some icons there anyway. 00550 dirs += mpDirs->resourceDirs("xdgdata-pixmap"); 00551 for (QStringList::ConstIterator it = dirs.constBegin(); it != dirs.constEnd(); ++it) 00552 mpDirs->addResourceDir("appicon", *it); 00553 00554 #ifndef NDEBUG 00555 QString dbgString = "Theme tree: "; 00556 mpThemeRoot->printTree(dbgString); 00557 kDebug(264) << dbgString; 00558 #endif 00559 00560 return true; 00561 } 00562 00563 KIconLoader::~KIconLoader() 00564 { 00565 delete d; 00566 } 00567 00568 void KIconLoader::addAppDir(const QString& appname) 00569 { 00570 d->initIconThemes(); 00571 00572 d->mpDirs->addResourceType("appicon", "data", appname + "/pics/"); 00573 // ################## KDE5: consider removing the toolbar directory 00574 d->mpDirs->addResourceType("appicon", "data", appname + "/toolbar/"); 00575 d->addAppThemes(appname); 00576 } 00577 00578 void KIconLoaderPrivate::addAppThemes(const QString& appname) 00579 { 00580 initIconThemes(); 00581 00582 KIconTheme *def = new KIconTheme(KIconTheme::current(), appname); 00583 if (!def->isValid()) { 00584 delete def; 00585 def = new KIconTheme(KIconTheme::defaultThemeName(), appname); 00586 } 00587 KIconThemeNode* node = new KIconThemeNode(def); 00588 bool addedToLinks = false; 00589 00590 if (!mThemesInTree.contains(node->theme->internalName())) { 00591 mThemesInTree.append(node->theme->internalName()); 00592 links.append(node); 00593 addedToLinks = true; 00594 } 00595 addBaseThemes(node, appname); 00596 00597 if (!addedToLinks) { 00598 // Nodes in links are being deleted later - this one needs manual care. 00599 delete node; 00600 } 00601 } 00602 00603 void KIconLoaderPrivate::addBaseThemes(KIconThemeNode *node, const QString &appname) 00604 { 00605 // Quote from the icon theme specification: 00606 // The lookup is done first in the current theme, and then recursively 00607 // in each of the current theme's parents, and finally in the 00608 // default theme called "hicolor" (implementations may add more 00609 // default themes before "hicolor", but "hicolor" must be last). 00610 // 00611 // So we first make sure that all inherited themes are added, then we 00612 // add the KDE default theme as fallback for all icons that might not be 00613 // present in an inherited theme, and hicolor goes last. 00614 00615 addInheritedThemes(node, appname); 00616 addThemeByName(KIconTheme::defaultThemeName(), appname); 00617 addThemeByName("hicolor", appname); 00618 } 00619 00620 void KIconLoaderPrivate::addInheritedThemes(KIconThemeNode *node, const QString &appname) 00621 { 00622 const QStringList lst = node->theme->inherits(); 00623 00624 for (QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it) { 00625 if ((*it) == "hicolor") { 00626 // The icon theme spec says that "hicolor" must be the very last 00627 // of all inherited themes, so don't add it here but at the very end 00628 // of addBaseThemes(). 00629 continue; 00630 } 00631 addThemeByName(*it, appname); 00632 } 00633 } 00634 00635 void KIconLoaderPrivate::addThemeByName(const QString &themename, const QString &appname) 00636 { 00637 if (mThemesInTree.contains(themename + appname)) { 00638 return; 00639 } 00640 KIconTheme *theme = new KIconTheme(themename, appname); 00641 if (!theme->isValid()) { 00642 delete theme; 00643 return; 00644 } 00645 KIconThemeNode *n = new KIconThemeNode(theme); 00646 mThemesInTree.append(themename + appname); 00647 links.append(n); 00648 addInheritedThemes(n, appname); 00649 } 00650 00651 void KIconLoader::addExtraDesktopThemes() 00652 { 00653 if ( d->extraDesktopIconsLoaded ) return; 00654 00655 d->initIconThemes(); 00656 00657 QStringList list; 00658 const QStringList icnlibs = KGlobal::dirs()->resourceDirs("icon"); 00659 QStringList::ConstIterator it; 00660 char buf[1000]; 00661 int r; 00662 for (it=icnlibs.begin(); it!=icnlibs.end(); ++it) 00663 { 00664 QDir dir(*it); 00665 if (!dir.exists()) 00666 continue; 00667 const QStringList lst = dir.entryList(QStringList( "default.*" ), QDir::Dirs); 00668 QStringList::ConstIterator it2; 00669 for (it2=lst.begin(); it2!=lst.end(); ++it2) 00670 { 00671 if (!KStandardDirs::exists(*it + *it2 + "/index.desktop") 00672 && !KStandardDirs::exists(*it + *it2 + "/index.theme")) 00673 continue; 00674 r=readlink( QFile::encodeName(*it + *it2) , buf, sizeof(buf)-1); 00675 if ( r>0 ) 00676 { 00677 buf[r]=0; 00678 const QDir dir2( buf ); 00679 QString themeName=dir2.dirName(); 00680 00681 if (!list.contains(themeName)) 00682 list.append(themeName); 00683 } 00684 } 00685 } 00686 00687 for (it = list.constBegin(); it != list.constEnd(); ++it) 00688 { 00689 // Don't add the KDE defaults once more, we have them anyways. 00690 if (*it == QLatin1String("default.kde") 00691 || *it == QLatin1String("default.kde4")) { 00692 continue; 00693 } 00694 d->addThemeByName(*it, ""); 00695 } 00696 00697 d->extraDesktopIconsLoaded=true; 00698 00699 } 00700 00701 bool KIconLoader::extraDesktopThemesAdded() const 00702 { 00703 return d->extraDesktopIconsLoaded; 00704 } 00705 00706 void KIconLoader::drawOverlays(const QStringList &overlays, QPixmap &pixmap, KIconLoader::Group group, int state) const 00707 { 00708 d->drawOverlays(this, group, state, pixmap, overlays); 00709 } 00710 00711 QString KIconLoaderPrivate::removeIconExtension(const QString &name) const 00712 { 00713 if (name.endsWith(QLatin1String(".png")) 00714 || name.endsWith(QLatin1String(".xpm")) 00715 || name.endsWith(QLatin1String(".svg"))) { 00716 return name.left(name.length() - 4); 00717 } else if (name.endsWith(QLatin1String(".svgz"))) { 00718 return name.left(name.length() - 5); 00719 } 00720 00721 return name; 00722 } 00723 00724 void KIconLoaderPrivate::normalizeIconMetadata(KIconLoader::Group &group, int &size, int &state) const 00725 { 00726 if ((state < 0) || (state >= KIconLoader::LastState)) 00727 { 00728 kWarning(264) << "Illegal icon state: " << state; 00729 state = KIconLoader::DefaultState; 00730 } 00731 00732 if (size < 0) { 00733 size = 0; 00734 } 00735 00736 // For "User" icons, bail early since the size should be based on the size on disk, 00737 // which we've already checked. 00738 if (group == KIconLoader::User) { 00739 return; 00740 } 00741 00742 if ((group < -1) || (group >= KIconLoader::LastGroup)) 00743 { 00744 kWarning(264) << "Illegal icon group: " << group; 00745 group = KIconLoader::Desktop; 00746 } 00747 00748 // If size == 0, use default size for the specified group. 00749 if (size == 0) 00750 { 00751 if (group < 0) 00752 { 00753 kWarning(264) << "Neither size nor group specified!"; 00754 group = KIconLoader::Desktop; 00755 } 00756 size = mpGroups[group].size; 00757 } 00758 } 00759 00760 QString KIconLoaderPrivate::makeCacheKey(const QString &name, KIconLoader::Group group, 00761 const QStringList &overlays, int size, int state) const 00762 { 00763 // The KSharedDataCache is shared so add some namespacing. The following code 00764 // uses QStringBuilder (new in Qt 4.6) 00765 00766 return (group == KIconLoader::User 00767 ? QLatin1Literal("$kicou_") 00768 : QLatin1Literal("$kico_")) 00769 % name 00770 % QLatin1Char('_') 00771 % QString::number(size) 00772 % QLatin1Char('_') 00773 % overlays.join("_") 00774 % ( group >= 0 ? mpEffect.fingerprint(group, state) 00775 : *NULL_EFFECT_FINGERPRINT); 00776 } 00777 00778 QImage KIconLoaderPrivate::createIconImage(const QString &path, int size) 00779 { 00780 // Use the extension as the format. Works for XPM and PNG, but not for SVG. The 00781 // "VGZ" is the last 3 characters of "SVGZ" 00782 QString ext = path.right(3).toUpper(); 00783 QImage img; 00784 00785 if (ext != "SVG" && ext != "VGZ") 00786 { 00787 // Not a SVG or SVGZ 00788 img = QImage(path, ext.toLatin1()); 00789 00790 if (size != 0 && !img.isNull()) { 00791 img = img.scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 00792 } 00793 } 00794 else 00795 { 00796 #ifndef _WIN32_WCE 00797 QSvgRenderer renderer(path, q); 00798 00799 if (renderer.isValid()) { 00800 img = QImage(size, size, QImage::Format_ARGB32_Premultiplied); 00801 img.fill(0); 00802 QPainter p(&img); 00803 renderer.render(&p); 00804 } 00805 #endif 00806 } 00807 00808 return img; 00809 } 00810 00811 void KIconLoaderPrivate::insertCachedPixmapWithPath( 00812 const QString &key, 00813 const QPixmap &data, 00814 const QString &path = QString()) 00815 { 00816 // Even if the pixmap is null, we add it to the caches so that we record 00817 // the fact that whatever icon led to us getting a null pixmap doesn't 00818 // exist. 00819 00820 QBuffer output; 00821 output.open(QIODevice::WriteOnly); 00822 00823 QDataStream outputStream(&output); 00824 outputStream.setVersion(QDataStream::Qt_4_6); 00825 00826 outputStream << path; 00827 00828 // Convert the QPixmap to PNG. This is actually done by Qt's own operator. 00829 outputStream << data; 00830 00831 output.close(); 00832 00833 // The byte array contained in the QBuffer is what we want in the cache. 00834 mIconCache->insert(key, output.buffer()); 00835 00836 // Also insert the object into our process-local cache for even more 00837 // speed. 00838 PixmapWithPath *pixmapPath = new PixmapWithPath; 00839 pixmapPath->pixmap = data; 00840 pixmapPath->path = path; 00841 00842 mPixmapCache.insert(key, pixmapPath, data.width() * data.height() + 1); 00843 } 00844 00845 bool KIconLoaderPrivate::findCachedPixmapWithPath(const QString &key, QPixmap &data, QString &path) 00846 { 00847 // If the pixmap is present in our local process cache, use that since we 00848 // don't need to decompress and upload it to the X server/graphics card. 00849 const PixmapWithPath *pixmapPath = mPixmapCache.object(key); 00850 if (pixmapPath) { 00851 path = pixmapPath->path; 00852 data = pixmapPath->pixmap; 00853 00854 return true; 00855 } 00856 00857 // Otherwise try to find it in our shared memory cache since that will 00858 // be quicker than the disk, especially for SVGs. 00859 QByteArray result; 00860 00861 if (!mIconCache->find(key, &result) || result.isEmpty()) { 00862 return false; 00863 } 00864 00865 QBuffer buffer; 00866 buffer.setBuffer(&result); 00867 buffer.open(QIODevice::ReadOnly); 00868 00869 QDataStream inputStream(&buffer); 00870 inputStream.setVersion(QDataStream::Qt_4_6); 00871 00872 QString tempPath; 00873 inputStream >> tempPath; 00874 00875 if (inputStream.status() == QDataStream::Ok) { 00876 QPixmap tempPixmap; 00877 inputStream >> tempPixmap; 00878 00879 if (inputStream.status() == QDataStream::Ok) { 00880 data = tempPixmap; 00881 path = tempPath; 00882 00883 // Since we're here we didn't have a QPixmap cache entry, add one now. 00884 PixmapWithPath *newPixmapWithPath = new PixmapWithPath; 00885 newPixmapWithPath->pixmap = data; 00886 newPixmapWithPath->path = path; 00887 00888 mPixmapCache.insert(key, newPixmapWithPath, data.width() * data.height() + 1); 00889 00890 return true; 00891 } 00892 } 00893 00894 return false; 00895 } 00896 00897 K3Icon KIconLoaderPrivate::findMatchingIconWithGenericFallbacks(const QString& name, int size) const 00898 { 00899 K3Icon icon = findMatchingIcon(name, size); 00900 if (icon.isValid()) 00901 return icon; 00902 00903 const QString genericIcon = s_globalData->genericIconFor(name); 00904 if (!genericIcon.isEmpty()) { 00905 icon = findMatchingIcon(genericIcon, size); 00906 } 00907 return icon; 00908 } 00909 00910 K3Icon KIconLoaderPrivate::findMatchingIcon(const QString& name, int size) const 00911 { 00912 const_cast<KIconLoaderPrivate*>(this)->initIconThemes(); 00913 00914 K3Icon icon; 00915 00916 // The following code has been commented out because the Qt SVG renderer needs 00917 // to be improved. If you are going to change/remove some code from this part, 00918 // please contact me before (ereslibre@kde.org), or kde-core-devel@kde.org. (ereslibre) 00919 #ifdef KDE_QT_SVG_RENDERER_FIXED 00920 const char * ext1[4] = { ".png", ".svgz", ".svg", ".xpm" }; 00921 const char * ext2[4] = { ".svgz", ".svg", ".png", ".xpm" }; 00922 const char ** ext; 00923 00924 if (size == KIconLoader::SizeSmall || 00925 size == KIconLoader::SizeSmallMedium || 00926 size == KIconLoader::SizeMedium || 00927 size == KIconLoader::SizeLarge || 00928 size == KIconLoader::SizeHuge || 00929 size == KIconLoader::SizeEnormous) 00930 { 00931 ext = ext1; // size is standard, give preference to PNG over SVG when searching 00932 } 00933 else 00934 { 00935 ext = ext2; // size is non-standard, give preference to SVG over PNG when searching 00936 } 00937 00938 /* If size parameter is a standard one, that means: 00939 00940 - KIconLoader::SizeSmall 00941 - KIconLoader::SizeSmallMedium 00942 - KIconLoader::SizeMedium 00943 - KIconLoader::SizeLarge 00944 - KIconLoader::SizeHuge 00945 - KIconLoader::SizeEnormous 00946 00947 To follow the XDG icon theme and icon naming specifications, 00948 the order in which we look for an icon is: 00949 00950 png, svgz, svg, xpm exact match 00951 png, svgz, svg, xpm best match 00952 less specific fallback in this theme: png, svgz, svg, xpm exact match 00953 png, svgz, svg, xpm best match 00954 even less specific fallback in this theme: [same order] 00955 (...) 00956 00957 next theme in inheritance tree: png, svgz, svg, xpm exact match 00958 png, svgz, svg, xpm best match 00959 less specific fallbacks in this next theme 00960 (...) 00961 00962 next theme in inheritance tree: png, svgz, svg, xpm exact match 00963 png, svgz, svg, xpm best match 00964 less specific fallbacks in this next theme 00965 (...) 00966 00967 and so on. 00968 00969 If size parameter is a non-standard one, then we give more preference to 00970 SVG format since drawing SVG's gives better quality and despite being 00971 slower than resizing a PNG image, the cases where non-standard sizes are 00972 asked are very rare. For non-standard sizes what we have is: 00973 00974 svgz, svg, png, xpm exact match 00975 svgz, svg, png, xpm best match 00976 less specific fallback in this theme: svgz, svg, png, xpm exact match 00977 svgz, svg, png, xpm best match 00978 even less specific fallback in this theme: [same order] 00979 (...) 00980 00981 next theme in inheritance tree: svgz, svg, png, xpm exact match 00982 svgz, svg, png, xpm best match 00983 less specific fallbacks in this next theme 00984 (...) 00985 00986 next theme in inheritance tree: svgz, svg, png, xpm exact match 00987 svgz, svg, png, xpm best match 00988 less specific fallbacks in this next theme 00989 (...) 00990 00991 and so on. 00992 */ 00993 #else 00994 const char * const ext[4] = { ".png", ".svgz", ".svg", ".xpm" }; 00995 #endif 00996 00997 bool genericFallback = name.endsWith(QLatin1String("-x-generic")); 00998 00999 foreach(KIconThemeNode *themeNode, links) 01000 { 01001 QString currentName = name; 01002 01003 while (!currentName.isEmpty()) 01004 { 01005 01006 //kDebug(264) << "Looking up" << currentName; 01007 01008 // The following code has been commented out because the Qt SVG renderer needs 01009 // to be improved. If you are going to change/remove some code from this part, 01010 // please contact me before (ereslibre@kde.org), or kde-core-devel@kde.org. (ereslibre) 01011 #ifdef KDE_QT_SVG_RENDERER_FIXED 01012 for (int i = 0 ; i < 4 ; i++) 01013 { 01014 icon = themeNode->theme->iconPath(currentName + ext[i], size, KIconLoader::MatchExact); 01015 if (icon.isValid()) 01016 return icon; 01017 } 01018 01019 for (int i = 0 ; i < 4 ; i++) 01020 { 01021 icon = themeNode->theme->iconPath(currentName + ext[i], size, KIconLoader::MatchBest); 01022 if (icon.isValid()) 01023 return icon; 01024 } 01025 #else 01026 for (int i = 0 ; i < 4 ; i++) 01027 { 01028 icon = themeNode->theme->iconPath(currentName + ext[i], size, KIconLoader::MatchExact); 01029 if (icon.isValid()) 01030 return icon; 01031 01032 icon = themeNode->theme->iconPath(currentName + ext[i], size, KIconLoader::MatchBest); 01033 if (icon.isValid()) 01034 return icon; 01035 } 01036 #endif 01037 if (genericFallback) 01038 // we already tested the base name 01039 break; 01040 01041 int rindex = currentName.lastIndexOf('-'); 01042 if (rindex > 1) { // > 1 so that we don't split x-content or x-epoc 01043 currentName.truncate(rindex); 01044 01045 if (currentName.endsWith(QLatin1String("-x"))) 01046 currentName.chop(2); 01047 } else { 01048 // From update-mime-database.c 01049 static const QSet<QString> mediaTypes = QSet<QString>() 01050 << "text" << "application" << "image" << "audio" 01051 << "inode" << "video" << "message" << "model" << "multipart" 01052 << "x-content" << "x-epoc"; 01053 // Shared-mime-info spec says: 01054 // "If [generic-icon] is not specified then the mimetype is used to generate the 01055 // generic icon by using the top-level media type (e.g. "video" in "video/ogg") 01056 // and appending "-x-generic" (i.e. "video-x-generic" in the previous example)." 01057 if (mediaTypes.contains(currentName)) { 01058 currentName += QLatin1String("-x-generic"); 01059 genericFallback = true; 01060 } else { 01061 break; 01062 } 01063 } 01064 } 01065 } 01066 return icon; 01067 } 01068 01069 inline QString KIconLoaderPrivate::unknownIconPath( int size ) const 01070 { 01071 static const QString &str_unknown = KGlobal::staticQString("unknown"); 01072 01073 K3Icon icon = findMatchingIcon(str_unknown, size); 01074 if (!icon.isValid()) 01075 { 01076 kDebug(264) << "Warning: could not find \"Unknown\" icon for size = " 01077 << size << endl; 01078 return QString(); 01079 } 01080 return icon.path; 01081 } 01082 01083 // Finds the absolute path to an icon. 01084 01085 QString KIconLoader::iconPath(const QString& _name, int group_or_size, 01086 bool canReturnNull) const 01087 { 01088 if (!d->initIconThemes()) { 01089 return QString(); 01090 } 01091 01092 if (_name.isEmpty() || !pathIsRelative(_name)) 01093 { 01094 // we have either an absolute path or nothing to work with 01095 return _name; 01096 } 01097 01098 QString name = d->removeIconExtension( _name ); 01099 01100 QString path; 01101 if (group_or_size == KIconLoader::User) 01102 { 01103 static const QString &png_ext = KGlobal::staticQString(".png"); 01104 static const QString &xpm_ext = KGlobal::staticQString(".xpm"); 01105 path = d->mpDirs->findResource("appicon", name + png_ext); 01106 01107 static const QString &svgz_ext = KGlobal::staticQString(".svgz"); 01108 static const QString &svg_ext = KGlobal::staticQString(".svg"); 01109 if (path.isEmpty()) 01110 path = d->mpDirs->findResource("appicon", name + svgz_ext); 01111 if (path.isEmpty()) 01112 path = d->mpDirs->findResource("appicon", name + svg_ext); 01113 if (path.isEmpty()) 01114 path = d->mpDirs->findResource("appicon", name + xpm_ext); 01115 return path; 01116 } 01117 01118 if (group_or_size >= KIconLoader::LastGroup) 01119 { 01120 kDebug(264) << "Illegal icon group: " << group_or_size; 01121 return path; 01122 } 01123 01124 int size; 01125 if (group_or_size >= 0) 01126 size = d->mpGroups[group_or_size].size; 01127 else 01128 size = -group_or_size; 01129 01130 if (_name.isEmpty()) { 01131 if (canReturnNull) 01132 return QString(); 01133 else 01134 return d->unknownIconPath(size); 01135 } 01136 01137 K3Icon icon = d->findMatchingIconWithGenericFallbacks(name, size); 01138 01139 if (!icon.isValid()) 01140 { 01141 // Try "User" group too. 01142 path = iconPath(name, KIconLoader::User, true); 01143 if (!path.isEmpty() || canReturnNull) 01144 return path; 01145 01146 return d->unknownIconPath(size); 01147 } 01148 return icon.path; 01149 } 01150 01151 QPixmap KIconLoader::loadMimeTypeIcon( const QString& _iconName, KIconLoader::Group group, int size, 01152 int state, const QStringList& overlays, QString *path_store ) const 01153 { 01154 QString iconName = _iconName; 01155 const int slashindex = iconName.indexOf(QLatin1Char('/')); 01156 if (slashindex != -1) { 01157 iconName[slashindex] = QLatin1Char('-'); 01158 } 01159 01160 if ( !d->extraDesktopIconsLoaded ) 01161 { 01162 const QPixmap pixmap = loadIcon( iconName, group, size, state, overlays, path_store, true ); 01163 if (!pixmap.isNull() ) { 01164 return pixmap; 01165 } 01166 const_cast<KIconLoader *>(this)->addExtraDesktopThemes(); 01167 } 01168 const QPixmap pixmap = loadIcon(iconName, group, size, state, overlays, path_store, true); 01169 if (pixmap.isNull()) { 01170 // Icon not found, fallback to application/octet-stream 01171 return loadIcon("application-octet-stream", group, size, state, overlays, path_store, false); 01172 } 01173 return pixmap; 01174 } 01175 01176 QPixmap KIconLoader::loadIcon(const QString& _name, KIconLoader::Group group, int size, 01177 int state, const QStringList& overlays, 01178 QString *path_store, bool canReturnNull) const 01179 { 01180 QString name = _name; 01181 bool favIconOverlay = false; 01182 01183 if (size < 0 || _name.isEmpty()) 01184 return QPixmap(); 01185 01186 /* 01187 * This method works in a kind of pipeline, with the following steps: 01188 * 1. Sanity checks. 01189 * 2. Convert _name, group, size, etc. to a key name. 01190 * 3. Check if the key is already cached. 01191 * 4. If not, initialize the theme and find/load the icon. 01192 * 4a Apply overlays 01193 * 4b Re-add to cache. 01194 */ 01195 01196 // Special case for absolute path icons. 01197 if (name.startsWith(QLatin1String("favicons/"))) 01198 { 01199 favIconOverlay = true; 01200 name = KStandardDirs::locateLocal("cache", name+".png"); 01201 } 01202 01203 bool absolutePath = !pathIsRelative(name); 01204 if (!absolutePath) { 01205 name = d->removeIconExtension(name); 01206 } 01207 01208 // Don't bother looking for an icon with no name. 01209 if (name.isEmpty()) { 01210 return QPixmap(); 01211 } 01212 01213 // May modify group, size, or state. This function puts them into sane 01214 // states. 01215 d->normalizeIconMetadata(group, size, state); 01216 01217 // See if the image is already cached. 01218 QString key = d->makeCacheKey(name, group, overlays, size, state); 01219 QPixmap pix; 01220 bool iconWasUnknown = false; 01221 K3Icon icon; 01222 01223 // icon.path would be empty for "unknown" icons, which should be searched for 01224 // anew each time. 01225 if (d->findCachedPixmapWithPath(key, pix, icon.path) && !icon.path.isEmpty()) { 01226 if (path_store) { 01227 *path_store = icon.path; 01228 } 01229 01230 return pix; 01231 } 01232 01233 // Image is not cached... go find it and apply effects. 01234 if (!d->initIconThemes()) { 01235 return QPixmap(); 01236 } 01237 01238 favIconOverlay = favIconOverlay && size > 22; 01239 01240 // First we look for non-User icons. If we don't find one we'd search in 01241 // the User space anyways... 01242 if (group != KIconLoader::User) { 01243 // K3Icon seems to hold some needed information. 01244 01245 if (absolutePath && !favIconOverlay) 01246 { 01247 icon.context = KIconLoader::Any; 01248 icon.type = KIconLoader::Scalable; 01249 icon.path = name; 01250 } 01251 else 01252 { 01253 icon = d->findMatchingIconWithGenericFallbacks(favIconOverlay ? QString("text-html") : name, size); 01254 } 01255 } 01256 01257 if (icon.path.isEmpty()) { 01258 // We do have a "User" icon, or we couldn't find the non-User one. 01259 icon.path = (absolutePath) ? name : 01260 iconPath(name, KIconLoader::User, canReturnNull); 01261 } 01262 01263 // Still can't find it? Use "unknown" if we can't return null. 01264 // We keep going in the function so we can ensure this result gets cached. 01265 if (icon.path.isEmpty() && !canReturnNull) { 01266 icon.path = d->unknownIconPath(size); 01267 iconWasUnknown = true; 01268 } 01269 01270 QImage img = d->createIconImage(icon.path, size); 01271 01272 if (group >= 0) 01273 { 01274 img = d->mpEffect.apply(img, group, state); 01275 } 01276 01277 if (favIconOverlay) 01278 { 01279 QImage favIcon(name, "PNG"); 01280 if (!favIcon.isNull()) // if favIcon not there yet, don't try to blend it 01281 { 01282 QPainter p(&img); 01283 01284 // Align the favicon overlay 01285 QRect r(favIcon.rect()); 01286 r.moveBottomRight(img.rect().bottomRight()); 01287 r.adjust(-1, -1, -1, -1); // Move off edge 01288 01289 // Blend favIcon over img. 01290 p.drawImage(r, favIcon); 01291 } 01292 } 01293 01294 pix = QPixmap::fromImage(img); 01295 01296 // TODO: If we make a loadIcon that returns the image we can convert 01297 // drawOverlays to use the image instead of pixmaps as well so we don't 01298 // have to transfer so much to the graphics card. 01299 d->drawOverlays(this, group, state, pix, overlays); 01300 01301 // Don't add the path to our unknown icon to the cache, only cache the 01302 // actual image. 01303 if (iconWasUnknown) { 01304 icon.path.clear(); 01305 } 01306 01307 d->insertCachedPixmapWithPath(key, pix, icon.path); 01308 01309 if (path_store) { 01310 *path_store = icon.path; 01311 } 01312 01313 return pix; 01314 } 01315 01316 QMovie *KIconLoader::loadMovie(const QString& name, KIconLoader::Group group, int size, QObject *parent) const 01317 { 01318 QString file = moviePath( name, group, size ); 01319 if (file.isEmpty()) 01320 return 0; 01321 int dirLen = file.lastIndexOf('/'); 01322 QString icon = iconPath(name, size ? -size : group, true); 01323 if (!icon.isEmpty() && file.left(dirLen) != icon.left(dirLen)) 01324 return 0; 01325 QMovie *movie = new QMovie(file, QByteArray(), parent); 01326 if (!movie->isValid()) 01327 { 01328 delete movie; 01329 return 0; 01330 } 01331 return movie; 01332 } 01333 01334 QString KIconLoader::moviePath(const QString& name, KIconLoader::Group group, int size) const 01335 { 01336 if (!d->mpGroups) return QString(); 01337 01338 d->initIconThemes(); 01339 01340 if ( (group < -1 || group >= KIconLoader::LastGroup) && group != KIconLoader::User ) 01341 { 01342 kDebug(264) << "Illegal icon group: " << group; 01343 group = KIconLoader::Desktop; 01344 } 01345 if (size == 0 && group < 0) 01346 { 01347 kDebug(264) << "Neither size nor group specified!"; 01348 group = KIconLoader::Desktop; 01349 } 01350 01351 QString file = name + ".mng"; 01352 if (group == KIconLoader::User) 01353 { 01354 file = d->mpDirs->findResource("appicon", file); 01355 } 01356 else 01357 { 01358 if (size == 0) 01359 size = d->mpGroups[group].size; 01360 01361 K3Icon icon; 01362 01363 foreach(KIconThemeNode *themeNode, d->links) 01364 { 01365 icon = themeNode->theme->iconPath(file, size, KIconLoader::MatchExact); 01366 if (icon.isValid()) 01367 break; 01368 } 01369 01370 if ( !icon.isValid() ) 01371 { 01372 foreach(KIconThemeNode *themeNode, d->links) 01373 { 01374 icon = themeNode->theme->iconPath(file, size, KIconLoader::MatchBest); 01375 if (icon.isValid()) 01376 break; 01377 } 01378 } 01379 01380 file = icon.isValid() ? icon.path : QString(); 01381 } 01382 return file; 01383 } 01384 01385 01386 QStringList KIconLoader::loadAnimated(const QString& name, KIconLoader::Group group, int size) const 01387 { 01388 QStringList lst; 01389 01390 if (!d->mpGroups) return lst; 01391 01392 d->initIconThemes(); 01393 01394 if ((group < -1) || (group >= KIconLoader::LastGroup)) 01395 { 01396 kDebug(264) << "Illegal icon group: " << group; 01397 group = KIconLoader::Desktop; 01398 } 01399 if ((size == 0) && (group < 0)) 01400 { 01401 kDebug(264) << "Neither size nor group specified!"; 01402 group = KIconLoader::Desktop; 01403 } 01404 01405 QString file = name + "/0001"; 01406 if (group == KIconLoader::User) 01407 { 01408 file = d->mpDirs->findResource("appicon", file + ".png"); 01409 } else 01410 { 01411 if (size == 0) 01412 size = d->mpGroups[group].size; 01413 K3Icon icon = d->findMatchingIcon(file, size); 01414 file = icon.isValid() ? icon.path : QString(); 01415 01416 } 01417 if (file.isEmpty()) 01418 return lst; 01419 01420 QString path = file.left(file.length()-8); 01421 DIR* dp = opendir( QFile::encodeName(path) ); 01422 if(!dp) 01423 return lst; 01424 01425 KDE_struct_dirent* ep; 01426 while( ( ep = KDE_readdir( dp ) ) != 0L ) 01427 { 01428 QString fn(QFile::decodeName(ep->d_name)); 01429 if(!(fn.left(4)).toUInt()) 01430 continue; 01431 01432 lst += path + fn; 01433 } 01434 closedir ( dp ); 01435 lst.sort(); 01436 return lst; 01437 } 01438 01439 KIconTheme *KIconLoader::theme() const 01440 { 01441 d->initIconThemes(); 01442 if (d->mpThemeRoot) return d->mpThemeRoot->theme; 01443 return 0L; 01444 } 01445 01446 int KIconLoader::currentSize(KIconLoader::Group group) const 01447 { 01448 if (!d->mpGroups) return -1; 01449 01450 if (group < 0 || group >= KIconLoader::LastGroup) 01451 { 01452 kDebug(264) << "Illegal icon group: " << group; 01453 return -1; 01454 } 01455 return d->mpGroups[group].size; 01456 } 01457 01458 QStringList KIconLoader::queryIconsByDir( const QString& iconsDir ) const 01459 { 01460 const QDir dir(iconsDir); 01461 const QStringList formats = QStringList() << "*.png" << "*.xpm" << "*.svg" << "*.svgz"; 01462 const QStringList lst = dir.entryList(formats, QDir::Files); 01463 QStringList result; 01464 QStringList::ConstIterator it; 01465 for (it=lst.begin(); it!=lst.end(); ++it) 01466 result += iconsDir + '/' + *it; 01467 return result; 01468 } 01469 01470 QStringList KIconLoader::queryIconsByContext(int group_or_size, 01471 KIconLoader::Context context) const 01472 { 01473 d->initIconThemes(); 01474 01475 QStringList result; 01476 if (group_or_size >= KIconLoader::LastGroup) 01477 { 01478 kDebug(264) << "Illegal icon group: " << group_or_size; 01479 return result; 01480 } 01481 int size; 01482 if (group_or_size >= 0) 01483 size = d->mpGroups[group_or_size].size; 01484 else 01485 size = -group_or_size; 01486 01487 foreach(KIconThemeNode *themeNode, d->links) 01488 themeNode->queryIconsByContext(&result, size, context); 01489 01490 // Eliminate duplicate entries (same icon in different directories) 01491 QString name; 01492 QStringList res2, entries; 01493 QStringList::ConstIterator it; 01494 for (it=result.constBegin(); it!=result.constEnd(); ++it) 01495 { 01496 int n = (*it).lastIndexOf('/'); 01497 if (n == -1) 01498 name = *it; 01499 else 01500 name = (*it).mid(n+1); 01501 name = d->removeIconExtension(name); 01502 if (!entries.contains(name)) 01503 { 01504 entries += name; 01505 res2 += *it; 01506 } 01507 } 01508 return res2; 01509 01510 } 01511 01512 QStringList KIconLoader::queryIcons(int group_or_size, KIconLoader::Context context) const 01513 { 01514 d->initIconThemes(); 01515 01516 QStringList result; 01517 if (group_or_size >= KIconLoader::LastGroup) 01518 { 01519 kDebug(264) << "Illegal icon group: " << group_or_size; 01520 return result; 01521 } 01522 int size; 01523 if (group_or_size >= 0) 01524 size = d->mpGroups[group_or_size].size; 01525 else 01526 size = -group_or_size; 01527 01528 foreach(KIconThemeNode *themeNode, d->links) 01529 themeNode->queryIcons(&result, size, context); 01530 01531 // Eliminate duplicate entries (same icon in different directories) 01532 QString name; 01533 QStringList res2, entries; 01534 QStringList::ConstIterator it; 01535 for (it=result.constBegin(); it!=result.constEnd(); ++it) 01536 { 01537 int n = (*it).lastIndexOf('/'); 01538 if (n == -1) 01539 name = *it; 01540 else 01541 name = (*it).mid(n+1); 01542 name = d->removeIconExtension(name); 01543 if (!entries.contains(name)) 01544 { 01545 entries += name; 01546 res2 += *it; 01547 } 01548 } 01549 return res2; 01550 } 01551 01552 // used by KIconDialog to find out which contexts to offer in a combobox 01553 bool KIconLoader::hasContext(KIconLoader::Context context) const 01554 { 01555 d->initIconThemes(); 01556 01557 foreach(KIconThemeNode *themeNode, d->links) 01558 if( themeNode->theme->hasContext( context )) 01559 return true; 01560 return false; 01561 } 01562 01563 KIconEffect * KIconLoader::iconEffect() const 01564 { 01565 return &d->mpEffect; 01566 } 01567 01568 bool KIconLoader::alphaBlending(KIconLoader::Group group) const 01569 { 01570 if (!d->mpGroups) return false; 01571 01572 if (group < 0 || group >= KIconLoader::LastGroup) 01573 { 01574 kDebug(264) << "Illegal icon group: " << group; 01575 return false; 01576 } 01577 return d->mpGroups[group].alphaBlending; 01578 } 01579 01580 // deprecated 01581 #ifndef KDE_NO_DEPRECATED 01582 QIcon KIconLoader::loadIconSet( const QString& name, KIconLoader::Group g, int s, 01583 bool canReturnNull ) 01584 { 01585 QIcon iconset; 01586 QPixmap tmp = loadIcon(name, g, s, KIconLoader::ActiveState, QStringList(), NULL, canReturnNull); 01587 iconset.addPixmap( tmp, QIcon::Active, QIcon::On ); 01588 // we don't use QIconSet's resizing anyway 01589 tmp = loadIcon(name, g, s, KIconLoader::DisabledState, QStringList(), NULL, canReturnNull); 01590 iconset.addPixmap( tmp, QIcon::Disabled, QIcon::On ); 01591 tmp = loadIcon(name, g, s, KIconLoader::DefaultState, QStringList(), NULL, canReturnNull); 01592 iconset.addPixmap( tmp, QIcon::Normal, QIcon::On ); 01593 return iconset; 01594 } 01595 #endif 01596 01597 // Easy access functions 01598 01599 QPixmap DesktopIcon(const QString& name, int force_size, int state, const QStringList &overlays) 01600 { 01601 KIconLoader *loader = KIconLoader::global(); 01602 return loader->loadIcon(name, KIconLoader::Desktop, force_size, state, overlays); 01603 } 01604 01605 // deprecated 01606 #ifndef KDE_NO_DEPRECATED 01607 QIcon DesktopIconSet(const QString& name, int force_size) 01608 { 01609 KIconLoader *loader = KIconLoader::global(); 01610 return loader->loadIconSet(name, KIconLoader::Desktop, force_size); 01611 } 01612 #endif 01613 01614 QPixmap BarIcon(const QString& name, int force_size, int state, const QStringList &overlays) 01615 { 01616 KIconLoader *loader = KIconLoader::global(); 01617 return loader->loadIcon(name, KIconLoader::Toolbar, force_size, state, overlays); 01618 } 01619 01620 // deprecated 01621 #ifndef KDE_NO_DEPRECATED 01622 QIcon BarIconSet(const QString& name, int force_size) 01623 { 01624 KIconLoader *loader = KIconLoader::global(); 01625 return loader->loadIconSet( name, KIconLoader::Toolbar, force_size ); 01626 } 01627 #endif 01628 01629 QPixmap SmallIcon(const QString& name, int force_size, int state, const QStringList &overlays) 01630 { 01631 KIconLoader *loader = KIconLoader::global(); 01632 return loader->loadIcon(name, KIconLoader::Small, force_size, state, overlays); 01633 } 01634 01635 // deprecated 01636 #ifndef KDE_NO_DEPRECATED 01637 QIcon SmallIconSet(const QString& name, int force_size) 01638 { 01639 KIconLoader *loader = KIconLoader::global(); 01640 return loader->loadIconSet( name, KIconLoader::Small, force_size ); 01641 } 01642 #endif 01643 01644 QPixmap MainBarIcon(const QString& name, int force_size, int state, const QStringList &overlays) 01645 { 01646 KIconLoader *loader = KIconLoader::global(); 01647 return loader->loadIcon(name, KIconLoader::MainToolbar, force_size, state, overlays); 01648 } 01649 01650 // deprecated 01651 #ifndef KDE_NO_DEPRECATED 01652 QIcon MainBarIconSet(const QString& name, int force_size) 01653 { 01654 KIconLoader *loader = KIconLoader::global(); 01655 return loader->loadIconSet( name, KIconLoader::MainToolbar, force_size ); 01656 } 01657 #endif 01658 01659 QPixmap UserIcon(const QString& name, int state, const QStringList &overlays) 01660 { 01661 KIconLoader *loader = KIconLoader::global(); 01662 return loader->loadIcon(name, KIconLoader::User, 0, state, overlays); 01663 } 01664 01665 // deprecated 01666 #ifndef KDE_NO_DEPRECATED 01667 QIcon UserIconSet(const QString& name) 01668 { 01669 KIconLoader *loader = KIconLoader::global(); 01670 return loader->loadIconSet( name, KIconLoader::User ); 01671 } 01672 #endif 01673 01674 int IconSize(KIconLoader::Group group) 01675 { 01676 KIconLoader *loader = KIconLoader::global(); 01677 return loader->currentSize(group); 01678 } 01679 01680 QPixmap KIconLoader::unknown() 01681 { 01682 QPixmap pix; 01683 if ( QPixmapCache::find("unknown", pix) ) //krazy:exclude=iconnames 01684 return pix; 01685 01686 QString path = global()->iconPath("unknown", KIconLoader::Small, true); //krazy:exclude=iconnames 01687 if (path.isEmpty()) 01688 { 01689 kDebug(264) << "Warning: Cannot find \"unknown\" icon."; 01690 pix = QPixmap(32,32); 01691 } else 01692 { 01693 pix.load(path); 01694 QPixmapCache::insert("unknown", pix); //krazy:exclude=iconnames 01695 } 01696 01697 return pix; 01698 } 01699 01700 /*** the global icon loader ***/ 01701 K_GLOBAL_STATIC_WITH_ARGS(KIconLoader, globalIconLoader, (KGlobal::mainComponent(), 0)) 01702 01703 KIconLoader *KIconLoader::global() 01704 { 01705 return globalIconLoader; 01706 } 01707 01708 void KIconLoader::newIconLoader() 01709 { 01710 if ( global() == this) { 01711 KIconTheme::reconfigure(); 01712 } 01713 01714 reconfigure( objectName(), d->mpDirs ); 01715 emit iconLoaderSettingsChanged(); 01716 } 01717 01718 #include "kiconloader.moc" 01719
This file is part of the KDE documentation.
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:32:40 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:32:40 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.