KIconThemes

kiconloader.cpp
1/*
2
3 kiconloader.cpp: An icon loader for KDE with theming functionality.
4
5 This file is part of the KDE project, module kdeui.
6 SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org>
7 SPDX-FileCopyrightText: 2000 Antonio Larrosa <larrosa@kde.org>
8 SPDX-FileCopyrightText: 2010 Michael Pyne <mpyne@kde.org>
9
10 SPDX-License-Identifier: LGPL-2.0-only
11*/
12
13#include "kiconloader.h"
14#include "kiconloader_p.h"
15
16// kdecore
17#include <KConfigGroup>
18#include <KSharedConfig>
19#ifdef WITH_QTDBUS
20#include <QDBusConnection>
21#include <QDBusMessage>
22#endif
23#include <QCryptographicHash>
24#include <QXmlStreamReader>
25#include <QXmlStreamWriter>
26
27// kdeui
28#include "debug.h"
29#include "kiconcolors.h"
30#include "kiconeffect.h"
31#include "kicontheme.h"
32
33#include <KColorScheme>
34#include <KCompressionDevice>
35
36#include <QBuffer>
37#include <QByteArray>
38#include <QDataStream>
39#include <QDir>
40#include <QElapsedTimer>
41#include <QFileInfo>
42#include <QGuiApplication>
43#include <QIcon>
44#include <QImage>
45#include <QMimeDatabase>
46#include <QMovie>
47#include <QPainter>
48#include <QPixmap>
49#include <QPixmapCache>
50#include <QStringBuilder> // % operator for QString
51#include <QtGui/private/qiconloader_p.h>
52
53#include <qplatformdefs.h> //for readlink
54
55/**
56 * Function to convert an uint32_t to AARRGGBB hex values.
57 *
58 * W A R N I N G !
59 * This function is for internal use!
60 */
61KICONTHEMES_EXPORT void uintToHex(uint32_t colorData, QChar *buffer)
62{
63 static const char hexLookup[] = "0123456789abcdef";
64 buffer += 7;
65 uchar *colorFields = reinterpret_cast<uchar *>(&colorData);
66
67 for (int i = 0; i < 4; i++) {
68 *buffer-- = hexLookup[*colorFields & 0xf];
69 *buffer-- = hexLookup[*colorFields >> 4];
70 colorFields++;
71 }
72}
73
74static QString paletteId(const KIconColors &colors)
75{
76 // 8 per color. We want 3 colors thus 8*4=32.
77 QString buffer(32, Qt::Uninitialized);
78
79 uintToHex(colors.text().rgba(), buffer.data());
80 uintToHex(colors.highlight().rgba(), buffer.data() + 8);
81 uintToHex(colors.highlightedText().rgba(), buffer.data() + 16);
82 uintToHex(colors.background().rgba(), buffer.data() + 24);
83
84 return buffer;
85}
86
87/*** KIconThemeNode: A node in the icon theme dependency tree. ***/
88
89class KIconThemeNode
90{
91public:
92 KIconThemeNode(KIconTheme *_theme);
93 ~KIconThemeNode();
94
95 KIconThemeNode(const KIconThemeNode &) = delete;
96 KIconThemeNode &operator=(const KIconThemeNode &) = delete;
97
98 void queryIcons(QStringList *lst, int size, KIconLoader::Context context) const;
99 void queryIconsByContext(QStringList *lst, int size, KIconLoader::Context context) const;
100 QString findIcon(const QString &name, int size, KIconLoader::MatchType match) const;
101
102 KIconTheme *theme;
103};
104
105KIconThemeNode::KIconThemeNode(KIconTheme *_theme)
106{
107 theme = _theme;
108}
109
110KIconThemeNode::~KIconThemeNode()
111{
112 delete theme;
113}
114
115void KIconThemeNode::queryIcons(QStringList *result, int size, KIconLoader::Context context) const
116{
117 // add the icons of this theme to it
118 *result += theme->queryIcons(size, context);
119}
120
121void KIconThemeNode::queryIconsByContext(QStringList *result, int size, KIconLoader::Context context) const
122{
123 // add the icons of this theme to it
124 *result += theme->queryIconsByContext(size, context);
125}
126
127QString KIconThemeNode::findIcon(const QString &name, int size, KIconLoader::MatchType match) const
128{
129 return theme->iconPath(name, size, match);
130}
131
132extern KICONTHEMES_EXPORT int kiconloader_ms_between_checks;
133KICONTHEMES_EXPORT int kiconloader_ms_between_checks = 5000;
134
135class KIconLoaderGlobalData : public QObject
136{
138
139public:
140 KIconLoaderGlobalData()
141 {
142 // use Qt to fill in the generic mime-type icon information
143 const auto allMimeTypes = QMimeDatabase().allMimeTypes();
144 for (const auto &mimeType : allMimeTypes) {
145 m_genericIcons.insert(mimeType.iconName(), mimeType.genericIconName());
146 }
147
148#ifdef WITH_QTDBUS
149 if (QDBusConnection::sessionBus().interface()) {
151 QStringLiteral("/KIconLoader"),
152 QStringLiteral("org.kde.KIconLoader"),
153 QStringLiteral("iconChanged"),
154 this,
155 SIGNAL(iconChanged(int)));
156 }
157#endif
158 }
159
160 void emitChange(KIconLoader::Group group)
161 {
162#ifdef WITH_QTDBUS
163 if (QDBusConnection::sessionBus().interface()) {
164 QDBusMessage message =
165 QDBusMessage::createSignal(QStringLiteral("/KIconLoader"), QStringLiteral("org.kde.KIconLoader"), QStringLiteral("iconChanged"));
166 message.setArguments(QList<QVariant>() << int(group));
168 }
169#endif
170 }
171
172 QString genericIconFor(const QString &icon) const
173 {
174 return m_genericIcons.value(icon);
175 }
176
178 void iconChanged(int group);
179
180private:
181 QHash<QString, QString> m_genericIcons;
182};
183
184Q_GLOBAL_STATIC(KIconLoaderGlobalData, s_globalData)
185
186KIconLoaderPrivate::KIconLoaderPrivate(const QString &_appname, const QStringList &extraSearchPaths, KIconLoader *qq)
187 : q(qq)
188 , m_appname(_appname)
189{
190 q->connect(s_globalData, &KIconLoaderGlobalData::iconChanged, q, [this](int group) {
191 _k_refreshIcons(group);
192 });
193 init(m_appname, extraSearchPaths);
194}
195
196KIconLoaderPrivate::~KIconLoaderPrivate()
197{
198 clear();
199}
200
201KIconLoaderPrivate *KIconLoaderPrivate::get(KIconLoader *loader)
202{
203 return loader->d.get();
204}
205
206void KIconLoaderPrivate::clear()
207{
208 /* antlarr: There's no need to delete d->mpThemeRoot as it's already
209 deleted when the elements of d->links are deleted */
210 qDeleteAll(links);
211 delete[] mpGroups;
212 mpGroups = nullptr;
213 mPixmapCache.clear();
214 m_appname.clear();
215 searchPaths.clear();
216 links.clear();
217 mIconThemeInited = false;
218 mThemesInTree.clear();
219}
220
221#if KICONTHEMES_BUILD_DEPRECATED_SINCE(6, 5)
222void KIconLoaderPrivate::drawOverlays(const KIconLoader *iconLoader, KIconLoader::Group group, int state, QPixmap &pix, const QStringList &overlays)
223{
224 if (overlays.isEmpty()) {
225 return;
226 }
227
228 const int width = pix.size().width();
229 const int height = pix.size().height();
230 const int iconSize = qMin(width, height);
231 int overlaySize;
232
233 if (iconSize < 32) {
234 overlaySize = 8;
235 } else if (iconSize <= 48) {
236 overlaySize = 16;
237 } else if (iconSize <= 96) {
238 overlaySize = 22;
239 } else if (iconSize < 256) {
240 overlaySize = 32;
241 } else {
242 overlaySize = 64;
243 }
244
245 QPainter painter(&pix);
246
247 int count = 0;
248 for (const QString &overlay : overlays) {
249 // Ensure empty strings fill up a emblem spot
250 // Needed when you have several emblems to ensure they're always painted
251 // at the same place, even if one is not here
252 if (overlay.isEmpty()) {
253 ++count;
254 continue;
255 }
256
257 // TODO: should we pass in the kstate? it results in a slower
258 // path, and perhaps emblems should remain in the default state
259 // anyways?
260 QPixmap pixmap = iconLoader->loadIcon(overlay, group, overlaySize, state, QStringList(), nullptr, true);
261
262 if (pixmap.isNull()) {
263 continue;
264 }
265
266 // match the emblem's devicePixelRatio to the original pixmap's
268 const int margin = pixmap.devicePixelRatio() * 0.05 * iconSize;
269
270 QPoint startPoint;
271 switch (count) {
272 case 0:
273 // bottom right corner
274 startPoint = QPoint(width - overlaySize - margin, height - overlaySize - margin);
275 break;
276 case 1:
277 // bottom left corner
278 startPoint = QPoint(margin, height - overlaySize - margin);
279 break;
280 case 2:
281 // top left corner
282 startPoint = QPoint(margin, margin);
283 break;
284 case 3:
285 // top right corner
286 startPoint = QPoint(width - overlaySize - margin, margin);
287 break;
288 }
289
290 startPoint /= pix.devicePixelRatio();
291
292 painter.drawPixmap(startPoint, pixmap);
293
294 ++count;
295 if (count > 3) {
296 break;
297 }
298 }
299}
300#endif
301
302void KIconLoaderPrivate::_k_refreshIcons(int group)
303{
304 KSharedConfig::Ptr sharedConfig = KSharedConfig::openConfig();
305 sharedConfig->reparseConfiguration();
306 const QString newThemeName = sharedConfig->group("Icons").readEntry("Theme", QStringLiteral("breeze"));
307 if (!newThemeName.isEmpty()) {
308 // NOTE Do NOT use QIcon::setThemeName here it makes Qt not use icon engine of the platform theme
309 // anymore (KIconEngine on Plasma, which breaks recoloring) and overwrites a user set themeName
310 // TODO KF6 this should be done in the Plasma QPT
311 QIconLoader::instance()->updateSystemTheme();
312 }
313
314 q->newIconLoader();
315 mIconAvailability.clear();
316 Q_EMIT q->iconChanged(group);
317}
318
319bool KIconLoaderPrivate::shouldCheckForUnknownIcons()
320{
321 if (mLastUnknownIconCheck.isValid() && mLastUnknownIconCheck.elapsed() < kiconloader_ms_between_checks) {
322 return false;
323 }
324 mLastUnknownIconCheck.start();
325 return true;
326}
327
328KIconLoader::KIconLoader(const QString &appname, const QStringList &extraSearchPaths, QObject *parent)
329 : QObject(parent)
330 , d(new KIconLoaderPrivate(appname, extraSearchPaths, this))
331{
332 setObjectName(appname);
333}
334
335void KIconLoader::reconfigure(const QString &_appname, const QStringList &extraSearchPaths)
336{
337 d->clear();
338 d->init(_appname, extraSearchPaths);
339}
340
341void KIconLoaderPrivate::init(const QString &_appname, const QStringList &extraSearchPaths)
342{
343 extraDesktopIconsLoaded = false;
344 mIconThemeInited = false;
345 mpThemeRoot = nullptr;
346
347 searchPaths = extraSearchPaths;
348
349 m_appname = !_appname.isEmpty() ? _appname : QCoreApplication::applicationName();
350
351 // Cost here is number of pixels
352 mPixmapCache.setMaxCost(10 * 1024 * 1024);
353
354 // These have to match the order in kiconloader.h
355 static const char *const groups[] = {"Desktop", "Toolbar", "MainToolbar", "Small", "Panel", "Dialog", nullptr};
356
357 // load default sizes
358 initIconThemes();
359 KIconTheme *defaultSizesTheme = links.empty() ? nullptr : links.first()->theme;
360 mpGroups = new KIconGroup[static_cast<int>(KIconLoader::LastGroup)];
362 if (groups[i] == nullptr) {
363 break;
364 }
365
366 if (defaultSizesTheme) {
367 mpGroups[i].size = defaultSizesTheme->defaultSize(i);
368 }
369 }
370}
371
372void KIconLoaderPrivate::initIconThemes()
373{
374 if (mIconThemeInited) {
375 return;
376 }
377 // qCDebug(KICONTHEMES);
378 mIconThemeInited = true;
379
380 // Add the default theme and its base themes to the theme tree
381 KIconTheme *def = new KIconTheme(KIconTheme::current(), m_appname);
382 if (!def->isValid()) {
383 delete def;
384 // warn, as this is actually a small penalty hit
385 qCDebug(KICONTHEMES) << "Couldn't find current icon theme, falling back to default.";
386 def = new KIconTheme(KIconTheme::defaultThemeName(), m_appname);
387 if (!def->isValid()) {
388 qCDebug(KICONTHEMES) << "Standard icon theme" << KIconTheme::defaultThemeName() << "not found!";
389 delete def;
390 return;
391 }
392 }
393 mpThemeRoot = new KIconThemeNode(def);
394 mThemesInTree.append(def->internalName());
395 links.append(mpThemeRoot);
396 addBaseThemes(mpThemeRoot, m_appname);
397
398 // Insert application specific themes at the top.
399 searchPaths.append(m_appname + QStringLiteral("/pics"));
400
401 // Add legacy icon dirs.
402 searchPaths.append(QStringLiteral("icons")); // was xdgdata-icon in KStandardDirs
403 // These are not in the icon spec, but e.g. GNOME puts some icons there anyway.
404 searchPaths.append(QStringLiteral("pixmaps")); // was xdgdata-pixmaps in KStandardDirs
405}
406
407KIconLoader::~KIconLoader() = default;
408
410{
411 return d->searchPaths;
412}
413
414void KIconLoader::addAppDir(const QString &appname, const QString &themeBaseDir)
415{
416 d->searchPaths.append(appname + QStringLiteral("/pics"));
417 d->addAppThemes(appname, themeBaseDir);
418}
419
420void KIconLoaderPrivate::addAppThemes(const QString &appname, const QString &themeBaseDir)
421{
422 KIconTheme *def = new KIconTheme(QStringLiteral("hicolor"), appname, themeBaseDir);
423 if (!def->isValid()) {
424 delete def;
425 def = new KIconTheme(KIconTheme::defaultThemeName(), appname, themeBaseDir);
426 }
427 KIconThemeNode *node = new KIconThemeNode(def);
428 bool addedToLinks = false;
429
430 if (!mThemesInTree.contains(appname)) {
431 mThemesInTree.append(appname);
432 links.append(node);
433 addedToLinks = true;
434 }
435 addBaseThemes(node, appname);
436
437 if (!addedToLinks) {
438 // Nodes in links are being deleted later - this one needs manual care.
439 delete node;
440 }
441}
442
443void KIconLoaderPrivate::addBaseThemes(KIconThemeNode *node, const QString &appname)
444{
445 // Quote from the icon theme specification:
446 // The lookup is done first in the current theme, and then recursively
447 // in each of the current theme's parents, and finally in the
448 // default theme called "hicolor" (implementations may add more
449 // default themes before "hicolor", but "hicolor" must be last).
450 //
451 // So we first make sure that all inherited themes are added, then we
452 // add the KDE default theme as fallback for all icons that might not be
453 // present in an inherited theme, and hicolor goes last.
454
455 addInheritedThemes(node, appname);
456 addThemeByName(QIcon::fallbackThemeName(), appname);
457 addThemeByName(QStringLiteral("hicolor"), appname);
458}
459
460void KIconLoaderPrivate::addInheritedThemes(KIconThemeNode *node, const QString &appname)
461{
462 const QStringList inheritedThemes = node->theme->inherits();
463
464 for (const auto &inheritedTheme : inheritedThemes) {
465 if (inheritedTheme == QLatin1String("hicolor")) {
466 // The icon theme spec says that "hicolor" must be the very last
467 // of all inherited themes, so don't add it here but at the very end
468 // of addBaseThemes().
469 continue;
470 }
471 addThemeByName(inheritedTheme, appname);
472 }
473}
474
475void KIconLoaderPrivate::addThemeByName(const QString &themename, const QString &appname)
476{
477 if (mThemesInTree.contains(themename + appname)) {
478 return;
479 }
480 KIconTheme *theme = new KIconTheme(themename, appname);
481 if (!theme->isValid()) {
482 delete theme;
483 return;
484 }
485 KIconThemeNode *n = new KIconThemeNode(theme);
486 mThemesInTree.append(themename + appname);
487 links.append(n);
488 addInheritedThemes(n, appname);
489}
490
491void KIconLoaderPrivate::addExtraDesktopThemes()
492{
493 if (extraDesktopIconsLoaded) {
494 return;
495 }
496
499 for (const auto &iconDir : icnlibs) {
500 QDir dir(iconDir);
501 if (!dir.exists()) {
502 continue;
503 }
504 const auto defaultEntries = dir.entryInfoList(QStringList(QStringLiteral("default.*")), QDir::Dirs);
505 for (const auto &defaultEntry : defaultEntries) {
506 if (!QFileInfo::exists(defaultEntry.filePath() + QLatin1String("/index.desktop")) //
507 && !QFileInfo::exists(defaultEntry.filePath() + QLatin1String("/index.theme"))) {
508 continue;
509 }
510 if (defaultEntry.isSymbolicLink()) {
511 const QString themeName = QDir(defaultEntry.symLinkTarget()).dirName();
512 if (!list.contains(themeName)) {
513 list.append(themeName);
514 }
515 }
516 }
517 }
518
519 for (const auto &theme : list) {
520 // Don't add the KDE defaults once more, we have them anyways.
521 if (theme == QLatin1String("default.kde") || theme == QLatin1String("default.kde4")) {
522 continue;
523 }
524 addThemeByName(theme, QLatin1String(""));
525 }
526
527 extraDesktopIconsLoaded = true;
528}
529
530void KIconLoader::drawOverlays(const QStringList &overlays, QPixmap &pixmap, KIconLoader::Group group, int state) const
531{
532 d->drawOverlays(this, group, state, pixmap, overlays);
533}
534
535QString KIconLoaderPrivate::removeIconExtension(const QString &name) const
536{
537 if (name.endsWith(QLatin1String(".png")) //
538 || name.endsWith(QLatin1String(".xpm")) //
539 || name.endsWith(QLatin1String(".svg"))) {
540 return name.left(name.length() - 4);
541 } else if (name.endsWith(QLatin1String(".svgz"))) {
542 return name.left(name.length() - 5);
543 }
544
545 return name;
546}
547
548void KIconLoaderPrivate::normalizeIconMetadata(KIconLoader::Group &group, QSize &size, int &state) const
549{
550 if ((state < 0) || (state >= KIconLoader::LastState)) {
551 qCWarning(KICONTHEMES) << "Invalid icon state:" << state << ", should be one of KIconLoader::States";
553 }
554
555 if (size.width() < 0 || size.height() < 0) {
556 size = {};
557 }
558
559 // For "User" icons, bail early since the size should be based on the size on disk,
560 // which we've already checked.
561 if (group == KIconLoader::User) {
562 return;
563 }
564
565 if ((group < -1) || (group >= KIconLoader::LastGroup)) {
566 qCWarning(KICONTHEMES) << "Invalid icon group:" << group << ", should be one of KIconLoader::Group";
567 group = KIconLoader::Desktop;
568 }
569
570 // If size == 0, use default size for the specified group.
571 if (size.isNull()) {
572 if (group < 0) {
573 qWarning() << "Neither size nor group specified!";
574 group = KIconLoader::Desktop;
575 }
576 size = QSize(mpGroups[group].size, mpGroups[group].size);
577 }
578}
579
580QString KIconLoaderPrivate::makeCacheKey(const QString &name,
581 KIconLoader::Group group,
582 const QStringList &overlays,
583 const QSize &size,
584 qreal scale,
585 int state,
586 const KIconColors &colors) const
587{
588 // The KSharedDataCache is shared so add some namespacing. The following code
589 // uses QStringBuilder (new in Qt 4.6)
590
591 QString effectKey = QStringLiteral("noeffect");
592
593 if ((group == KIconLoader::Desktop || group == KIconLoader::Panel) && state == KIconLoader::ActiveState) {
594 effectKey = QStringLiteral("active");
595 } else if (state == KIconLoader::DisabledState && group >= 0 && group < KIconLoader::LastGroup) {
596 effectKey = QStringLiteral("disabled");
597 }
598
599 /* clang-format off */
600 return (group == KIconLoader::User ? QLatin1String("$kicou_") : QLatin1String("$kico_"))
601 % name
602 % QLatin1Char('_')
603 % (size.width() == size.height() ? QString::number(size.height()) : QString::number(size.height()) % QLatin1Char('x') % QString::number(size.width()))
604 % QLatin1Char('@')
605 % QString::number(scale, 'f', 1)
606 % QLatin1Char('_')
607 % overlays.join(QLatin1Char('_'))
608 % effectKey
609 % QLatin1Char('_')
610 % paletteId(colors)
611 % (q->theme() && q->theme()->followsColorScheme() && state == KIconLoader::SelectedState ? QStringLiteral("_selected") : QString());
612 /* clang-format on */
613}
614
615QByteArray KIconLoaderPrivate::processSvg(const QString &path, KIconLoader::States state, const KIconColors &colors) const
616{
617 std::unique_ptr<QIODevice> device;
618
619 if (path.endsWith(QLatin1String("svgz"))) {
620 device.reset(new KCompressionDevice(path, KCompressionDevice::GZip));
621 } else {
622 device.reset(new QFile(path));
623 }
624
625 if (!device->open(QIODevice::ReadOnly)) {
626 return QByteArray();
627 }
628
629 const QString styleSheet = colors.stylesheet(state);
630 QByteArray processedContents;
631 QXmlStreamReader reader(device.get());
632
633 QBuffer buffer(&processedContents);
634 buffer.open(QIODevice::WriteOnly);
635 QXmlStreamWriter writer(&buffer);
636 while (!reader.atEnd()) {
637 if (reader.readNext() == QXmlStreamReader::StartElement //
638 && reader.qualifiedName() == QLatin1String("style") //
639 && reader.attributes().value(QLatin1String("id")) == QLatin1String("current-color-scheme")) {
640 writer.writeStartElement(QStringLiteral("style"));
641 writer.writeAttributes(reader.attributes());
642 writer.writeCharacters(styleSheet);
643 writer.writeEndElement();
644 while (reader.tokenType() != QXmlStreamReader::EndElement) {
645 reader.readNext();
646 }
647 } else if (reader.tokenType() != QXmlStreamReader::Invalid) {
648 writer.writeCurrentToken(reader);
649 }
650 }
651 buffer.close();
652
653 return processedContents;
654}
655
656QImage KIconLoaderPrivate::createIconImage(const QString &path, const QSize &size, qreal scale, KIconLoader::States state, const KIconColors &colors)
657{
658 // TODO: metadata in the theme to make it do this only if explicitly supported?
659 QImageReader reader;
660 QBuffer buffer;
661
662 if (q->theme() && q->theme()->followsColorScheme() && (path.endsWith(QLatin1String("svg")) || path.endsWith(QLatin1String("svgz")))) {
663 buffer.setData(processSvg(path, state, colors));
664 reader.setDevice(&buffer);
665 reader.setFormat("svg");
666 } else {
667 reader.setFileName(path);
668 }
669
670 if (!reader.canRead()) {
671 return QImage();
672 }
673
674 if (!size.isNull()) {
675 // ensure we keep aspect ratio
676 const QSize wantedSize = size * scale;
677 QSize finalSize(reader.size());
678 if (finalSize.isNull()) {
679 // nothing to scale
680 finalSize = wantedSize;
681 } else {
682 // like QSvgIconEngine::pixmap try to keep aspect ratio
683 finalSize.scale(wantedSize, Qt::KeepAspectRatio);
684 }
685 reader.setScaledSize(finalSize);
686 }
687
688 return reader.read();
689}
690
691void KIconLoaderPrivate::insertCachedPixmapWithPath(const QString &key, const QPixmap &data, const QString &path = QString())
692{
693 // Even if the pixmap is null, we add it to the caches so that we record
694 // the fact that whatever icon led to us getting a null pixmap doesn't
695 // exist.
696
697 PixmapWithPath *pixmapPath = new PixmapWithPath;
698 pixmapPath->pixmap = data;
699 pixmapPath->path = path;
700
701 mPixmapCache.insert(key, pixmapPath, data.width() * data.height() + 1);
702}
703
704bool KIconLoaderPrivate::findCachedPixmapWithPath(const QString &key, QPixmap &data, QString &path)
705{
706 // If the pixmap is present in our local process cache, use that since we
707 // don't need to decompress and upload it to the X server/graphics card.
708 const PixmapWithPath *pixmapPath = mPixmapCache.object(key);
709 if (pixmapPath) {
710 path = pixmapPath->path;
711 data = pixmapPath->pixmap;
712 return true;
713 }
714
715 return false;
716}
717
718QString KIconLoaderPrivate::findMatchingIconWithGenericFallbacks(const QString &name, int size, qreal scale) const
719{
720 QString path = findMatchingIcon(name, size, scale);
721 if (!path.isEmpty()) {
722 return path;
723 }
724
725 const QString genericIcon = s_globalData()->genericIconFor(name);
726 if (!genericIcon.isEmpty()) {
727 path = findMatchingIcon(genericIcon, size, scale);
728 }
729 return path;
730}
731
732QString KIconLoaderPrivate::findMatchingIcon(const QString &name, int size, qreal scale) const
733{
734 // This looks for the exact match and its
735 // generic fallbacks in each themeNode one after the other.
736
737 // In theory we should only do this for mimetype icons, not for app icons,
738 // but that would require different APIs. The long term solution is under
739 // development for Qt >= 5.8, QFileIconProvider calling QPlatformTheme::fileIcon,
740 // using QMimeType::genericIconName() to get the proper -x-generic fallback.
741 // Once everyone uses that to look up mimetype icons, we can kill the fallback code
742 // from this method.
743
744 bool genericFallback = name.endsWith(QLatin1String("-x-generic"));
745 bool isSymbolic = name.endsWith(QLatin1String("-symbolic"));
747 for (KIconThemeNode *themeNode : std::as_const(links)) {
748 QString currentName = name;
749
750 while (!currentName.isEmpty()) {
751 path = themeNode->theme->iconPathByName(currentName, size, KIconLoader::MatchBest, scale);
752 if (!path.isEmpty()) {
753 return path;
754 }
755
756 if (genericFallback) {
757 // we already tested the base name
758 break;
759 }
760
761 // If the icon was originally symbolic, we want to keep that suffix at the end.
762 // The next block removes the last word including the -, "a-b-symbolic" will become "a-b"
763 // We remove it beforehand, "a-b-symbolic" now is "a-symbolic" and we'll add it back later.
764 if (isSymbolic) {
765 currentName.remove(QLatin1String("-symbolic"));
766
767 // Handle cases where the icon lacks a symbolic version.
768 // For example, "knotes-symbolic" doesn't exist and has no fallback or generic version.
769 // "knotes" does exist, so let's check if a non-symbolic icon works before continuing.
770 path = themeNode->theme->iconPathByName(currentName, size, KIconLoader::MatchBest, scale);
771 if (!path.isEmpty()) {
772 return path;
773 }
774 }
775
776 int rindex = currentName.lastIndexOf(QLatin1Char('-'));
777 if (rindex > 1) { // > 1 so that we don't split x-content or x-epoc
778 currentName.truncate(rindex);
779
780 if (currentName.endsWith(QLatin1String("-x"))) {
781 currentName.chop(2);
782 }
783
784 // Add back the -symbolic if requested
785 if (isSymbolic) {
786 currentName += QLatin1String("-symbolic");
787 }
788 } else {
789 // From update-mime-database.c
790 static const QSet<QString> mediaTypes = QSet<QString>{QStringLiteral("text"),
791 QStringLiteral("application"),
792 QStringLiteral("image"),
793 QStringLiteral("audio"),
794 QStringLiteral("inode"),
795 QStringLiteral("video"),
796 QStringLiteral("message"),
797 QStringLiteral("model"),
798 QStringLiteral("multipart"),
799 QStringLiteral("x-content"),
800 QStringLiteral("x-epoc")};
801 // Shared-mime-info spec says:
802 // "If [generic-icon] is not specified then the mimetype is used to generate the
803 // generic icon by using the top-level media type (e.g. "video" in "video/ogg")
804 // and appending "-x-generic" (i.e. "video-x-generic" in the previous example)."
805 if (mediaTypes.contains(currentName)) {
806 currentName += QLatin1String("-x-generic");
807 genericFallback = true;
808 } else {
809 break;
810 }
811 }
812 }
813 }
814
815 if (path.isEmpty()) {
816 const QStringList fallbackPaths = QIcon::fallbackSearchPaths();
817
818 for (const QString &path : fallbackPaths) {
819 const QString extensions[] = {QStringLiteral(".png"), QStringLiteral(".svg"), QStringLiteral(".svgz"), QStringLiteral(".xpm")};
820
821 for (const QString &ext : extensions) {
822 const QString file = path + '/' + name + ext;
823
824 if (QFileInfo::exists(file)) {
825 return file;
826 }
827 }
828 }
829 }
830
831 return path;
832}
833
834QString KIconLoaderPrivate::preferredIconPath(const QString &name)
835{
837
838 auto it = mIconAvailability.constFind(name);
839 const auto end = mIconAvailability.constEnd();
840
841 if (it != end && it.value().isEmpty() && !shouldCheckForUnknownIcons()) {
842 return path; // known to be unavailable
843 }
844
845 if (it != end) {
846 path = it.value();
847 }
848
849 if (path.isEmpty()) {
851 mIconAvailability.insert(name, path);
852 }
853
854 return path;
855}
856
857inline QString KIconLoaderPrivate::unknownIconPath(int size, qreal scale) const
858{
859 QString path = findMatchingIcon(QStringLiteral("unknown"), size, scale);
860 if (path.isEmpty()) {
861 qCDebug(KICONTHEMES) << "Warning: could not find \"unknown\" icon for size" << size << "at scale" << scale;
862 return QString();
863 }
864 return path;
865}
866
867QString KIconLoaderPrivate::locate(const QString &fileName)
868{
869 for (const QString &dir : std::as_const(searchPaths)) {
870 const QString path = dir + QLatin1Char('/') + fileName;
871 if (QDir(dir).isAbsolute()) {
872 if (QFileInfo::exists(path)) {
873 return path;
874 }
875 } else {
877 if (!fullPath.isEmpty()) {
878 return fullPath;
879 }
880 }
881 }
882 return QString();
883}
884
885// Finds the absolute path to an icon.
886
887QString KIconLoader::iconPath(const QString &_name, int group_or_size, bool canReturnNull) const
888{
889 return iconPath(_name, group_or_size, canReturnNull, 1 /*scale*/);
890}
891
892QString KIconLoader::iconPath(const QString &_name, int group_or_size, bool canReturnNull, qreal scale) const
893{
894 // we need to honor resource :/ paths and QDir::searchPaths => use QDir::isAbsolutePath, see bug 434451
895 if (_name.isEmpty() || QDir::isAbsolutePath(_name)) {
896 // we have either an absolute path or nothing to work with
897 return _name;
898 }
899
900 QString name = d->removeIconExtension(_name);
901
902 QString path;
903 if (group_or_size == KIconLoader::User) {
904 path = d->locate(name + QLatin1String(".png"));
905 if (path.isEmpty()) {
906 path = d->locate(name + QLatin1String(".svgz"));
907 }
908 if (path.isEmpty()) {
909 path = d->locate(name + QLatin1String(".svg"));
910 }
911 if (path.isEmpty()) {
912 path = d->locate(name + QLatin1String(".xpm"));
913 }
914 return path;
915 }
916
917 if (group_or_size >= KIconLoader::LastGroup) {
918 qCDebug(KICONTHEMES) << "Invalid icon group:" << group_or_size;
919 return path;
920 }
921
922 int size;
923 if (group_or_size >= 0) {
924 size = d->mpGroups[group_or_size].size;
925 } else {
926 size = -group_or_size;
927 }
928
929 if (_name.isEmpty()) {
930 if (canReturnNull) {
931 return QString();
932 } else {
933 return d->unknownIconPath(size, scale);
934 }
935 }
936
937 path = d->findMatchingIconWithGenericFallbacks(name, size, scale);
938
939 if (path.isEmpty()) {
940 // Try "User" group too.
941 path = iconPath(name, KIconLoader::User, true);
942 if (!path.isEmpty() || canReturnNull) {
943 return path;
944 }
945
946 return d->unknownIconPath(size, scale);
947 }
948 return path;
949}
950
952KIconLoader::loadMimeTypeIcon(const QString &_iconName, KIconLoader::Group group, int size, int state, const QStringList &overlays, QString *path_store) const
953{
954 QString iconName = _iconName;
955 const int slashindex = iconName.indexOf(QLatin1Char('/'));
956 if (slashindex != -1) {
957 iconName[slashindex] = QLatin1Char('-');
958 }
959
960 if (!d->extraDesktopIconsLoaded) {
961 const QPixmap pixmap = loadIcon(iconName, group, size, state, overlays, path_store, true);
962 if (!pixmap.isNull()) {
963 return pixmap;
964 }
965 d->addExtraDesktopThemes();
966 }
967 const QPixmap pixmap = loadIcon(iconName, group, size, state, overlays, path_store, true);
968 if (pixmap.isNull()) {
969 // Icon not found, fallback to application/octet-stream
970 return loadIcon(QStringLiteral("application-octet-stream"), group, size, state, overlays, path_store, false);
971 }
972 return pixmap;
973}
974
976 KIconLoader::Group group,
977 int size,
978 int state,
979 const QStringList &overlays,
980 QString *path_store,
981 bool canReturnNull) const
982{
983 return loadScaledIcon(_name, group, 1.0 /*scale*/, size, state, overlays, path_store, canReturnNull);
984}
985
987 KIconLoader::Group group,
988 qreal scale,
989 int size,
990 int state,
991 const QStringList &overlays,
992 QString *path_store,
993 bool canReturnNull) const
994{
995 return loadScaledIcon(_name, group, scale, QSize(size, size), state, overlays, path_store, canReturnNull);
996}
997
999 KIconLoader::Group group,
1000 qreal scale,
1001 const QSize &size,
1002 int state,
1003 const QStringList &overlays,
1004 QString *path_store,
1005 bool canReturnNull) const
1006{
1007 return loadScaledIcon(_name, group, scale, size, state, overlays, path_store, canReturnNull, {});
1008}
1009
1011 KIconLoader::Group group,
1012 qreal scale,
1013 const QSize &_size,
1014 int state,
1015 const QStringList &overlays,
1016 QString *path_store,
1017 bool canReturnNull,
1018 const std::optional<KIconColors> &colors) const
1019
1020{
1021 QString name = _name;
1022 bool favIconOverlay = false;
1023
1024 if (_size.width() < 0 || _size.height() < 0 || _name.isEmpty()) {
1025 return QPixmap();
1026 }
1027
1028 QSize size = _size;
1029
1030 /*
1031 * This method works in a kind of pipeline, with the following steps:
1032 * 1. Sanity checks.
1033 * 2. Convert _name, group, size, etc. to a key name.
1034 * 3. Check if the key is already cached.
1035 * 4. If not, initialize the theme and find/load the icon.
1036 * 4a Apply overlays
1037 * 4b Re-add to cache.
1038 */
1039
1040 // Special case for absolute path icons.
1041 if (name.startsWith(QLatin1String("favicons/"))) {
1042 favIconOverlay = true;
1044 }
1045
1046 // we need to honor resource :/ paths and QDir::searchPaths => use QDir::isAbsolutePath, see bug 434451
1047 const bool absolutePath = QDir::isAbsolutePath(name);
1048 if (!absolutePath) {
1049 name = d->removeIconExtension(name);
1050 }
1051
1052 // Don't bother looking for an icon with no name.
1053 if (name.isEmpty()) {
1054 return QPixmap();
1055 }
1056
1057 // May modify group, size, or state. This function puts them into sane
1058 // states.
1059 d->normalizeIconMetadata(group, size, state);
1060
1061 // See if the image is already cached.
1062 auto usedColors = colors ? *colors : d->mCustomColors ? d->mColors : KIconColors(qApp->palette());
1063 QString key = d->makeCacheKey(name, group, overlays, size, scale, state, usedColors);
1064 QPixmap pix;
1065
1066 bool iconWasUnknown = false;
1067 QString path;
1068
1069 if (d->findCachedPixmapWithPath(key, pix, path)) {
1070 if (path_store) {
1071 *path_store = path;
1072 }
1073
1074 if (!path.isEmpty()) {
1075 return pix;
1076 } else {
1077 // path is empty for "unknown" icons, which should be searched for
1078 // anew regularly
1079 if (!d->shouldCheckForUnknownIcons()) {
1080 return canReturnNull ? QPixmap() : pix;
1081 }
1082 }
1083 }
1084
1085 // Image is not cached... go find it and apply effects.
1086
1087 favIconOverlay = favIconOverlay && std::min(size.height(), size.width()) > 22;
1088
1089 // First we look for non-User icons. If we don't find one we'd search in
1090 // the User space anyways...
1091 if (group != KIconLoader::User) {
1092 if (absolutePath && !favIconOverlay) {
1093 path = name;
1094 } else {
1095 path = d->findMatchingIconWithGenericFallbacks(favIconOverlay ? QStringLiteral("text-html") : name, std::min(size.height(), size.width()), scale);
1096 }
1097 }
1098
1099 if (path.isEmpty()) {
1100 // We do have a "User" icon, or we couldn't find the non-User one.
1101 path = (absolutePath) ? name : iconPath(name, KIconLoader::User, canReturnNull);
1102 }
1103
1104 // Still can't find it? Use "unknown" if we can't return null.
1105 // We keep going in the function so we can ensure this result gets cached.
1106 if (path.isEmpty() && !canReturnNull) {
1107 path = d->unknownIconPath(std::min(size.height(), size.width()), scale);
1108 iconWasUnknown = true;
1109 }
1110
1111 QImage img;
1112 if (!path.isEmpty()) {
1113 img = d->createIconImage(path, size, scale, static_cast<KIconLoader::States>(state), usedColors);
1114 }
1115
1116 // apply effects. When changing the logic here also adapt makeCacheKey
1117 if ((group == KIconLoader::Desktop || group == KIconLoader::Panel) && state == KIconLoader::ActiveState) {
1119 }
1120
1121 if (state == KIconLoader::DisabledState && group >= 0 && group < KIconLoader::LastGroup) {
1123 }
1124
1125 if (favIconOverlay) {
1126 QImage favIcon(name, "PNG");
1127 if (!favIcon.isNull()) { // if favIcon not there yet, don't try to blend it
1128 QPainter p(&img);
1129
1130 // Align the favicon overlay
1131 QRect r(favIcon.rect());
1132 r.moveBottomRight(img.rect().bottomRight());
1133 r.adjust(-1, -1, -1, -1); // Move off edge
1134
1135 // Blend favIcon over img.
1136 p.drawImage(r, favIcon);
1137 }
1138 }
1139
1140 pix = QPixmap::fromImage(std::move(img));
1141 pix.setDevicePixelRatio(scale);
1142
1143 // TODO: If we make a loadIcon that returns the image we can convert
1144 // drawOverlays to use the image instead of pixmaps as well so we don't
1145 // have to transfer so much to the graphics card.
1146 d->drawOverlays(this, group, state, pix, overlays);
1147
1148 // Don't add the path to our unknown icon to the cache, only cache the
1149 // actual image.
1150 if (iconWasUnknown) {
1151 path.clear();
1152 }
1153
1154 d->insertCachedPixmapWithPath(key, pix, path);
1155
1156 if (path_store) {
1157 *path_store = path;
1158 }
1159
1160 return pix;
1161}
1162
1163#if KICONTHEMES_BUILD_DEPRECATED_SINCE(6, 5)
1164QMovie *KIconLoader::loadMovie(const QString &name, KIconLoader::Group group, int size, QObject *parent) const
1165{
1166 QString file = moviePath(name, group, size);
1167 if (file.isEmpty()) {
1168 return nullptr;
1169 }
1170 int dirLen = file.lastIndexOf(QLatin1Char('/'));
1171 const QString icon = iconPath(name, size ? -size : group, true);
1172 if (!icon.isEmpty() && file.left(dirLen) != icon.left(dirLen)) {
1173 return nullptr;
1174 }
1175 QMovie *movie = new QMovie(file, QByteArray(), parent);
1176 if (!movie->isValid()) {
1177 delete movie;
1178 return nullptr;
1179 }
1180 return movie;
1181}
1182#endif
1183
1184#if KICONTHEMES_BUILD_DEPRECATED_SINCE(6, 5)
1186{
1187 if (!d->mpGroups) {
1188 return QString();
1189 }
1190
1191 if ((group < -1 || group >= KIconLoader::LastGroup) && group != KIconLoader::User) {
1192 qCDebug(KICONTHEMES) << "Invalid icon group:" << group << ", should be one of KIconLoader::Group";
1193 group = KIconLoader::Desktop;
1194 }
1195 if (size == 0 && group < 0) {
1196 qCDebug(KICONTHEMES) << "Neither size nor group specified!";
1197 group = KIconLoader::Desktop;
1198 }
1199
1200 QString file = name + QStringLiteral(".mng");
1201 if (group == KIconLoader::User) {
1202 file = d->locate(file);
1203 } else {
1204 if (size == 0) {
1205 size = d->mpGroups[group].size;
1206 }
1207
1208 QString path;
1209
1210 for (KIconThemeNode *themeNode : std::as_const(d->links)) {
1211 path = themeNode->theme->iconPath(file, size, KIconLoader::MatchExact);
1212 if (!path.isEmpty()) {
1213 break;
1214 }
1215 }
1216
1217 if (path.isEmpty()) {
1218 for (KIconThemeNode *themeNode : std::as_const(d->links)) {
1219 path = themeNode->theme->iconPath(file, size, KIconLoader::MatchBest);
1220 if (!path.isEmpty()) {
1221 break;
1222 }
1223 }
1224 }
1225
1226 file = path;
1227 }
1228 return file;
1229}
1230#endif
1231
1232#if KICONTHEMES_BUILD_DEPRECATED_SINCE(6, 5)
1234{
1235 QStringList lst;
1236
1237 if (!d->mpGroups) {
1238 return lst;
1239 }
1240
1241 d->initIconThemes();
1242
1243 if ((group < -1) || (group >= KIconLoader::LastGroup)) {
1244 qCDebug(KICONTHEMES) << "Invalid icon group: " << group << ", should be one of KIconLoader::Group";
1245 group = KIconLoader::Desktop;
1246 }
1247 if ((size == 0) && (group < 0)) {
1248 qCDebug(KICONTHEMES) << "Neither size nor group specified!";
1249 group = KIconLoader::Desktop;
1250 }
1251
1252 QString file = name + QStringLiteral("/0001");
1253 if (group == KIconLoader::User) {
1254 file = d->locate(file + QStringLiteral(".png"));
1255 } else {
1256 if (size == 0) {
1257 size = d->mpGroups[group].size;
1258 }
1259 file = d->findMatchingIcon(file, size, 1); // FIXME scale
1260 }
1261 if (file.isEmpty()) {
1262 return lst;
1263 }
1264
1265 QString path = file.left(file.length() - 8);
1266 QDir dir(QFile::encodeName(path));
1267 if (!dir.exists()) {
1268 return lst;
1269 }
1270
1271 const auto entryList = dir.entryList();
1272 for (const QString &entry : entryList) {
1273 const QStringView chunk = QStringView(entry).left(4);
1274 if (!chunk.toUInt()) {
1275 continue;
1276 }
1277
1278 lst += path + entry;
1279 }
1280 lst.sort();
1281 return lst;
1282}
1283#endif
1284
1286{
1287 if (d->mpThemeRoot) {
1288 return d->mpThemeRoot->theme;
1289 }
1290 return nullptr;
1291}
1292
1294{
1295 if (!d->mpGroups) {
1296 return -1;
1297 }
1298
1299 if (group < 0 || group >= KIconLoader::LastGroup) {
1300 qCDebug(KICONTHEMES) << "Invalid icon group:" << group << ", should be one of KIconLoader::Group";
1301 return -1;
1302 }
1303 return d->mpGroups[group].size;
1304}
1305
1307{
1308 const QDir dir(iconsDir);
1309 const QStringList formats = QStringList() << QStringLiteral("*.png") << QStringLiteral("*.xpm") << QStringLiteral("*.svg") << QStringLiteral("*.svgz");
1310 const QStringList lst = dir.entryList(formats, QDir::Files);
1311 QStringList result;
1312 for (const auto &file : lst) {
1313 result += iconsDir + QLatin1Char('/') + file;
1314 }
1315 return result;
1316}
1317
1319{
1320 QStringList result;
1321 if (group_or_size >= KIconLoader::LastGroup) {
1322 qCDebug(KICONTHEMES) << "Invalid icon group:" << group_or_size;
1323 return result;
1324 }
1325 int size;
1326 if (group_or_size >= 0) {
1327 size = d->mpGroups[group_or_size].size;
1328 } else {
1329 size = -group_or_size;
1330 }
1331
1332 for (KIconThemeNode *themeNode : std::as_const(d->links)) {
1333 themeNode->queryIconsByContext(&result, size, context);
1334 }
1335
1336 // Eliminate duplicate entries (same icon in different directories)
1337 QString name;
1338 QStringList res2;
1339 QStringList entries;
1340 for (const auto &icon : std::as_const(result)) {
1341 const int n = icon.lastIndexOf(QLatin1Char('/'));
1342 if (n == -1) {
1343 name = icon;
1344 } else {
1345 name = icon.mid(n + 1);
1346 }
1347 name = d->removeIconExtension(name);
1348 if (!entries.contains(name)) {
1349 entries += name;
1350 res2 += icon;
1351 }
1352 }
1353 return res2;
1354}
1355
1357{
1358 d->initIconThemes();
1359
1360 QStringList result;
1361 if (group_or_size >= KIconLoader::LastGroup) {
1362 qCDebug(KICONTHEMES) << "Invalid icon group:" << group_or_size;
1363 return result;
1364 }
1365 int size;
1366 if (group_or_size >= 0) {
1367 size = d->mpGroups[group_or_size].size;
1368 } else {
1369 size = -group_or_size;
1370 }
1371
1372 for (KIconThemeNode *themeNode : std::as_const(d->links)) {
1373 themeNode->queryIcons(&result, size, context);
1374 }
1375
1376 // Eliminate duplicate entries (same icon in different directories)
1377 QString name;
1378 QStringList res2;
1379 QStringList entries;
1380 for (const auto &icon : std::as_const(result)) {
1381 const int n = icon.lastIndexOf(QLatin1Char('/'));
1382 if (n == -1) {
1383 name = icon;
1384 } else {
1385 name = icon.mid(n + 1);
1386 }
1387 name = d->removeIconExtension(name);
1388 if (!entries.contains(name)) {
1389 entries += name;
1390 res2 += icon;
1391 }
1392 }
1393 return res2;
1394}
1395
1396// used by KIconDialog to find out which contexts to offer in a combobox
1398{
1399 for (KIconThemeNode *themeNode : std::as_const(d->links)) {
1400 if (themeNode->theme->hasContext(context)) {
1401 return true;
1402 }
1403 }
1404 return false;
1405}
1406
1407#if KICONTHEMES_BUILD_DEPRECATED_SINCE(6, 5)
1409{
1410 return &d->mpEffect;
1411}
1412#endif
1413
1415{
1416 QPixmap pix;
1417 if (QPixmapCache::find(QStringLiteral("unknown"), &pix)) { // krazy:exclude=iconnames
1418 return pix;
1419 }
1420
1421 const QString path = global()->iconPath(QStringLiteral("unknown"), KIconLoader::Small, true); // krazy:exclude=iconnames
1422 if (path.isEmpty()) {
1423 qCDebug(KICONTHEMES) << "Warning: Cannot find \"unknown\" icon.";
1424 pix = QPixmap(32, 32);
1425 } else {
1426 pix.load(path);
1427 QPixmapCache::insert(QStringLiteral("unknown"), pix); // krazy:exclude=iconnames
1428 }
1429
1430 return pix;
1431}
1432
1433bool KIconLoader::hasIcon(const QString &name) const
1434{
1435 return !d->preferredIconPath(name).isEmpty();
1436}
1437
1439{
1440 d->mCustomColors = true;
1441 d->mColors = KIconColors(palette);
1442}
1443
1445{
1446 return d->mCustomColors ? d->mPalette : QPalette();
1447}
1448
1450{
1451 d->mCustomColors = false;
1452}
1453
1455{
1456 return d->mCustomColors;
1457}
1458
1459/*** the global icon loader ***/
1460Q_GLOBAL_STATIC(KIconLoader, globalIconLoader)
1461
1463{
1464 return globalIconLoader();
1465}
1466
1468{
1469 if (global() == this) {
1471 }
1472
1475}
1476
1478{
1479 s_globalData->emitChange(g);
1480}
1481
1482#include <kiconengine.h>
1483QIcon KDE::icon(const QString &iconName, KIconLoader *iconLoader)
1484{
1485 return QIcon(new KIconEngine(iconName, iconLoader ? iconLoader : KIconLoader::global()));
1486}
1487
1488QIcon KDE::icon(const QString &iconName, const QStringList &overlays, KIconLoader *iconLoader)
1489{
1490 return QIcon(new KIconEngine(iconName, iconLoader ? iconLoader : KIconLoader::global(), overlays));
1491}
1492
1493QIcon KDE::icon(const QString &iconName, const KIconColors &colors, KIconLoader *iconLoader)
1494{
1495 return QIcon(new KIconEngine(iconName, colors, iconLoader ? iconLoader : KIconLoader::global()));
1496}
1497
1498#include "kiconloader.moc"
1499#include "moc_kiconloader.cpp"
Sepecifies which colors will be used when recoloring icons as its stylesheet.
Definition kiconcolors.h:31
QString stylesheet(KIconLoader::States state) const
Applies effects to icons.
Definition kiconeffect.h:40
static void toDisabled(QImage &image)
Applies a disabled effect.
static void toActive(QImage &image)
Applies an effect for an icon that is in an 'active' state.
A class to provide rendering of KDE icons.
Definition kiconengine.h:29
Iconloader for KDE.
Definition kiconloader.h:73
QStringList queryIconsByContext(int group_or_size, KIconLoader::Context context=KIconLoader::Any) const
Queries all available icons for a specific context.
KIconLoader(const QString &appname=QString(), const QStringList &extraSearchPaths=QStringList(), QObject *parent=nullptr)
Constructs an iconloader.
Group
The group of the icon.
@ Small
Small icons, e.g. for buttons.
@ FirstGroup
First group.
@ Panel
Panel (Plasma Taskbar) icons.
@ User
User icons.
@ LastGroup
Last group.
@ Desktop
Desktop icons.
QStringList queryIcons(int group_or_size, KIconLoader::Context context=KIconLoader::Any) const
Queries all available icons for a specific group, having a specific context.
~KIconLoader() override
Cleanup.
QStringList searchPaths() const
Returns all the search paths for this icon loader, either absolute or relative to GenericDataLocation...
void resetPalette()
Resets the custom palette used by the KIconLoader to use the QGuiApplication::palette() again (and to...
bool hasContext(KIconLoader::Context context) const
States
Defines the possible states of an icon.
@ ActiveState
Icon is active.
@ DisabledState
Icon is disabled.
@ LastState
Last state (last constant)
@ DefaultState
The default state.
@ SelectedState
Icon is selected.
QString moviePath(const QString &name, KIconLoader::Group group, int size=0) const
Returns the path to an animated icon.
QMovie * loadMovie(const QString &name, KIconLoader::Group group, int size=0, QObject *parent=nullptr) const
Loads an animated icon.
QStringList queryIconsByDir(const QString &iconsDir) const
Returns a list of all icons (*.png or *.xpm extension) in the given directory.
int currentSize(KIconLoader::Group group) const
Returns the size of the specified icon group.
static KIconLoader * global()
Returns the global icon loader initialized with the application name.
QPixmap loadIcon(const QString &name, KIconLoader::Group group, int size=0, int state=KIconLoader::DefaultState, const QStringList &overlays=QStringList(), QString *path_store=nullptr, bool canReturnNull=false) const
Loads an icon.
void reconfigure(const QString &appname, const QStringList &extraSearchPaths=QStringList())
Reconfigure the icon loader, for instance to change the associated app name or extra search paths.
KIconTheme * theme() const
Returns a pointer to the current theme.
QPixmap loadMimeTypeIcon(const QString &iconName, KIconLoader::Group group, int size=0, int state=KIconLoader::DefaultState, const QStringList &overlays=QStringList(), QString *path_store=nullptr) const
Loads an icon for a mimetype.
static void emitChange(Group group)
Emits an iconChanged() signal on all the KIconLoader instances in the system indicating that a system...
bool hasCustomPalette() const
QPixmap loadScaledIcon(const QString &name, KIconLoader::Group group, qreal scale, int size=0, int state=KIconLoader::DefaultState, const QStringList &overlays=QStringList(), QString *path_store=nullptr, bool canReturnNull=false) const
Loads an icon.
void drawOverlays(const QStringList &overlays, QPixmap &pixmap, KIconLoader::Group group, int state=KIconLoader::DefaultState) const
Draws overlays on the specified pixmap, it takes the width and height of the pixmap into consideratio...
QStringList loadAnimated(const QString &name, KIconLoader::Group group, int size=0) const
Loads an animated icon as a series of still frames.
KICONTHEMES_EXPORT QIcon icon(const QString &iconName, KIconLoader *iconLoader=nullptr)
Returns a QIcon with an appropriate KIconEngine to perform loading and rendering.
void setCustomPalette(const QPalette &palette)
Sets the colors for this KIconLoader.
KIconEffect * iconEffect() const
Returns a pointer to the KIconEffect object used by the icon loader.
void addAppDir(const QString &appname, const QString &themeBaseDir=QString())
Adds appname to the list of application specific directories with themeBaseDir as its base directory.
QPalette customPalette() const
The colors that will be used for the svg stylesheet in case the loaded icons are svg-based,...
void newIconLoader()
Re-initialize the global icon loader.
Context
Defines the context of the icon.
Definition kiconloader.h:80
void iconLoaderSettingsChanged()
Emitted by newIconLoader once the new settings have been loaded.
MatchType
The type of a match.
@ MatchBest
Take the best match if there is no exact match.
@ MatchExact
Only try to find an exact match.
QString iconPath(const QString &name, int group_or_size, bool canReturnNull=false) const
Returns the path of an icon.
static QPixmap unknown()
Returns the unknown icon.
bool isValid() const
The icon theme exists?
QString internalName() const
The internal name of the icon theme (same as the name argument passed to the constructor).
static void reconfigure()
Reconfigure the theme.
QStringList queryIconsByContext(int size, KIconLoader::Context context=KIconLoader::Any) const
Query available icons for a context and preferred size.
static QString current()
Returns the current icon theme.
QString iconPath(const QString &name, int size, KIconLoader::MatchType match) const
Lookup an icon in the theme.
QStringList inherits() const
The themes this icon theme falls back on.
QStringList queryIcons(int size, KIconLoader::Context context=KIconLoader::Any) const
Query available icons for a size and context.
int defaultSize(KIconLoader::Group group) const
The default size of this theme for a certain icon group.
static QString defaultThemeName()
Returns the default icon theme.
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
KCALUTILS_EXPORT QString mimeType()
void init(KXmlGuiWindow *window, KGameDifficulty *difficulty=nullptr)
QString path(const QString &relativePath)
KIOCORE_EXPORT QString dir(const QString &fileClass)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QString name(StandardAction id)
KGuiItem clear()
const QList< QKeySequence > & end()
void setData(const QByteArray &data)
QRgb rgba() const const
bool connect(const QString &service, const QString &path, const QString &interface, const QString &name, QObject *receiver, const char *slot)
bool send(const QDBusMessage &message) const const
QDBusConnection sessionBus()
QDBusMessage createSignal(const QString &path, const QString &interface, const QString &name)
void setArguments(const QList< QVariant > &arguments)
QString dirName() const const
bool isAbsolutePath(const QString &path)
QByteArray encodeName(const QString &fileName)
bool exists() const const
iterator insert(const Key &key, const T &value)
T value(const Key &key) const const
QStringList fallbackSearchPaths()
QString fallbackThemeName()
QRect rect() const const
bool canRead() const const
QImage read()
void setDevice(QIODevice *device)
void setFileName(const QString &fileName)
void setFormat(const QByteArray &format)
void setScaledSize(const QSize &size)
QSize size() const const
void append(QList< T > &&value)
bool isEmpty() const const
QList< QMimeType > allMimeTypes() const const
bool isValid() const const
Q_EMITQ_EMIT
Q_OBJECTQ_OBJECT
Q_SIGNALSQ_SIGNALS
QObject * parent() const const
void setObjectName(QAnyStringView name)
qreal devicePixelRatio() const const
QPixmap fromImage(QImage &&image, Qt::ImageConversionFlags flags)
int height() const const
bool isNull() const const
bool load(const QString &fileName, const char *format, Qt::ImageConversionFlags flags)
void setDevicePixelRatio(qreal scaleFactor)
QSize size() const const
int width() const const
bool find(const Key &key, QPixmap *pixmap)
Key insert(const QPixmap &pixmap)
QPoint bottomRight() const const
bool contains(const QSet< T > &other) const const
int height() const const
bool isNull() const const
int width() const const
QString locate(StandardLocation type, const QString &fileName, LocateOptions options)
QStringList locateAll(StandardLocation type, const QString &fileName, LocateOptions options)
QString writableLocation(StandardLocation type)
void chop(qsizetype n)
void clear()
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
QString & insert(qsizetype position, QChar ch)
bool isEmpty() const const
qsizetype lastIndexOf(QChar ch, Qt::CaseSensitivity cs) const const
QString left(qsizetype n) const const
qsizetype length() const const
QString number(double n, char format, int precision)
QString & remove(QChar ch, Qt::CaseSensitivity cs)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
void truncate(qsizetype position)
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QString join(QChar separator) const const
void sort(Qt::CaseSensitivity cs)
QStringView left(qsizetype length) const const
uint toUInt(bool *ok, int base) const const
KeepAspectRatio
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 11 2024 12:18:57 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.