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