KIconThemes

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

KDE's Doxygen guidelines are available online.