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

source code of kiconthemes/src/kicontheme.cpp