1/* vi: ts=8 sts=4 sw=4
2
3 kicontheme.cpp: Lowlevel icon theme handling.
4
5 This file is part of the KDE project, module kdecore.
6 SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org>
7 SPDX-FileCopyrightText: 2000 Antonio Larrosa <larrosa@kde.org>
8
9 SPDX-License-Identifier: LGPL-2.0-only
10*/
11
12#include "kicontheme.h"
13
14#include "debug.h"
15
16#include <KConfigGroup>
17#include <KLocalizedString> // KLocalizedString::localizedFilePath. Need such functionality in, hmm, QLocale? QStandardPaths?
18#include <KSharedConfig>
19
20#include <QAction>
21#include <QCoreApplication>
22#include <QDebug>
23#include <QDir>
24#include <QFileInfo>
25#include <QMap>
26#include <QResource>
27#include <QSet>
28
29#include <private/qguiapplication_p.h>
30#include <qpa/qplatformtheme.h>
31
32#include <qplatformdefs.h>
33
34#include <array>
35#include <cmath>
36
37Q_GLOBAL_STATIC(QString, _themeOverride)
38
39// Support for icon themes in RCC files.
40// The intended use case is standalone apps on Windows / MacOS / etc.
41// For this reason we use AppDataLocation: BINDIR/data on Windows, Resources on OS X
42void initRCCIconTheme()
43{
44 const QString iconThemeRcc = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("icontheme.rcc"));
45 if (!iconThemeRcc.isEmpty()) {
46 const QString iconThemeName = QStringLiteral("kf6_rcc_theme");
47 const QString iconSubdir = QStringLiteral("/icons/") + iconThemeName;
48 if (QResource::registerResource(iconThemeRcc, iconSubdir)) {
49 if (QFileInfo::exists(QLatin1Char(':') + iconSubdir + QStringLiteral("/index.theme"))) {
50 // Tell Qt about the theme
51 // Note that since qtbase commit a8621a3f8, this means the QPA (i.e. KIconLoader) will NOT be used.
52 QIcon::setThemeName(iconThemeName); // Qt looks under :/icons automatically
53 // Tell KIconTheme about the theme, in case KIconLoader is used directly
54 *_themeOverride() = iconThemeName;
55 } else {
56 qWarning() << "No index.theme found in" << iconThemeRcc;
57 QResource::unregisterResource(iconThemeRcc, iconSubdir);
58 }
59 } else {
60 qWarning() << "Invalid rcc file" << iconThemeRcc;
61 }
62 }
63}
64Q_COREAPP_STARTUP_FUNCTION(initRCCIconTheme)
65
66// Makes sure the icon theme fallback is set to breeze or one of its
67// variants. Most of our apps use "lots" of icons that most of the times
68// are only available with breeze, we still honour the user icon theme
69// but if the icon is not found there, we go to breeze since it's almost
70// sure it'll be there
71static void setBreezeFallback()
72{
73 if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) {
74 const QVariant themeHint = theme->themeHint(QPlatformTheme::SystemIconFallbackThemeName);
75 if (themeHint.isValid()) {
76 const QString iconTheme = themeHint.toString();
77 if (iconTheme.contains(QStringLiteral("breeze"), Qt::CaseInsensitive)) {
78 QIcon::setFallbackThemeName(iconTheme);
79 return;
80 }
81 }
82 }
83
84 QIcon::setFallbackThemeName(QStringLiteral("breeze"));
85}
86
87#ifndef Q_OS_ANDROID
88Q_COREAPP_STARTUP_FUNCTION(setBreezeFallback)
89#endif
90class KIconThemeDir;
91class KIconThemePrivate
92{
93public:
94 QString example, screenshot;
95 bool hidden;
96 KSharedConfig::Ptr sharedConfig;
97
98 struct GroupInfo {
99 KIconLoader::Group type;
100 const char *name;
101 int defaultSize;
102 QList<int> availableSizes{};
103 };
104 std::array<GroupInfo, KIconLoader::LastGroup> m_iconGroups = {{
105 {KIconLoader::Desktop, "Desktop", 32},
106 {KIconLoader::Toolbar, "Toolbar", 22},
107 {KIconLoader::MainToolbar, "MainToolbar", 22},
108 {KIconLoader::Small, "Small", 16},
109 {KIconLoader::Panel, "Panel", 48},
110 {KIconLoader::Dialog, "Dialog", 32},
111 }};
112
113 int mDepth;
114 QString mDir, mName, mInternalName, mDesc;
115 QStringList mInherits;
116 QStringList mExtensions;
117 QList<KIconThemeDir *> mDirs;
118 QList<KIconThemeDir *> mScaledDirs;
119 bool followsColorScheme : 1;
120
121 /// Searches the given dirs vector for a matching icon
122 QString iconPath(const QList<KIconThemeDir *> &dirs, const QString &name, int size, qreal scale, KIconLoader::MatchType match) const;
123};
124Q_GLOBAL_STATIC(QString, _theme)
125Q_GLOBAL_STATIC(QStringList, _theme_list)
126
127/**
128 * A subdirectory in an icon theme.
129 */
130class KIconThemeDir
131{
132public:
133 KIconThemeDir(const QString &basedir, const QString &themedir, const KConfigGroup &config);
134
135 bool isValid() const
136 {
137 return mbValid;
138 }
139 QString iconPath(const QString &name) const;
140 QStringList iconList() const;
141 QString constructFileName(const QString &file) const
142 {
143 return mBaseDir + mThemeDir + QLatin1Char('/') + file;
144 }
145
146 KIconLoader::Context context() const
147 {
148 return mContext;
149 }
150 KIconLoader::Type type() const
151 {
152 return mType;
153 }
154 int size() const
155 {
156 return mSize;
157 }
158 int scale() const
159 {
160 return mScale;
161 }
162 int minSize() const
163 {
164 return mMinSize;
165 }
166 int maxSize() const
167 {
168 return mMaxSize;
169 }
170 int threshold() const
171 {
172 return mThreshold;
173 }
174
175private:
176 bool mbValid = false;
177 KIconLoader::Type mType = KIconLoader::Fixed;
178 KIconLoader::Context mContext;
179 int mSize = 0;
180 int mScale = 1;
181 int mMinSize = 1;
182 int mMaxSize = 50;
183 int mThreshold = 2;
184
185 const QString mBaseDir;
186 const QString mThemeDir;
187};
188
189QString KIconThemePrivate::iconPath(const QList<KIconThemeDir *> &dirs, const QString &name, int size, qreal scale, KIconLoader::MatchType match) const
190{
191 QString path;
192 QString tempPath; // used to cache icon path if it exists
193
194 int delta = -INT_MAX; // current icon size delta of 'icon'
195 int dw = INT_MAX; // icon size delta of current directory
196
197 // Rather downsample than upsample
198 int integerScale = std::ceil(scale);
199
200 // Search the directory that contains the icon which matches best to the requested
201 // size. If there is no directory which matches exactly to the requested size, the
202 // following criteria get applied:
203 // - Take a directory having icons with a minimum difference to the requested size.
204 // - Prefer directories that allow a downscaling even if the difference to
205 // the requested size is bigger than a directory where an upscaling is required.
206 for (KIconThemeDir *dir : dirs) {
207 if (dir->scale() != integerScale) {
208 continue;
209 }
210
211 if (match == KIconLoader::MatchExact) {
212 if ((dir->type() == KIconLoader::Fixed) && (dir->size() != size)) {
213 continue;
214 }
215 if ((dir->type() == KIconLoader::Scalable) //
216 && ((size < dir->minSize()) || (size > dir->maxSize()))) {
217 continue;
218 }
219 if ((dir->type() == KIconLoader::Threshold) //
220 && (abs(dir->size() - size) > dir->threshold())) {
221 continue;
222 }
223 } else {
224 // dw < 0 means need to scale up to get an icon of the requested size.
225 // Upscaling should only be done if no larger icon is available.
226 if (dir->type() == KIconLoader::Fixed) {
227 dw = dir->size() - size;
228 } else if (dir->type() == KIconLoader::Scalable) {
229 if (size < dir->minSize()) {
230 dw = dir->minSize() - size;
231 } else if (size > dir->maxSize()) {
232 dw = dir->maxSize() - size;
233 } else {
234 dw = 0;
235 }
236 } else if (dir->type() == KIconLoader::Threshold) {
237 if (size < dir->size() - dir->threshold()) {
238 dw = dir->size() - dir->threshold() - size;
239 } else if (size > dir->size() + dir->threshold()) {
240 dw = dir->size() + dir->threshold() - size;
241 } else {
242 dw = 0;
243 }
244 }
245 // Usually if the delta (= 'dw') of the current directory is
246 // not smaller than the delta (= 'delta') of the currently best
247 // matching icon, this candidate can be skipped. But skipping
248 // the candidate may only be done, if this does not imply
249 // in an upscaling of the icon (it is OK to use a directory with
250 // smaller icons that what we've already found, however).
251 if ((abs(dw) >= abs(delta)) && ((dw < 0) || (delta > 0))) {
252 continue;
253 }
254
255 if (match == KIconLoader::MatchBestOrGreaterSize && dw < 0) {
256 continue;
257 }
258 }
259
260 // cache the result of iconPath() call which checks if file exists
261 tempPath = dir->iconPath(name);
262
263 if (tempPath.isEmpty()) {
264 continue;
265 }
266
267 path = tempPath;
268
269 // if we got in MatchExact that far, we find no better
270 if (match == KIconLoader::MatchExact) {
271 return path;
272 }
273 delta = dw;
274 if (delta == 0) {
275 return path; // We won't find a better match anyway
276 }
277 }
278 return path;
279}
280
281KIconTheme::KIconTheme(const QString &name, const QString &appName, const QString &basePathHint)
282 : d(new KIconThemePrivate)
283{
284 d->mInternalName = name;
285
286 QStringList themeDirs;
287
288 // Applications can have local additions to the global "locolor" and
289 // "hicolor" icon themes. For these, the _global_ theme description
290 // files are used..
291
292 /* clang-format off */
293 if (!appName.isEmpty()
294 && (name == defaultThemeName()
295 || name == QLatin1String("hicolor")
296 || name == QLatin1String("locolor"))) { /* clang-format on */
297 const QString suffix = QLatin1Char('/') + appName + QLatin1String("/icons/") + name + QLatin1Char('/');
298 QStringList dataDirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
299 for (auto &cDir : dataDirs) {
300 cDir += suffix;
301 if (QFileInfo::exists(cDir)) {
302 themeDirs += cDir;
303 }
304 }
305
306 if (!basePathHint.isEmpty()) {
307 // Checks for dir existing are done below
308 themeDirs += basePathHint + QLatin1Char('/') + name + QLatin1Char('/');
309 }
310 }
311
312 // Find the theme description file. These are either locally in the :/icons resource path or global.
313 QStringList icnlibs;
314
315 // local embedded icons have preference
316 icnlibs << QStringLiteral(":/icons");
317
318 // global icons
319 icnlibs += QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("icons"), QStandardPaths::LocateDirectory);
320
321 // These are not in the icon spec, but e.g. GNOME puts some icons there anyway.
322 icnlibs += QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("pixmaps"), QStandardPaths::LocateDirectory);
323
324 QString fileName;
325 QString mainSection;
326 const QString pathSuffix = QLatin1Char('/') + name + QLatin1Char('/');
327 const QLatin1String indexTheme("index.theme");
328 const QLatin1String indexDesktop("theme.desktop");
329 for (auto &iconDir : icnlibs) {
330 iconDir += pathSuffix;
331 const QFileInfo fi(iconDir);
332 if (!fi.exists() || !fi.isDir()) {
333 continue;
334 }
335 themeDirs.append(iconDir);
336
337 if (d->mDir.isEmpty()) {
338 QString possiblePath;
339 if (possiblePath = iconDir + indexTheme; QFileInfo::exists(possiblePath)) {
340 d->mDir = iconDir;
341 fileName = possiblePath;
342 mainSection = QStringLiteral("Icon Theme");
343 } else if (possiblePath = iconDir + indexDesktop; QFileInfo::exists(possiblePath)) {
344 d->mDir = iconDir;
345 fileName = possiblePath;
346 mainSection = QStringLiteral("KDE Icon Theme");
347 }
348 }
349 }
350
351 if (d->mDir.isEmpty()) {
352 qCDebug(KICONTHEMES) << "Icon theme" << name << "not found.";
353 return;
354 }
355
356 // Use KSharedConfig to avoid parsing the file many times, from each component.
357 // Need to keep a ref to it to make this useful
358 d->sharedConfig = KSharedConfig::openConfig(fileName, KConfig::NoGlobals);
359
360 KConfigGroup cfg(d->sharedConfig, mainSection);
361 d->mName = cfg.readEntry("Name");
362 d->mDesc = cfg.readEntry("Comment");
363 d->mDepth = cfg.readEntry(key: "DisplayDepth", defaultValue: 32);
364 d->mInherits = cfg.readEntry("Inherits", QStringList());
365 if (name != defaultThemeName()) {
366 for (auto &inheritedTheme : d->mInherits) {
367 if (inheritedTheme == QLatin1String("default")) {
368 inheritedTheme = defaultThemeName();
369 }
370 }
371 }
372
373 d->hidden = cfg.readEntry(key: "Hidden", defaultValue: false);
374 d->followsColorScheme = cfg.readEntry(key: "FollowsColorScheme", defaultValue: false);
375 d->example = cfg.readPathEntry("Example", QString());
376 d->screenshot = cfg.readPathEntry("ScreenShot", QString());
377 d->mExtensions =
378 cfg.readEntry("KDE-Extensions", QStringList{QStringLiteral(".png"), QStringLiteral(".svgz"), QStringLiteral(".svg"), QStringLiteral(".xpm")});
379
380 QSet<QString> addedDirs; // Used for avoiding duplicates.
381 const QStringList dirs = cfg.readPathEntry("Directories", QStringList()) + cfg.readPathEntry("ScaledDirectories", QStringList());
382 for (const auto &dirName : dirs) {
383 KConfigGroup cg(d->sharedConfig, dirName);
384 for (const auto &themeDir : std::as_const(themeDirs)) {
385 const QString currentDir(themeDir + dirName + QLatin1Char('/'));
386 if (!addedDirs.contains(currentDir) && QDir(currentDir).exists()) {
387 addedDirs.insert(currentDir);
388 KIconThemeDir *dir = new KIconThemeDir(themeDir, dirName, cg);
389 if (dir->isValid()) {
390 if (dir->scale() > 1) {
391 d->mScaledDirs.append(dir);
392 } else {
393 d->mDirs.append(dir);
394 }
395 } else {
396 delete dir;
397 }
398 }
399 }
400 }
401
402 KConfigGroup cg(d->sharedConfig, mainSection);
403 for (auto &iconGroup : d->m_iconGroups) {
404 iconGroup.defaultSize = cg.readEntry(iconGroup.name + QLatin1String("Default"), iconGroup.defaultSize);
405 iconGroup.availableSizes = cg.readEntry(iconGroup.name + QLatin1String("Sizes"), QList<int>());
406 }
407}
408
409KIconTheme::~KIconTheme()
410{
411 qDeleteAll(d->mDirs);
412 qDeleteAll(d->mScaledDirs);
413}
414
415QString KIconTheme::name() const
416{
417 return d->mName;
418}
419
420QString KIconTheme::internalName() const
421{
422 return d->mInternalName;
423}
424
425QString KIconTheme::description() const
426{
427 return d->mDesc;
428}
429
430QString KIconTheme::example() const
431{
432 return d->example;
433}
434
435QString KIconTheme::screenshot() const
436{
437 return d->screenshot;
438}
439
440QString KIconTheme::dir() const
441{
442 return d->mDir;
443}
444
445QStringList KIconTheme::inherits() const
446{
447 return d->mInherits;
448}
449
450bool KIconTheme::isValid() const
451{
452 return !d->mDirs.isEmpty() || !d->mScaledDirs.isEmpty();
453}
454
455bool KIconTheme::isHidden() const
456{
457 return d->hidden;
458}
459
460int KIconTheme::depth() const
461{
462 return d->mDepth;
463}
464
465int KIconTheme::defaultSize(KIconLoader::Group group) const
466{
467 if (group < 0 || group >= KIconLoader::LastGroup) {
468 qCWarning(KICONTHEMES) << "Invalid icon group:" << group << ", should be one of KIconLoader::Group";
469 return -1;
470 }
471 return d->m_iconGroups[group].defaultSize;
472}
473
474QList<int> KIconTheme::querySizes(KIconLoader::Group group) const
475{
476 if (group < 0 || group >= KIconLoader::LastGroup) {
477 qCWarning(KICONTHEMES) << "Invalid icon group:" << group << ", should be one of KIconLoader::Group";
478 return QList<int>();
479 }
480 return d->m_iconGroups[group].availableSizes;
481}
482
483static bool isAnyOrDirContext(const KIconThemeDir *dir, KIconLoader::Context context)
484{
485 return context == KIconLoader::Any || context == dir->context();
486}
487
488QStringList KIconTheme::queryIcons(int size, KIconLoader::Context context) const
489{
490 // Try to find exact match
491 QStringList result;
492 const QList<KIconThemeDir *> listDirs = d->mDirs + d->mScaledDirs;
493 for (const KIconThemeDir *dir : listDirs) {
494 if (!isAnyOrDirContext(dir, context)) {
495 continue;
496 }
497
498 const int dirSize = dir->size();
499 if ((dir->type() == KIconLoader::Fixed && dirSize == size) //
500 || (dir->type() == KIconLoader::Scalable && size >= dir->minSize() && size <= dir->maxSize())
501 || (dir->type() == KIconLoader::Threshold && abs(size - dirSize) < dir->threshold())) {
502 result += dir->iconList();
503 }
504 }
505
506 return result;
507}
508
509QStringList KIconTheme::queryIconsByContext(int size, KIconLoader::Context context) const
510{
511 int dw;
512
513 // We want all the icons for a given context, but we prefer icons
514 // of size "size" . Note that this may (will) include duplicate icons
515 // QStringList iconlist[34]; // 33 == 48-16+1
516 QStringList iconlist[128]; // 33 == 48-16+1
517 // Usually, only the 0, 6 (22-16), 10 (32-22), 16 (48-32 or 32-16),
518 // 26 (48-22) and 32 (48-16) will be used, but who knows if someone
519 // will make icon themes with different icon sizes.
520 const auto listDirs = d->mDirs + d->mScaledDirs;
521 for (KIconThemeDir *dir : listDirs) {
522 if (!isAnyOrDirContext(dir, context)) {
523 continue;
524 }
525 dw = abs(dir->size() - size);
526 iconlist[(dw < 127) ? dw : 127] += dir->iconList();
527 }
528
529 QStringList iconlistResult;
530 for (int i = 0; i < 128; i++) {
531 iconlistResult += iconlist[i];
532 }
533
534 return iconlistResult;
535}
536
537bool KIconTheme::hasContext(KIconLoader::Context context) const
538{
539 const auto listDirs = d->mDirs + d->mScaledDirs;
540 for (KIconThemeDir *dir : listDirs) {
541 if (isAnyOrDirContext(dir, context)) {
542 return true;
543 }
544 }
545 return false;
546}
547
548QString KIconTheme::iconPathByName(const QString &iconName, int size, KIconLoader::MatchType match) const
549{
550 return iconPathByName(iconName, size, match, 1 /*scale*/);
551}
552
553QString KIconTheme::iconPathByName(const QString &iconName, int size, KIconLoader::MatchType match, qreal scale) const
554{
555 for (const QString &current : std::as_const(d->mExtensions)) {
556 const QString path = iconPath(iconName + current, size, match, scale);
557 if (!path.isEmpty()) {
558 return path;
559 }
560 }
561 return QString();
562}
563
564bool KIconTheme::followsColorScheme() const
565{
566 return d->followsColorScheme;
567}
568
569QString KIconTheme::iconPath(const QString &name, int size, KIconLoader::MatchType match) const
570{
571 return iconPath(name, size, match, 1 /*scale*/);
572}
573
574QString KIconTheme::iconPath(const QString &name, int size, KIconLoader::MatchType match, qreal scale) const
575{
576 // first look for a scaled image at exactly the requested size
577 QString path = d->iconPath(d->mScaledDirs, name, size, scale, KIconLoader::MatchExact);
578
579 // then look for an unscaled one but request it at larger size so it doesn't become blurry
580 if (path.isEmpty()) {
581 path = d->iconPath(d->mDirs, name, size * scale, 1, match);
582 }
583 return path;
584}
585
586// static
587QString KIconTheme::current()
588{
589 // Static pointers because of unloading problems wrt DSO's.
590 if (_themeOverride && !_themeOverride->isEmpty()) {
591 *_theme() = *_themeOverride();
592 }
593 if (!_theme()->isEmpty()) {
594 return *_theme();
595 }
596
597 QString theme;
598 // Check application specific config for a theme setting.
599 KConfigGroup app_cg(KSharedConfig::openConfig(QString(), KConfig::NoGlobals), "Icons");
600 theme = app_cg.readEntry("Theme", QString());
601 if (theme.isEmpty() || theme == QLatin1String("hicolor")) {
602 // No theme, try to use Qt's. A Platform plugin might have set
603 // a good theme there.
604 theme = QIcon::themeName();
605 }
606 if (theme.isEmpty() || theme == QLatin1String("hicolor")) {
607 // Still no theme, try config with kdeglobals.
608 KConfigGroup cg(KSharedConfig::openConfig(), "Icons");
609 theme = cg.readEntry("Theme", QStringLiteral("breeze"));
610 }
611 if (theme.isEmpty() || theme == QLatin1String("hicolor")) {
612 // Still no good theme, use default.
613 theme = defaultThemeName();
614 }
615 *_theme() = theme;
616 return *_theme();
617}
618
619void KIconTheme::forceThemeForTests(const QString &themeName)
620{
621 *_themeOverride() = themeName;
622 _theme()->clear(); // ::current sets this again based on conditions
623}
624
625// static
626QStringList KIconTheme::list()
627{
628 // Static pointer because of unloading problems wrt DSO's.
629 if (!_theme_list()->isEmpty()) {
630 return *_theme_list();
631 }
632
633 // Find the theme description file. These are either locally in the :/icons resource path or global.
634 QStringList icnlibs;
635
636 // local embedded icons have preference
637 icnlibs << QStringLiteral(":/icons");
638
639 // global icons
640 icnlibs += QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("icons"), QStandardPaths::LocateDirectory);
641
642 // These are not in the icon spec, but e.g. GNOME puts some icons there anyway.
643 icnlibs += QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("pixmaps"), QStandardPaths::LocateDirectory);
644
645 for (const QString &iconDir : std::as_const(icnlibs)) {
646 QDir dir(iconDir);
647 const QStringList themeDirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
648 for (const auto &theme : themeDirs) {
649 if (theme.startsWith(QLatin1String("default."))) {
650 continue;
651 }
652
653 const QString prefix = iconDir + QLatin1Char('/') + theme;
654 if (!QFileInfo::exists(prefix + QLatin1String("/index.desktop")) //
655 && !QFileInfo::exists(prefix + QLatin1String("/index.theme"))) {
656 continue;
657 }
658
659 if (!KIconTheme(theme).isValid()) {
660 continue;
661 }
662
663 if (!_theme_list()->contains(theme)) {
664 _theme_list()->append(theme);
665 }
666 }
667 }
668 return *_theme_list();
669}
670
671// static
672void KIconTheme::reconfigure()
673{
674 _theme()->clear();
675 _theme_list()->clear();
676}
677
678// static
679QString KIconTheme::defaultThemeName()
680{
681 return QStringLiteral("hicolor");
682}
683
684/*** KIconThemeDir ***/
685
686KIconThemeDir::KIconThemeDir(const QString &basedir, const QString &themedir, const KConfigGroup &config)
687 : mSize(config.readEntry("Size", 0))
688 , mScale(config.readEntry("Scale", 1))
689 , mBaseDir(basedir)
690 , mThemeDir(themedir)
691{
692 if (mSize == 0) {
693 return;
694 }
695
696 QString tmp = config.readEntry(QStringLiteral("Context"));
697 if (tmp == QLatin1String("Devices")) {
698 mContext = KIconLoader::Device;
699 } else if (tmp == QLatin1String("MimeTypes")) {
700 mContext = KIconLoader::MimeType;
701 } else if (tmp == QLatin1String("Applications")) {
702 mContext = KIconLoader::Application;
703 } else if (tmp == QLatin1String("Actions")) {
704 mContext = KIconLoader::Action;
705 } else if (tmp == QLatin1String("Animations")) {
706 mContext = KIconLoader::Animation;
707 } else if (tmp == QLatin1String("Categories")) {
708 mContext = KIconLoader::Category;
709 } else if (tmp == QLatin1String("Emblems")) {
710 mContext = KIconLoader::Emblem;
711 } else if (tmp == QLatin1String("Emotes")) {
712 mContext = KIconLoader::Emote;
713 } else if (tmp == QLatin1String("International")) {
714 mContext = KIconLoader::International;
715 } else if (tmp == QLatin1String("Places")) {
716 mContext = KIconLoader::Place;
717 } else if (tmp == QLatin1String("Status")) {
718 mContext = KIconLoader::StatusIcon;
719 } else if (tmp == QLatin1String("Stock")) { // invalid, but often present context, skip warning
720 return;
721 } else if (tmp == QLatin1String("Legacy")) { // invalid, but often present context for Adwaita, skip warning
722 return;
723 } else if (tmp == QLatin1String("UI")) { // invalid, but often present context for Adwaita, skip warning
724 return;
725 } else if (tmp.isEmpty()) {
726 // do nothing. key not required
727 } else {
728 qCDebug(KICONTHEMES) << "Invalid Context=" << tmp << "line for icon theme: " << constructFileName(QString());
729 return;
730 }
731 tmp = config.readEntry(QStringLiteral("Type"), QStringLiteral("Threshold"));
732 if (tmp == QLatin1String("Fixed")) {
733 mType = KIconLoader::Fixed;
734 } else if (tmp == QLatin1String("Scalable")) {
735 mType = KIconLoader::Scalable;
736 } else if (tmp == QLatin1String("Threshold")) {
737 mType = KIconLoader::Threshold;
738 } else {
739 qCDebug(KICONTHEMES) << "Invalid Type=" << tmp << "line for icon theme: " << constructFileName(QString());
740 return;
741 }
742 if (mType == KIconLoader::Scalable) {
743 mMinSize = config.readEntry(QStringLiteral("MinSize"), mSize);
744 mMaxSize = config.readEntry(QStringLiteral("MaxSize"), mSize);
745 } else if (mType == KIconLoader::Threshold) {
746 mThreshold = config.readEntry(QStringLiteral("Threshold"), 2);
747 }
748 mbValid = true;
749}
750
751QString KIconThemeDir::iconPath(const QString &name) const
752{
753 if (!mbValid) {
754 return QString();
755 }
756
757 const QString file = constructFileName(name);
758 if (QFileInfo::exists(file)) {
759 return KLocalizedString::localizedFilePath(file);
760 }
761
762 return QString();
763}
764
765QStringList KIconThemeDir::iconList() const
766{
767 const QDir icondir = constructFileName(QString());
768
769 const QStringList formats = QStringList() << QStringLiteral("*.png") << QStringLiteral("*.svg") << QStringLiteral("*.svgz") << QStringLiteral("*.xpm");
770 const QStringList lst = icondir.entryList(formats, QDir::Files);
771
772 QStringList result;
773 result.reserve(lst.size());
774 for (const QString &file : lst) {
775 result += constructFileName(file);
776 }
777 return result;
778}
779

source code of kiconthemes/src/kicontheme.cpp