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