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

KDE's Doxygen guidelines are available online.