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 &current : 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 }