File indexing completed on 2024-03-24 15:32:59

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 #include "kiconengine.h"
0009 
0010 #include "kiconloader_p.h"
0011 #include <kiconloader.h>
0012 
0013 #include "kiconcolors.h"
0014 #include <KIconTheme>
0015 #include <QFileInfo>
0016 #include <QPainter>
0017 #include <qscopeguard.h>
0018 
0019 class KIconEnginePrivate
0020 {
0021 public:
0022     QPointer<KIconLoader> mIconLoader;
0023     bool mCustomColors = false;
0024     KIconColors mColors;
0025     QString mActualIconName;
0026 };
0027 
0028 KIconEngine::KIconEngine(const QString &iconName, KIconLoader *iconLoader, const QStringList &overlays)
0029     : mIconName(iconName)
0030     , mOverlays(overlays)
0031     , d(new KIconEnginePrivate{iconLoader, false, {}, {}})
0032 {
0033 }
0034 
0035 KIconEngine::KIconEngine(const QString &iconName, KIconLoader *iconLoader)
0036     : mIconName(iconName)
0037     , d(new KIconEnginePrivate{iconLoader, false, {}, {}})
0038 {
0039 }
0040 
0041 KIconEngine::KIconEngine(const QString &iconName, const KIconColors &colors, KIconLoader *iconLoader)
0042     : mIconName(iconName)
0043     , d(new KIconEnginePrivate{iconLoader, true, colors, {}})
0044 {
0045 }
0046 
0047 KIconEngine::~KIconEngine()
0048 {
0049     delete d;
0050 }
0051 
0052 static inline int qIconModeToKIconState(QIcon::Mode mode)
0053 {
0054     switch (mode) {
0055     case QIcon::Normal:
0056         return KIconLoader::DefaultState;
0057     case QIcon::Active:
0058         return KIconLoader::ActiveState;
0059     case QIcon::Disabled:
0060         return KIconLoader::DisabledState;
0061     case QIcon::Selected:
0062         return KIconLoader::SelectedState;
0063     default:
0064         return KIconLoader::DefaultState;
0065     }
0066 }
0067 
0068 QSize KIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state)
0069 {
0070     return QIconEngine::actualSize(size, mode, state);
0071 }
0072 
0073 void KIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state)
0074 {
0075     if (!d->mIconLoader) {
0076         return;
0077     }
0078 
0079     const qreal dpr = painter->device()->devicePixelRatioF();
0080     const QPixmap pix = createPixmap(rect.size() * dpr, dpr, mode, state);
0081     painter->drawPixmap(rect, pix);
0082 }
0083 
0084 QPixmap KIconEngine::createPixmap(const QSize &size, qreal scale, QIcon::Mode mode, QIcon::State state)
0085 {
0086     Q_UNUSED(state)
0087 
0088     if (scale < 1) {
0089         scale = 1;
0090     }
0091 
0092     if (size.isEmpty()) {
0093         return QPixmap();
0094     }
0095 
0096     if (!d->mIconLoader) {
0097         QPixmap pm(size);
0098         pm.setDevicePixelRatio(scale);
0099         pm.fill(Qt::transparent);
0100         return pm;
0101     }
0102 
0103     const QSize scaledSize = size / scale;
0104 
0105     QString iconPath;
0106 
0107     const int kstate = qIconModeToKIconState(mode);
0108     QPixmap pix = d->mIconLoader->loadScaledIcon(mIconName,
0109                                                  KIconLoader::Desktop,
0110                                                  scale,
0111                                                  scaledSize,
0112                                                  kstate,
0113                                                  mOverlays,
0114                                                  &iconPath,
0115                                                  false,
0116                                                  d->mCustomColors ? std::make_optional(d->mColors) : std::nullopt);
0117 
0118     if (!iconPath.isEmpty() && !d->mActualIconName.isEmpty()) {
0119         d->mActualIconName = QFileInfo(iconPath).completeBaseName();
0120     }
0121 
0122     if (pix.size() == size) {
0123         return pix;
0124     }
0125 
0126     QPixmap pix2(size);
0127     pix2.setDevicePixelRatio(scale);
0128     pix2.fill(QColor(0, 0, 0, 0));
0129 
0130     QPainter painter(&pix2);
0131     painter.setRenderHint(QPainter::SmoothPixmapTransform);
0132     const QSizeF targetSize = pix.size().scaled(scaledSize, Qt::KeepAspectRatio);
0133     QRectF targetRect({0, 0}, targetSize);
0134     targetRect.moveCenter(QRectF(pix2.rect()).center() / scale);
0135     painter.drawPixmap(targetRect, pix, pix.rect());
0136 
0137     return pix2;
0138 }
0139 
0140 QPixmap KIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
0141 {
0142     return createPixmap(size, 1 /*scale*/, mode, state);
0143 }
0144 
0145 QString KIconEngine::iconName() const
0146 {
0147     if (!d->mActualIconName.isEmpty()) {
0148         return d->mActualIconName;
0149     }
0150 
0151     if (!d->mIconLoader) {
0152         return QString();
0153     }
0154 
0155     const QString iconPath = KIconLoaderPrivate::get(d->mIconLoader)->preferredIconPath(mIconName);
0156     if (iconPath.isEmpty()) {
0157         return QString();
0158     }
0159 
0160     d->mActualIconName = QFileInfo(iconPath).completeBaseName();
0161     return d->mActualIconName;
0162 }
0163 
0164 Q_GLOBAL_STATIC_WITH_ARGS(QList<QSize>,
0165                           sSizes,
0166                           (QList<QSize>() << QSize(16, 16) << QSize(22, 22) << QSize(32, 32) << QSize(48, 48) << QSize(64, 64) << QSize(128, 128)
0167                                           << QSize(256, 256)))
0168 
0169 QList<QSize> KIconEngine::availableSizes(QIcon::Mode mode, QIcon::State state) const
0170 {
0171     Q_UNUSED(mode);
0172     Q_UNUSED(state);
0173 
0174     if (!d->mIconLoader) {
0175         return QList<QSize>();
0176     }
0177 
0178     const bool found = d->mIconLoader->hasIcon(mIconName);
0179     return found ? *sSizes : QList<QSize>();
0180 }
0181 
0182 QString KIconEngine::key() const
0183 {
0184     return QStringLiteral("KIconEngine");
0185 }
0186 
0187 QIconEngine *KIconEngine::clone() const
0188 {
0189     return new KIconEngine(mIconName, d->mIconLoader, mOverlays);
0190 }
0191 
0192 bool KIconEngine::read(QDataStream &in)
0193 {
0194     in >> mIconName >> mOverlays;
0195     return true;
0196 }
0197 
0198 bool KIconEngine::write(QDataStream &out) const
0199 {
0200     out << mIconName << mOverlays;
0201     return true;
0202 }
0203 
0204 void KIconEngine::virtual_hook(int id, void *data)
0205 {
0206     if (id == QIconEngine::IsNullHook) {
0207         *reinterpret_cast<bool *>(data) = !d->mIconLoader || !d->mIconLoader->hasIcon(mIconName);
0208     }
0209     if (id == QIconEngine::ScaledPixmapHook) {
0210         auto *info = reinterpret_cast<ScaledPixmapArgument *>(data);
0211         info->pixmap = createPixmap(info->size, info->scale, info->mode, info->state);
0212         return;
0213     }
0214     QIconEngine::virtual_hook(id, data);
0215 }