KIconThemes

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

KDE's Doxygen guidelines are available online.