File indexing completed on 2025-03-09 03:40:24
0001 /* vi: ts=8 sts=4 sw=4 0002 0003 kicontheme.cpp: Lowlevel icon theme handling. 0004 0005 This file is part of the KDE project, module kdecore. 0006 SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org> 0007 SPDX-FileCopyrightText: 2000 Antonio Larrosa <larrosa@kde.org> 0008 0009 SPDX-License-Identifier: LGPL-2.0-only 0010 */ 0011 0012 #include "kicontheme.h" 0013 0014 #include "debug.h" 0015 0016 #include <KConfigGroup> 0017 #include <KLocalizedString> // KLocalizedString::localizedFilePath. Need such functionality in, hmm, QLocale? QStandardPaths? 0018 #include <KSharedConfig> 0019 0020 #ifdef WITH_BREEZEICONS_LIB 0021 #include <BreezeIcons> 0022 #endif 0023 0024 #include <QAction> 0025 #include <QCoreApplication> 0026 #include <QDebug> 0027 #include <QDir> 0028 #include <QFileInfo> 0029 #include <QMap> 0030 #include <QResource> 0031 #include <QSet> 0032 0033 #include <private/qguiapplication_p.h> 0034 #include <qpa/qplatformtheme.h> 0035 0036 #include <qplatformdefs.h> 0037 0038 #include <array> 0039 #include <cmath> 0040 0041 Q_GLOBAL_STATIC(QString, _themeOverride) 0042 0043 // Support for icon themes in RCC files. 0044 // The intended use case is standalone apps on Windows / MacOS / etc. 0045 // For this reason we use AppDataLocation: BINDIR/data on Windows, Resources on OS X 0046 void initRCCIconTheme() 0047 { 0048 #ifdef WITH_BREEZEICONS 0049 BreezeIcons::initIcons() 0050 #else 0051 const QString iconThemeRcc = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("icontheme.rcc")); 0052 if (!iconThemeRcc.isEmpty()) { 0053 const QString iconThemeName = QStringLiteral("kf6_rcc_theme"); 0054 const QString iconSubdir = QStringLiteral("/icons/") + iconThemeName; 0055 if (QResource::registerResource(iconThemeRcc, iconSubdir)) { 0056 if (QFileInfo::exists(QLatin1Char(':') + iconSubdir + QStringLiteral("/index.theme"))) { 0057 // Tell Qt about the theme 0058 // Note that since qtbase commit a8621a3f8, this means the QPA (i.e. KIconLoader) will NOT be used. 0059 QIcon::setThemeName(iconThemeName); // Qt looks under :/icons automatically 0060 // Tell KIconTheme about the theme, in case KIconLoader is used directly 0061 *_themeOverride() = iconThemeName; 0062 } else { 0063 qWarning() << "No index.theme found in" << iconThemeRcc; 0064 QResource::unregisterResource(iconThemeRcc, iconSubdir); 0065 } 0066 } else { 0067 qWarning() << "Invalid rcc file" << iconThemeRcc; 0068 } 0069 } 0070 #endif 0071 } 0072 Q_COREAPP_STARTUP_FUNCTION(initRCCIconTheme) 0073 0074 // Makes sure the icon theme fallback is set to breeze or one of its 0075 // variants. Most of our apps use "lots" of icons that most of the times 0076 // are only available with breeze, we still honour the user icon theme 0077 // but if the icon is not found there, we go to breeze since it's almost 0078 // sure it'll be there 0079 static void setBreezeFallback() 0080 { 0081 if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) { 0082 const QVariant themeHint = theme->themeHint(QPlatformTheme::SystemIconFallbackThemeName); 0083 if (themeHint.isValid()) { 0084 const QString iconTheme = themeHint.toString(); 0085 if (iconTheme.contains(QStringLiteral("breeze"), Qt::CaseInsensitive)) { 0086 QIcon::setFallbackThemeName(iconTheme); 0087 return; 0088 } 0089 } 0090 } 0091 0092 QIcon::setFallbackThemeName(QStringLiteral("breeze")); 0093 } 0094 0095 #ifndef Q_OS_ANDROID 0096 Q_COREAPP_STARTUP_FUNCTION(setBreezeFallback) 0097 #endif 0098 class KIconThemeDir; 0099 class KIconThemePrivate 0100 { 0101 public: 0102 QString example, screenshot; 0103 bool hidden; 0104 KSharedConfig::Ptr sharedConfig; 0105 0106 struct GroupInfo { 0107 KIconLoader::Group type; 0108 const char *name; 0109 int defaultSize; 0110 QList<int> availableSizes{}; 0111 }; 0112 std::array<GroupInfo, KIconLoader::LastGroup> m_iconGroups = {{ 0113 {KIconLoader::Desktop, "Desktop", 32}, 0114 {KIconLoader::Toolbar, "Toolbar", 22}, 0115 {KIconLoader::MainToolbar, "MainToolbar", 22}, 0116 {KIconLoader::Small, "Small", 16}, 0117 {KIconLoader::Panel, "Panel", 48}, 0118 {KIconLoader::Dialog, "Dialog", 32}, 0119 }}; 0120 0121 int mDepth; 0122 QString mDir, mName, mInternalName, mDesc; 0123 QStringList mInherits; 0124 QStringList mExtensions; 0125 QList<KIconThemeDir *> mDirs; 0126 QList<KIconThemeDir *> mScaledDirs; 0127 bool followsColorScheme : 1; 0128 0129 /// Searches the given dirs vector for a matching icon 0130 QString iconPath(const QList<KIconThemeDir *> &dirs, const QString &name, int size, qreal scale, KIconLoader::MatchType match) const; 0131 }; 0132 Q_GLOBAL_STATIC(QString, _theme) 0133 Q_GLOBAL_STATIC(QStringList, _theme_list) 0134 0135 /** 0136 * A subdirectory in an icon theme. 0137 */ 0138 class KIconThemeDir 0139 { 0140 public: 0141 KIconThemeDir(const QString &basedir, const QString &themedir, const KConfigGroup &config); 0142 0143 bool isValid() const 0144 { 0145 return mbValid; 0146 } 0147 QString iconPath(const QString &name) const; 0148 QStringList iconList() const; 0149 QString constructFileName(const QString &file) const 0150 { 0151 return mBaseDir + mThemeDir + QLatin1Char('/') + file; 0152 } 0153 0154 KIconLoader::Context context() const 0155 { 0156 return mContext; 0157 } 0158 KIconLoader::Type type() const 0159 { 0160 return mType; 0161 } 0162 int size() const 0163 { 0164 return mSize; 0165 } 0166 int scale() const 0167 { 0168 return mScale; 0169 } 0170 int minSize() const 0171 { 0172 return mMinSize; 0173 } 0174 int maxSize() const 0175 { 0176 return mMaxSize; 0177 } 0178 int threshold() const 0179 { 0180 return mThreshold; 0181 } 0182 0183 private: 0184 bool mbValid = false; 0185 KIconLoader::Type mType = KIconLoader::Fixed; 0186 KIconLoader::Context mContext; 0187 int mSize = 0; 0188 int mScale = 1; 0189 int mMinSize = 1; 0190 int mMaxSize = 50; 0191 int mThreshold = 2; 0192 0193 const QString mBaseDir; 0194 const QString mThemeDir; 0195 }; 0196 0197 QString KIconThemePrivate::iconPath(const QList<KIconThemeDir *> &dirs, const QString &name, int size, qreal scale, KIconLoader::MatchType match) const 0198 { 0199 QString path; 0200 QString tempPath; // used to cache icon path if it exists 0201 0202 int delta = -INT_MAX; // current icon size delta of 'icon' 0203 int dw = INT_MAX; // icon size delta of current directory 0204 0205 // Rather downsample than upsample 0206 int integerScale = std::ceil(scale); 0207 0208 // Search the directory that contains the icon which matches best to the requested 0209 // size. If there is no directory which matches exactly to the requested size, the 0210 // following criteria get applied: 0211 // - Take a directory having icons with a minimum difference to the requested size. 0212 // - Prefer directories that allow a downscaling even if the difference to 0213 // the requested size is bigger than a directory where an upscaling is required. 0214 for (KIconThemeDir *dir : dirs) { 0215 if (dir->scale() != integerScale) { 0216 continue; 0217 } 0218 0219 if (match == KIconLoader::MatchExact) { 0220 if ((dir->type() == KIconLoader::Fixed) && (dir->size() != size)) { 0221 continue; 0222 } 0223 if ((dir->type() == KIconLoader::Scalable) // 0224 && ((size < dir->minSize()) || (size > dir->maxSize()))) { 0225 continue; 0226 } 0227 if ((dir->type() == KIconLoader::Threshold) // 0228 && (abs(dir->size() - size) > dir->threshold())) { 0229 continue; 0230 } 0231 } else { 0232 // dw < 0 means need to scale up to get an icon of the requested size. 0233 // Upscaling should only be done if no larger icon is available. 0234 if (dir->type() == KIconLoader::Fixed) { 0235 dw = dir->size() - size; 0236 } else if (dir->type() == KIconLoader::Scalable) { 0237 if (size < dir->minSize()) { 0238 dw = dir->minSize() - size; 0239 } else if (size > dir->maxSize()) { 0240 dw = dir->maxSize() - size; 0241 } else { 0242 dw = 0; 0243 } 0244 } else if (dir->type() == KIconLoader::Threshold) { 0245 if (size < dir->size() - dir->threshold()) { 0246 dw = dir->size() - dir->threshold() - size; 0247 } else if (size > dir->size() + dir->threshold()) { 0248 dw = dir->size() + dir->threshold() - size; 0249 } else { 0250 dw = 0; 0251 } 0252 } 0253 // Usually if the delta (= 'dw') of the current directory is 0254 // not smaller than the delta (= 'delta') of the currently best 0255 // matching icon, this candidate can be skipped. But skipping 0256 // the candidate may only be done, if this does not imply 0257 // in an upscaling of the icon (it is OK to use a directory with 0258 // smaller icons that what we've already found, however). 0259 if ((abs(dw) >= abs(delta)) && ((dw < 0) || (delta > 0))) { 0260 continue; 0261 } 0262 0263 if (match == KIconLoader::MatchBestOrGreaterSize && dw < 0) { 0264 continue; 0265 } 0266 } 0267 0268 // cache the result of iconPath() call which checks if file exists 0269 tempPath = dir->iconPath(name); 0270 0271 if (tempPath.isEmpty()) { 0272 continue; 0273 } 0274 0275 path = tempPath; 0276 0277 // if we got in MatchExact that far, we find no better 0278 if (match == KIconLoader::MatchExact) { 0279 return path; 0280 } 0281 delta = dw; 0282 if (delta == 0) { 0283 return path; // We won't find a better match anyway 0284 } 0285 } 0286 return path; 0287 } 0288 0289 KIconTheme::KIconTheme(const QString &name, const QString &appName, const QString &basePathHint) 0290 : d(new KIconThemePrivate) 0291 { 0292 d->mInternalName = name; 0293 0294 QStringList themeDirs; 0295 0296 // Applications can have local additions to the global "locolor" and 0297 // "hicolor" icon themes. For these, the _global_ theme description 0298 // files are used.. 0299 0300 /* clang-format off */ 0301 if (!appName.isEmpty() 0302 && (name == defaultThemeName() 0303 || name == QLatin1String("hicolor") 0304 || name == QLatin1String("locolor"))) { /* clang-format on */ 0305 const QString suffix = QLatin1Char('/') + appName + QLatin1String("/icons/") + name + QLatin1Char('/'); 0306 QStringList dataDirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); 0307 for (auto &cDir : dataDirs) { 0308 cDir += suffix; 0309 if (QFileInfo::exists(cDir)) { 0310 themeDirs += cDir; 0311 } 0312 } 0313 0314 if (!basePathHint.isEmpty()) { 0315 // Checks for dir existing are done below 0316 themeDirs += basePathHint + QLatin1Char('/') + name + QLatin1Char('/'); 0317 } 0318 } 0319 0320 // Find the theme description file. These are either locally in the :/icons resource path or global. 0321 QStringList icnlibs; 0322 0323 // local embedded icons have preference 0324 icnlibs << QStringLiteral(":/icons"); 0325 0326 // global icons 0327 icnlibs += QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("icons"), QStandardPaths::LocateDirectory); 0328 0329 // These are not in the icon spec, but e.g. GNOME puts some icons there anyway. 0330 icnlibs += QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("pixmaps"), QStandardPaths::LocateDirectory); 0331 0332 QString fileName; 0333 QString mainSection; 0334 const QString pathSuffix = QLatin1Char('/') + name + QLatin1Char('/'); 0335 const QLatin1String indexTheme("index.theme"); 0336 const QLatin1String indexDesktop("theme.desktop"); 0337 for (auto &iconDir : icnlibs) { 0338 iconDir += pathSuffix; 0339 const QFileInfo fi(iconDir); 0340 if (!fi.exists() || !fi.isDir()) { 0341 continue; 0342 } 0343 themeDirs.append(iconDir); 0344 0345 if (d->mDir.isEmpty()) { 0346 QString possiblePath; 0347 if (possiblePath = iconDir + indexTheme; QFileInfo::exists(possiblePath)) { 0348 d->mDir = iconDir; 0349 fileName = possiblePath; 0350 mainSection = QStringLiteral("Icon Theme"); 0351 } else if (possiblePath = iconDir + indexDesktop; QFileInfo::exists(possiblePath)) { 0352 d->mDir = iconDir; 0353 fileName = possiblePath; 0354 mainSection = QStringLiteral("KDE Icon Theme"); 0355 } 0356 } 0357 } 0358 0359 if (d->mDir.isEmpty()) { 0360 qCDebug(KICONTHEMES) << "Icon theme" << name << "not found."; 0361 return; 0362 } 0363 0364 // Use KSharedConfig to avoid parsing the file many times, from each component. 0365 // Need to keep a ref to it to make this useful 0366 d->sharedConfig = KSharedConfig::openConfig(fileName, KConfig::NoGlobals); 0367 0368 KConfigGroup cfg(d->sharedConfig, mainSection); 0369 d->mName = cfg.readEntry("Name"); 0370 d->mDesc = cfg.readEntry("Comment"); 0371 d->mDepth = cfg.readEntry("DisplayDepth", 32); 0372 d->mInherits = cfg.readEntry("Inherits", QStringList()); 0373 if (name != defaultThemeName()) { 0374 for (auto &inheritedTheme : d->mInherits) { 0375 if (inheritedTheme == QLatin1String("default")) { 0376 inheritedTheme = defaultThemeName(); 0377 } 0378 } 0379 } 0380 0381 d->hidden = cfg.readEntry("Hidden", false); 0382 d->followsColorScheme = cfg.readEntry("FollowsColorScheme", false); 0383 d->example = cfg.readPathEntry("Example", QString()); 0384 d->screenshot = cfg.readPathEntry("ScreenShot", QString()); 0385 d->mExtensions = 0386 cfg.readEntry("KDE-Extensions", QStringList{QStringLiteral(".png"), QStringLiteral(".svgz"), QStringLiteral(".svg"), QStringLiteral(".xpm")}); 0387 0388 QSet<QString> addedDirs; // Used for avoiding duplicates. 0389 const QStringList dirs = cfg.readPathEntry("Directories", QStringList()) + cfg.readPathEntry("ScaledDirectories", QStringList()); 0390 for (const auto &dirName : dirs) { 0391 KConfigGroup cg(d->sharedConfig, dirName); 0392 for (const auto &themeDir : std::as_const(themeDirs)) { 0393 const QString currentDir(themeDir + dirName + QLatin1Char('/')); 0394 if (!addedDirs.contains(currentDir) && QDir(currentDir).exists()) { 0395 addedDirs.insert(currentDir); 0396 KIconThemeDir *dir = new KIconThemeDir(themeDir, dirName, cg); 0397 if (dir->isValid()) { 0398 if (dir->scale() > 1) { 0399 d->mScaledDirs.append(dir); 0400 } else { 0401 d->mDirs.append(dir); 0402 } 0403 } else { 0404 delete dir; 0405 } 0406 } 0407 } 0408 } 0409 0410 KConfigGroup cg(d->sharedConfig, mainSection); 0411 for (auto &iconGroup : d->m_iconGroups) { 0412 iconGroup.defaultSize = cg.readEntry(iconGroup.name + QLatin1String("Default"), iconGroup.defaultSize); 0413 iconGroup.availableSizes = cg.readEntry(iconGroup.name + QLatin1String("Sizes"), QList<int>()); 0414 } 0415 } 0416 0417 KIconTheme::~KIconTheme() 0418 { 0419 qDeleteAll(d->mDirs); 0420 qDeleteAll(d->mScaledDirs); 0421 } 0422 0423 QString KIconTheme::name() const 0424 { 0425 return d->mName; 0426 } 0427 0428 QString KIconTheme::internalName() const 0429 { 0430 return d->mInternalName; 0431 } 0432 0433 QString KIconTheme::description() const 0434 { 0435 return d->mDesc; 0436 } 0437 0438 QString KIconTheme::example() const 0439 { 0440 return d->example; 0441 } 0442 0443 QString KIconTheme::screenshot() const 0444 { 0445 return d->screenshot; 0446 } 0447 0448 QString KIconTheme::dir() const 0449 { 0450 return d->mDir; 0451 } 0452 0453 QStringList KIconTheme::inherits() const 0454 { 0455 return d->mInherits; 0456 } 0457 0458 bool KIconTheme::isValid() const 0459 { 0460 return !d->mDirs.isEmpty() || !d->mScaledDirs.isEmpty(); 0461 } 0462 0463 bool KIconTheme::isHidden() const 0464 { 0465 return d->hidden; 0466 } 0467 0468 int KIconTheme::depth() const 0469 { 0470 return d->mDepth; 0471 } 0472 0473 int KIconTheme::defaultSize(KIconLoader::Group group) const 0474 { 0475 if (group < 0 || group >= KIconLoader::LastGroup) { 0476 qCWarning(KICONTHEMES) << "Invalid icon group:" << group << ", should be one of KIconLoader::Group"; 0477 return -1; 0478 } 0479 return d->m_iconGroups[group].defaultSize; 0480 } 0481 0482 QList<int> KIconTheme::querySizes(KIconLoader::Group group) const 0483 { 0484 if (group < 0 || group >= KIconLoader::LastGroup) { 0485 qCWarning(KICONTHEMES) << "Invalid icon group:" << group << ", should be one of KIconLoader::Group"; 0486 return QList<int>(); 0487 } 0488 return d->m_iconGroups[group].availableSizes; 0489 } 0490 0491 static bool isAnyOrDirContext(const KIconThemeDir *dir, KIconLoader::Context context) 0492 { 0493 return context == KIconLoader::Any || context == dir->context(); 0494 } 0495 0496 QStringList KIconTheme::queryIcons(int size, KIconLoader::Context context) const 0497 { 0498 // Try to find exact match 0499 QStringList result; 0500 const QList<KIconThemeDir *> listDirs = d->mDirs + d->mScaledDirs; 0501 for (const KIconThemeDir *dir : listDirs) { 0502 if (!isAnyOrDirContext(dir, context)) { 0503 continue; 0504 } 0505 0506 const int dirSize = dir->size(); 0507 if ((dir->type() == KIconLoader::Fixed && dirSize == size) // 0508 || (dir->type() == KIconLoader::Scalable && size >= dir->minSize() && size <= dir->maxSize()) 0509 || (dir->type() == KIconLoader::Threshold && abs(size - dirSize) < dir->threshold())) { 0510 result += dir->iconList(); 0511 } 0512 } 0513 0514 return result; 0515 } 0516 0517 QStringList KIconTheme::queryIconsByContext(int size, KIconLoader::Context context) const 0518 { 0519 int dw; 0520 0521 // We want all the icons for a given context, but we prefer icons 0522 // of size "size" . Note that this may (will) include duplicate icons 0523 // QStringList iconlist[34]; // 33 == 48-16+1 0524 QStringList iconlist[128]; // 33 == 48-16+1 0525 // Usually, only the 0, 6 (22-16), 10 (32-22), 16 (48-32 or 32-16), 0526 // 26 (48-22) and 32 (48-16) will be used, but who knows if someone 0527 // will make icon themes with different icon sizes. 0528 const auto listDirs = d->mDirs + d->mScaledDirs; 0529 for (KIconThemeDir *dir : listDirs) { 0530 if (!isAnyOrDirContext(dir, context)) { 0531 continue; 0532 } 0533 dw = abs(dir->size() - size); 0534 iconlist[(dw < 127) ? dw : 127] += dir->iconList(); 0535 } 0536 0537 QStringList iconlistResult; 0538 for (int i = 0; i < 128; i++) { 0539 iconlistResult += iconlist[i]; 0540 } 0541 0542 return iconlistResult; 0543 } 0544 0545 bool KIconTheme::hasContext(KIconLoader::Context context) const 0546 { 0547 const auto listDirs = d->mDirs + d->mScaledDirs; 0548 for (KIconThemeDir *dir : listDirs) { 0549 if (isAnyOrDirContext(dir, context)) { 0550 return true; 0551 } 0552 } 0553 return false; 0554 } 0555 0556 QString KIconTheme::iconPathByName(const QString &iconName, int size, KIconLoader::MatchType match) const 0557 { 0558 return iconPathByName(iconName, size, match, 1 /*scale*/); 0559 } 0560 0561 QString KIconTheme::iconPathByName(const QString &iconName, int size, KIconLoader::MatchType match, qreal scale) const 0562 { 0563 for (const QString ¤t : std::as_const(d->mExtensions)) { 0564 const QString path = iconPath(iconName + current, size, match, scale); 0565 if (!path.isEmpty()) { 0566 return path; 0567 } 0568 } 0569 return QString(); 0570 } 0571 0572 bool KIconTheme::followsColorScheme() const 0573 { 0574 return d->followsColorScheme; 0575 } 0576 0577 QString KIconTheme::iconPath(const QString &name, int size, KIconLoader::MatchType match) const 0578 { 0579 return iconPath(name, size, match, 1 /*scale*/); 0580 } 0581 0582 QString KIconTheme::iconPath(const QString &name, int size, KIconLoader::MatchType match, qreal scale) const 0583 { 0584 // first look for a scaled image at exactly the requested size 0585 QString path = d->iconPath(d->mScaledDirs, name, size, scale, KIconLoader::MatchExact); 0586 0587 // then look for an unscaled one but request it at larger size so it doesn't become blurry 0588 if (path.isEmpty()) { 0589 path = d->iconPath(d->mDirs, name, size * scale, 1, match); 0590 } 0591 return path; 0592 } 0593 0594 // static 0595 QString KIconTheme::current() 0596 { 0597 // Static pointers because of unloading problems wrt DSO's. 0598 if (_themeOverride && !_themeOverride->isEmpty()) { 0599 *_theme() = *_themeOverride(); 0600 } 0601 if (!_theme()->isEmpty()) { 0602 return *_theme(); 0603 } 0604 0605 QString theme; 0606 // Check application specific config for a theme setting. 0607 KConfigGroup app_cg(KSharedConfig::openConfig(QString(), KConfig::NoGlobals), "Icons"); 0608 theme = app_cg.readEntry("Theme", QString()); 0609 if (theme.isEmpty() || theme == QLatin1String("hicolor")) { 0610 // No theme, try to use Qt's. A Platform plugin might have set 0611 // a good theme there. 0612 theme = QIcon::themeName(); 0613 } 0614 if (theme.isEmpty() || theme == QLatin1String("hicolor")) { 0615 // Still no theme, try config with kdeglobals. 0616 KConfigGroup cg(KSharedConfig::openConfig(), "Icons"); 0617 theme = cg.readEntry("Theme", QStringLiteral("breeze")); 0618 } 0619 if (theme.isEmpty() || theme == QLatin1String("hicolor")) { 0620 // Still no good theme, use default. 0621 theme = defaultThemeName(); 0622 } 0623 *_theme() = theme; 0624 return *_theme(); 0625 } 0626 0627 void KIconTheme::forceThemeForTests(const QString &themeName) 0628 { 0629 *_themeOverride() = themeName; 0630 _theme()->clear(); // ::current sets this again based on conditions 0631 } 0632 0633 // static 0634 QStringList KIconTheme::list() 0635 { 0636 // Static pointer because of unloading problems wrt DSO's. 0637 if (!_theme_list()->isEmpty()) { 0638 return *_theme_list(); 0639 } 0640 0641 // Find the theme description file. These are either locally in the :/icons resource path or global. 0642 QStringList icnlibs; 0643 0644 // local embedded icons have preference 0645 icnlibs << QStringLiteral(":/icons"); 0646 0647 // global icons 0648 icnlibs += QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("icons"), QStandardPaths::LocateDirectory); 0649 0650 // These are not in the icon spec, but e.g. GNOME puts some icons there anyway. 0651 icnlibs += QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("pixmaps"), QStandardPaths::LocateDirectory); 0652 0653 for (const QString &iconDir : std::as_const(icnlibs)) { 0654 QDir dir(iconDir); 0655 const QStringList themeDirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); 0656 for (const auto &theme : themeDirs) { 0657 if (theme.startsWith(QLatin1String("default."))) { 0658 continue; 0659 } 0660 0661 const QString prefix = iconDir + QLatin1Char('/') + theme; 0662 if (!QFileInfo::exists(prefix + QLatin1String("/index.desktop")) // 0663 && !QFileInfo::exists(prefix + QLatin1String("/index.theme"))) { 0664 continue; 0665 } 0666 0667 if (!KIconTheme(theme).isValid()) { 0668 continue; 0669 } 0670 0671 if (!_theme_list()->contains(theme)) { 0672 _theme_list()->append(theme); 0673 } 0674 } 0675 } 0676 return *_theme_list(); 0677 } 0678 0679 // static 0680 void KIconTheme::reconfigure() 0681 { 0682 _theme()->clear(); 0683 _theme_list()->clear(); 0684 } 0685 0686 // static 0687 QString KIconTheme::defaultThemeName() 0688 { 0689 return QStringLiteral("hicolor"); 0690 } 0691 0692 /*** KIconThemeDir ***/ 0693 0694 KIconThemeDir::KIconThemeDir(const QString &basedir, const QString &themedir, const KConfigGroup &config) 0695 : mSize(config.readEntry("Size", 0)) 0696 , mScale(config.readEntry("Scale", 1)) 0697 , mBaseDir(basedir) 0698 , mThemeDir(themedir) 0699 { 0700 if (mSize == 0) { 0701 return; 0702 } 0703 0704 QString tmp = config.readEntry(QStringLiteral("Context")); 0705 if (tmp == QLatin1String("Devices")) { 0706 mContext = KIconLoader::Device; 0707 } else if (tmp == QLatin1String("MimeTypes")) { 0708 mContext = KIconLoader::MimeType; 0709 } else if (tmp == QLatin1String("Applications")) { 0710 mContext = KIconLoader::Application; 0711 } else if (tmp == QLatin1String("Actions")) { 0712 mContext = KIconLoader::Action; 0713 } else if (tmp == QLatin1String("Animations")) { 0714 mContext = KIconLoader::Animation; 0715 } else if (tmp == QLatin1String("Categories")) { 0716 mContext = KIconLoader::Category; 0717 } else if (tmp == QLatin1String("Emblems")) { 0718 mContext = KIconLoader::Emblem; 0719 } else if (tmp == QLatin1String("Emotes")) { 0720 mContext = KIconLoader::Emote; 0721 } else if (tmp == QLatin1String("International")) { 0722 mContext = KIconLoader::International; 0723 } else if (tmp == QLatin1String("Places")) { 0724 mContext = KIconLoader::Place; 0725 } else if (tmp == QLatin1String("Status")) { 0726 mContext = KIconLoader::StatusIcon; 0727 } else if (tmp == QLatin1String("Stock")) { // invalid, but often present context, skip warning 0728 return; 0729 } else if (tmp == QLatin1String("Legacy")) { // invalid, but often present context for Adwaita, skip warning 0730 return; 0731 } else if (tmp == QLatin1String("UI")) { // invalid, but often present context for Adwaita, skip warning 0732 return; 0733 } else if (tmp.isEmpty()) { 0734 // do nothing. key not required 0735 } else { 0736 qCDebug(KICONTHEMES) << "Invalid Context=" << tmp << "line for icon theme: " << constructFileName(QString()); 0737 return; 0738 } 0739 tmp = config.readEntry(QStringLiteral("Type"), QStringLiteral("Threshold")); 0740 if (tmp == QLatin1String("Fixed")) { 0741 mType = KIconLoader::Fixed; 0742 } else if (tmp == QLatin1String("Scalable")) { 0743 mType = KIconLoader::Scalable; 0744 } else if (tmp == QLatin1String("Threshold")) { 0745 mType = KIconLoader::Threshold; 0746 } else { 0747 qCDebug(KICONTHEMES) << "Invalid Type=" << tmp << "line for icon theme: " << constructFileName(QString()); 0748 return; 0749 } 0750 if (mType == KIconLoader::Scalable) { 0751 mMinSize = config.readEntry(QStringLiteral("MinSize"), mSize); 0752 mMaxSize = config.readEntry(QStringLiteral("MaxSize"), mSize); 0753 } else if (mType == KIconLoader::Threshold) { 0754 mThreshold = config.readEntry(QStringLiteral("Threshold"), 2); 0755 } 0756 mbValid = true; 0757 } 0758 0759 QString KIconThemeDir::iconPath(const QString &name) const 0760 { 0761 if (!mbValid) { 0762 return QString(); 0763 } 0764 0765 const QString file = constructFileName(name); 0766 if (QFileInfo::exists(file)) { 0767 return KLocalizedString::localizedFilePath(file); 0768 } 0769 0770 return QString(); 0771 } 0772 0773 QStringList KIconThemeDir::iconList() const 0774 { 0775 const QDir icondir = constructFileName(QString()); 0776 0777 const QStringList formats = QStringList() << QStringLiteral("*.png") << QStringLiteral("*.svg") << QStringLiteral("*.svgz") << QStringLiteral("*.xpm"); 0778 const QStringList lst = icondir.entryList(formats, QDir::Files); 0779 0780 QStringList result; 0781 result.reserve(lst.size()); 0782 for (const QString &file : lst) { 0783 result += constructFileName(file); 0784 } 0785 return result; 0786 }