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

KDE's Doxygen guidelines are available online.