File indexing completed on 2024-12-22 04:13:13

0001 /*
0002  *  SPDX-FileCopyrightText: 2002 Patrick Julien <freak@codepimps.org>
0003  *  SPDX-FileCopyrightText: 2009 Sven Langkamp <sven.langkamp@gmail.com>
0004  *  SPDX-FileCopyrightText: 2011 Silvio Heinrich <plassy@web.de>
0005  *  SPDX-FileCopyrightText: 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
0006  *  SPDX-FileCopyrightText: 2011 José Luis Vergara <pentalis@gmail.com>
0007  *
0008  *  SPDX-License-Identifier: GPL-2.0-or-later
0009  */
0010 
0011 #include "kis_preset_chooser.h"
0012 
0013 #include <QVBoxLayout>
0014 #include <QPainter>
0015 #include <QAbstractItemDelegate>
0016 #include <QStyleOptionViewItem>
0017 #include <QSortFilterProxyModel>
0018 #include <KisResourceModel.h>
0019 #include <QApplication>
0020 
0021 #include <kis_config.h>
0022 #include <klocalizedstring.h>
0023 #include <KisKineticScroller.h>
0024 
0025 #include <KoIcon.h>
0026 #include <KisResourceItemChooser.h>
0027 #include <KisResourceItemChooserSync.h>
0028 #include <KisResourceItemListView.h>
0029 #include <KisResourceLocator.h>
0030 #include <KisResourceTypes.h>
0031 
0032 #include <brushengine/kis_paintop_settings.h>
0033 #include <brushengine/kis_paintop_preset.h>
0034 #include "kis_config_notifier.h"
0035 #include <kis_icon.h>
0036 #include <KisResourceModelProvider.h>
0037 #include <KisTagFilterResourceProxyModel.h>
0038 #include <KisResourceThumbnailCache.h>
0039 
0040 
0041 /// The resource item delegate for rendering the resource preview
0042 class KisPresetDelegate : public QAbstractItemDelegate
0043 {
0044 public:
0045     KisPresetDelegate(QObject * parent = 0)
0046         : QAbstractItemDelegate(parent)
0047         , m_showText(false)
0048         , m_useDirtyPresets(false) {}
0049 
0050     ~KisPresetDelegate() override {}
0051 
0052     /// reimplemented
0053     void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override;
0054 
0055     /// reimplemented
0056     QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex &) const override {
0057         return option.decorationSize;
0058     }
0059 
0060     void setShowText(bool showText) {
0061         m_showText = showText;
0062     }
0063 
0064     void setUseDirtyPresets(bool value) {
0065         m_useDirtyPresets = value;
0066     }
0067 
0068 private:
0069     bool m_showText;
0070     bool m_useDirtyPresets;
0071 };
0072 
0073 void KisPresetDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
0074 {
0075     painter->save();
0076     painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
0077     if (!(option.state & QStyle::State_Enabled)) {
0078         painter->setOpacity(0.2);
0079     }
0080 
0081     if (!index.isValid()) {
0082         painter->restore();
0083         return;
0084     }
0085 
0086     bool dirty = index.data(Qt::UserRole + KisAbstractResourceModel::Dirty).toBool();
0087 
0088     QImage preview = KisResourceThumbnailCache::instance()->getImage(index);
0089 
0090     if (preview.isNull()) {
0091         preview = QImage(512, 512, QImage::Format_RGB32);
0092         preview.fill(Qt::red);
0093     }
0094 
0095     qreal devicePixelRatioF = painter->device()->devicePixelRatioF();
0096 
0097     QRect paintRect = option.rect.adjusted(1, 1, -1, -1);
0098     if (!m_showText) {
0099         QImage previewHighDpi =
0100             KisResourceThumbnailCache::instance()->getImage(index,
0101                                                              paintRect.size() * devicePixelRatioF,
0102                                                              Qt::IgnoreAspectRatio,
0103                                                              Qt::SmoothTransformation);
0104         previewHighDpi.setDevicePixelRatio(devicePixelRatioF);
0105         painter->drawImage(paintRect.x(), paintRect.y(), previewHighDpi);
0106     }
0107     else {
0108         QSize pixSize(paintRect.height(), paintRect.height());
0109         QImage previewHighDpi = KisResourceThumbnailCache::instance()->getImage(index,
0110                                                                                  pixSize * devicePixelRatioF,
0111                                                                                  Qt::KeepAspectRatio,
0112                                                                                  Qt::SmoothTransformation);
0113         previewHighDpi.setDevicePixelRatio(devicePixelRatioF);
0114         painter->drawImage(paintRect.x(), paintRect.y(), previewHighDpi);
0115 
0116         // Put an asterisk after the preset if it is dirty. This will help in case the pixmap icon is too small
0117 
0118         QString dirtyPresetIndicator = QString("");
0119         if (m_useDirtyPresets && dirty) {
0120             dirtyPresetIndicator = QString("*");
0121         }
0122 
0123 //        qreal brushSize = metaData["paintopSize"].toReal();
0124 //        qDebug() << "brushsize" << brushSize;
0125 //        QString brushSizeText;
0126 //        // Disable displayed decimal precision beyond a certain brush size
0127 //        if (brushSize < 100) {
0128 //            brushSizeText = QString::number(brushSize, 'g', 3);
0129 //        }
0130 //        else {
0131 //            brushSizeText = QString::number(brushSize, 'f', 0);
0132 //        }
0133 
0134 //        painter->drawText(pixSize.width() + 10, option.rect.y() + option.rect.height() - 10, brushSizeText); // brush size
0135 
0136         QString presetDisplayName = index.data(Qt::UserRole + KisAbstractResourceModel::Name).toString().replace("_", " "); // don't need underscores that might be part of the file name
0137         painter->drawText(pixSize.width() + 10, option.rect.y() + option.rect.height() - 10, presetDisplayName.append(dirtyPresetIndicator));
0138 
0139     }
0140 
0141     if (m_useDirtyPresets && dirty) {
0142         const QIcon icon = KisIconUtils::loadIcon("dirty-preset");
0143         QPixmap pixmap = icon.pixmap(QSize(16,16));
0144         painter->drawPixmap(paintRect.x() + 3, paintRect.y() + 3, pixmap);
0145     }
0146 
0147     bool broken = false;
0148     QMap<QString, QVariant> metaData = index.data(Qt::UserRole + KisAbstractResourceModel::MetaData).value<QMap<QString, QVariant>>();
0149     QStringList requiredBrushes = metaData["dependent_resources_filenames"].toStringList();
0150     if (!requiredBrushes.isEmpty()) {
0151         KisAllResourcesModel *model = KisResourceModelProvider::resourceModel(ResourceType::Brushes);
0152         Q_FOREACH(const QString brushFile, requiredBrushes) {
0153             if (!model->resourceExists("", brushFile, "")) {
0154                 qWarning() << "dependent resource" << brushFile << "misses.";
0155                 broken = true;
0156             }
0157         }
0158     }
0159 
0160     if (broken) {
0161         const QIcon icon = KisIconUtils::loadIcon("broken-preset");
0162         icon.paint(painter, QRect(paintRect.x() + paintRect.height() - 25, paintRect.y() + paintRect.height() - 25, 25, 25));
0163     }
0164 
0165     if (option.state & QStyle::State_Selected) {
0166         painter->setCompositionMode(QPainter::CompositionMode_HardLight);
0167         painter->setOpacity(1.0);
0168         painter->fillRect(option.rect, option.palette.highlight());
0169 
0170         // highlight is not strong enough to pick out preset. draw border around it.
0171         painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
0172         painter->setPen(QPen(option.palette.highlight(), 4, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
0173         QRect selectedBorder = option.rect.adjusted(2 , 2, -2, -2); // constrict the rectangle so it doesn't bleed into other presets
0174         painter->drawRect(selectedBorder);
0175     }
0176 
0177     painter->restore();
0178 
0179 }
0180 
0181 KisPresetChooser::KisPresetChooser(QWidget *parent)
0182     : QWidget(parent)
0183 {
0184     setObjectName("KisPresetChooser");
0185 
0186     QVBoxLayout * layout = new QVBoxLayout(this);
0187     layout->setMargin(0);
0188 
0189     m_chooser = new KisResourceItemChooser(ResourceType::PaintOpPresets, false, this);
0190     m_chooser->setRowHeight(50);
0191     m_delegate = new KisPresetDelegate(this);
0192     m_chooser->setItemDelegate(m_delegate);
0193     m_chooser->setSynced(true);
0194     m_chooser->showImportExportBtns(false);
0195     layout->addWidget(m_chooser);
0196 
0197     connect(m_chooser, SIGNAL(resourceSelected(KoResourceSP )),
0198             this, SLOT(slotResourceWasSelected(KoResourceSP )));
0199 
0200     connect(m_chooser, SIGNAL(resourceSelected(KoResourceSP )),
0201             this, SIGNAL(resourceSelected(KoResourceSP )));
0202     connect(m_chooser, SIGNAL(resourceClicked(KoResourceSP )),
0203             this, SIGNAL(resourceClicked(KoResourceSP )));
0204 
0205     connect(m_chooser, &KisResourceItemChooser::listViewModeChanged, this, &KisPresetChooser::showHideBrushNames);
0206 
0207     m_mode = ViewMode::THUMBNAIL;
0208 
0209     connect(KisConfigNotifier::instance(), SIGNAL(configChanged()),
0210             SLOT(notifyConfigChanged()));
0211 
0212 
0213     notifyConfigChanged();
0214 }
0215 
0216 KisPresetChooser::~KisPresetChooser()
0217 {
0218 }
0219 
0220 void KisPresetChooser::setViewMode(KisPresetChooser::ViewMode mode)
0221 {
0222     m_mode = mode;
0223     updateViewSettings();
0224 }
0225 
0226 void KisPresetChooser::setViewModeToThumbnail()
0227 {
0228     setViewMode(KisPresetChooser::ViewMode::THUMBNAIL);
0229 }
0230 
0231 void KisPresetChooser::setViewModeToDetail()
0232 {
0233     setViewMode(KisPresetChooser::ViewMode::DETAIL);
0234 }
0235 
0236 void KisPresetChooser::notifyConfigChanged()
0237 {
0238     KisConfig cfg(true);
0239     m_delegate->setUseDirtyPresets(cfg.useDirtyPresets());
0240     setIconSize(cfg.presetIconSize());
0241 }
0242 
0243 void KisPresetChooser::slotResourceWasSelected(KoResourceSP resource)
0244 {
0245     m_currentPresetConnections.clear();
0246     if (!resource) return;
0247 
0248     KisPaintOpPresetSP preset = resource.dynamicCast<KisPaintOpPreset>();
0249     KIS_SAFE_ASSERT_RECOVER_RETURN(preset);
0250 
0251     m_currentPresetConnections.addUniqueConnection(
0252         preset->updateProxy(), SIGNAL(sigSettingsChanged()),
0253         this, SLOT(slotCurrentPresetChanged()));
0254 }
0255 
0256 void KisPresetChooser::slotCurrentPresetChanged()
0257 {
0258     KoResourceSP currentResource = m_chooser->currentResource();
0259     if (!currentResource) return;
0260 
0261     QModelIndex index = m_chooser->tagFilterModel()->indexForResource(currentResource);
0262     emit m_chooser->tagFilterModel()->dataChanged(index,
0263                                                index,
0264                                                {Qt::UserRole + KisAbstractResourceModel::Thumbnail});
0265 }
0266 
0267 void KisPresetChooser::updateViewSettings()
0268 {
0269     switch (m_mode) {
0270     case ViewMode::THUMBNAIL: {
0271         m_chooser->setListViewMode(ListViewMode::IconGrid);
0272         m_delegate->setShowText(false);
0273         break;
0274     }
0275     case ViewMode::DETAIL: {
0276         m_chooser->setListViewMode(ListViewMode::Detail);
0277         m_delegate->setShowText(true);
0278         break;
0279     }
0280     }
0281 }
0282 
0283 void KisPresetChooser::setCurrentResource(KoResourceSP resource)
0284 {
0285     m_chooser->setCurrentResource(resource);
0286 }
0287 
0288 KoResourceSP KisPresetChooser::currentResource() const
0289 {
0290     return m_chooser->currentResource();
0291 }
0292 
0293 void KisPresetChooser::showTaggingBar(bool show)
0294 {
0295     m_chooser->showTaggingBar(show);
0296 }
0297 
0298 KisResourceItemChooser *KisPresetChooser::itemChooser()
0299 {
0300     return m_chooser;
0301 }
0302 
0303 void KisPresetChooser::setPresetFilter(const QString& paintOpId)
0304 {
0305     QMap<QString, QVariant> metaDataFilter;
0306     if (!paintOpId.isEmpty()) { // empty means "all"
0307         metaDataFilter["paintopid"] = paintOpId;
0308     }
0309     m_chooser->tagFilterModel()->setMetaDataFilter(metaDataFilter);
0310     updateViewSettings();
0311 }
0312 
0313 void KisPresetChooser::setIconSize(int newSize)
0314 {
0315     KisResourceItemChooserSync* chooserSync = KisResourceItemChooserSync::instance();
0316     chooserSync->setBaseLength(newSize);
0317 }
0318 
0319 int KisPresetChooser::iconSize()
0320 {
0321     KisResourceItemChooserSync* chooserSync = KisResourceItemChooserSync::instance();
0322     return chooserSync->baseLength();
0323 }
0324 
0325 void KisPresetChooser::saveIconSize()
0326 {
0327     // save icon size
0328     if (KisConfig(true).presetIconSize() != iconSize()) {
0329         KisConfig(false).setPresetIconSize(iconSize());
0330     }
0331 }
0332 
0333 void KisPresetChooser::showHideBrushNames(ListViewMode newViewMode)
0334 {
0335     switch (newViewMode) {
0336     case ListViewMode::Detail: {
0337         m_delegate->setShowText(true);
0338         break;
0339     }
0340     default: {
0341         m_delegate->setShowText(false);
0342     }
0343     }
0344 }