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

KDE's Doxygen guidelines are available online.