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

KDE's Doxygen guidelines are available online.