File indexing completed on 2025-03-09 04:10:27

0001 /*
0002  *  SPDX-FileCopyrightText: 2017 Eugene Ingerman
0003  *  SPDX-FileCopyrightText: 2022 L. E. Segovia <amy@amyspark.me>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include "ToolReferenceImagesWidget.h"
0009 
0010 #include "ui_WdgToolOptions.h"
0011 
0012 #include <KoSelection.h>
0013 #include <KoShapeTransparencyCommand.h>
0014 #include <KoShapeKeepAspectRatioCommand.h>
0015 #include <kis_config.h>
0016 #include <kis_signals_blocker.h>
0017 #include <kis_signal_compressor.h>
0018 #include <KisReferenceImage.h>
0019 #include <KisSpinBoxI18nHelper.h>
0020 #include <kis_clipboard.h>
0021 
0022 #include <QApplication>
0023 #include <QStandardItemModel>
0024 
0025 #include "ToolReferenceImages.h"
0026 
0027 struct ToolReferenceImagesWidget::Private {
0028     Private(ToolReferenceImages *tool)
0029     : tool(tool)
0030     {
0031     }
0032 
0033     Ui_WdgToolOptions *ui {nullptr};
0034     ToolReferenceImages *tool {nullptr};
0035 };
0036 
0037 ToolReferenceImagesWidget::ToolReferenceImagesWidget(ToolReferenceImages *tool, KisCanvasResourceProvider */*provider*/, QWidget *parent)
0038     : QWidget(parent),
0039       d(new Private(tool))
0040 {
0041     d->ui = new Ui_WdgToolOptions();
0042     d->ui->setupUi(this);
0043 
0044     d->ui->opacitySlider->setRange(0, 100);
0045     d->ui->opacitySlider->setTextTemplates(
0046         i18nc("{n} is the number value, % is the percent sign", "Opacity: {n}%"),
0047         i18nc("{n} is the number value, % is the percent sign", "Opacity [*varies*]: {n}%"));
0048     d->ui->opacitySlider->setValueGetter(
0049         [](KoShape *s){ return 100.0 * (1.0 - s->transparency()); }
0050     );
0051 
0052     d->ui->saturationSlider->setRange(0, 100);
0053     d->ui->saturationSlider->setTextTemplates(
0054         i18nc("{n} is the number value, % is the percent sign", "Saturation: {n}%"),
0055         i18nc("{n} is the number value, % is the percent sign", "Saturation [*varies*]: {n}%"));
0056     d->ui->saturationSlider->setValueGetter(
0057         [](KoShape *s){
0058             auto *r = dynamic_cast<KisReferenceImage*>(s);
0059             KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(r, 0.0);
0060             return 100.0 * r->saturation();
0061         }
0062     );
0063 
0064     d->ui->bnAddReferenceImage->setToolTip(i18n("Add Reference Image"));
0065     d->ui->bnAddReferenceImage->setIcon(KisIconUtils::loadIcon("list-add"));
0066     d->ui->bnAddReferenceImage->setIconSize(QSize(16, 16));
0067 
0068 
0069     d->ui->bnDelete->setToolTip(i18n("Delete all Reference Images"));
0070     d->ui->bnDelete->setIcon(KisIconUtils::loadIcon("edit-delete"));
0071     d->ui->bnDelete->setIconSize(QSize(16, 16));
0072 
0073     d->ui->bnLoad->setToolTip(i18n("Load Reference Images Set"));
0074     d->ui->bnLoad->setIcon(KisIconUtils::loadIcon("folder"));
0075     d->ui->bnLoad->setIconSize(QSize(16, 16));
0076 
0077     d->ui->bnSave->setToolTip(i18n("Export Reference Images Set"));
0078     d->ui->bnSave->setIcon(KisIconUtils::loadIcon("document-save-16"));
0079     d->ui->bnSave->setIconSize(QSize(16, 16));
0080 
0081     d->ui->bnPasteReferenceImage->setToolTip(i18n("Paste Reference Image From System Clipboard"));
0082     d->ui->bnPasteReferenceImage->setIcon(KisIconUtils::loadIcon("edit-paste-16"));
0083     d->ui->bnPasteReferenceImage->setIconSize(QSize(16, 16));
0084     d->ui->bnPasteReferenceImage->setEnabled(KisClipboard::instance()->hasClip() || KisClipboard::instance()->hasUrls());
0085 
0086     connect(d->ui->bnAddReferenceImage, SIGNAL(clicked()), tool, SLOT(addReferenceImage()));
0087     connect(d->ui->bnPasteReferenceImage, SIGNAL(clicked()), tool, SLOT(pasteReferenceImage()));
0088 
0089     connect(KisClipboard::instance(), &KisClipboard::clipChanged, this, [&]() {
0090         d->ui->bnPasteReferenceImage->setEnabled(KisClipboard::instance()->hasClip() || KisClipboard::instance()->hasUrls());
0091     });
0092 
0093     connect(d->ui->bnDelete, SIGNAL(clicked()), tool, SLOT(removeAllReferenceImages()));
0094     connect(d->ui->bnSave, SIGNAL(clicked()), tool, SLOT(saveReferenceImages()));
0095     connect(d->ui->bnLoad, SIGNAL(clicked()), tool, SLOT(loadReferenceImages()));
0096 
0097     connect(d->ui->chkKeepAspectRatio, SIGNAL(stateChanged(int)), this, SLOT(slotKeepAspectChanged()));
0098 
0099     KisSignalCompressor *compressor = new KisSignalCompressor(100 /* ms */, KisSignalCompressor::POSTPONE, this);
0100     connect(compressor, SIGNAL(timeout()), this, SLOT(slotImageValuesChanged()));
0101 
0102     connect(d->ui->saturationSlider, SIGNAL(valueChanged(qreal)), compressor, SLOT(start()));
0103     connect(d->ui->opacitySlider, SIGNAL(valueChanged(qreal)), compressor, SLOT(start()));
0104 
0105     d->ui->referenceImageLocationCombobox->addItem(i18n("Embed to .KRA"));
0106     d->ui->referenceImageLocationCombobox->addItem(i18n("Link to Image"));
0107     connect(d->ui->referenceImageLocationCombobox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSaveLocationChanged(int)));
0108 
0109     updateVisibility(false); // no selection when we start
0110 }
0111 
0112 ToolReferenceImagesWidget::~ToolReferenceImagesWidget()
0113 {
0114     delete d->ui;
0115     d->ui = nullptr;
0116 }
0117 
0118 void ToolReferenceImagesWidget::selectionChanged(KoSelection *selection)
0119 {
0120     QList<KoShape*> shapes = selection->selectedEditableShapes();
0121 
0122     d->ui->opacitySlider->setSelection(shapes);
0123     d->ui->saturationSlider->setSelection(shapes);
0124 
0125     bool anyKeepingAspectRatio = false;
0126     bool anyNotKeepingAspectRatio = false;
0127     bool anyEmbedded = false;
0128     bool anyLinked = false;
0129     // bool anyNonLinkable = false;
0130     bool anySelected = selection->count() > 0;
0131 
0132     Q_FOREACH(KoShape *shape, shapes) {
0133         KisReferenceImage *reference = dynamic_cast<KisReferenceImage*>(shape);
0134 
0135         anyKeepingAspectRatio |= shape->keepAspectRatio();
0136         anyNotKeepingAspectRatio |= !shape->keepAspectRatio();
0137 
0138         if (reference) {
0139             anyEmbedded |= reference->embed();
0140             anyLinked |= !reference->embed();
0141             // anyNonLinkable |= !reference->hasLocalFile();
0142         }
0143     }
0144 
0145     KisSignalsBlocker blocker(
0146         d->ui->chkKeepAspectRatio,
0147         d->ui->referenceImageLocationCombobox
0148     );
0149 
0150     d->ui->chkKeepAspectRatio->setCheckState(
0151         (anyKeepingAspectRatio && anyNotKeepingAspectRatio) ? Qt::PartiallyChecked :
0152          anyKeepingAspectRatio ? Qt::Checked : Qt::Unchecked);
0153 
0154 
0155     // set save location combobox
0156     bool imagesEmbedded = anyEmbedded && !anyLinked;
0157     int comboBoxIndex = imagesEmbedded ? 0 : 1; // maps state to combobox index
0158     d->ui->referenceImageLocationCombobox->setCurrentIndex(comboBoxIndex);
0159 
0160 
0161     updateVisibility(anySelected);
0162 }
0163 
0164 void ToolReferenceImagesWidget::slotKeepAspectChanged()
0165 {
0166     KoSelection *selection = d->tool->koSelection();
0167     QList<KoShape*> shapes = selection->selectedEditableShapes();
0168 
0169     KUndo2Command *cmd =
0170             new KoShapeKeepAspectRatioCommand(shapes, d->ui->chkKeepAspectRatio->isChecked());
0171 
0172     d->tool->canvas()->addCommand(cmd);
0173 }
0174 
0175 void ToolReferenceImagesWidget::slotOpacitySliderChanged(qreal newOpacity)
0176 {
0177     QList<KoShape*> shapes = d->ui->opacitySlider->selection();
0178     if (shapes.isEmpty()) return;
0179 
0180     KUndo2Command *cmd =
0181         new KoShapeTransparencyCommand(shapes, 1.0 - newOpacity / 100.0);
0182 
0183     d->tool->canvas()->addCommand(cmd);
0184 }
0185 
0186 void ToolReferenceImagesWidget::slotSaturationSliderChanged(qreal newSaturation)
0187 {
0188     QList<KoShape*> shapes = d->ui->saturationSlider->selection();
0189     if (shapes.isEmpty()) return;
0190 
0191     KUndo2Command *cmd =
0192             new KisReferenceImage::SetSaturationCommand(shapes, newSaturation / 100.0);
0193 
0194     d->tool->canvas()->addCommand(cmd);
0195 }
0196 
0197 void ToolReferenceImagesWidget::slotSaveLocationChanged(int index)
0198 {
0199     KoSelection *selection = d->tool->koSelection();
0200     QList<KoShape*> shapes = selection->selectedEditableShapes();
0201 
0202 
0203     Q_FOREACH(KoShape *shape, shapes) {
0204         KisReferenceImage *reference = dynamic_cast<KisReferenceImage*>(shape);
0205         KIS_SAFE_ASSERT_RECOVER_RETURN(reference);
0206 
0207         if (index == 0) { // embed to KRA
0208             reference->setEmbed(true);
0209         } else { // link to file
0210             if (reference->hasLocalFile()) {
0211                 reference->setEmbed(false);
0212             } else {
0213                 //In the case no local file is found, switch back to embed file data.
0214                 d->ui->referenceImageLocationCombobox->setCurrentIndex(0);
0215             }
0216         }
0217     }
0218 }
0219 
0220 void ToolReferenceImagesWidget::slotImageValuesChanged()
0221 {
0222     slotSaturationSliderChanged(d->ui->saturationSlider->value());
0223     slotOpacitySliderChanged(d->ui->opacitySlider->value());
0224 }
0225 
0226 void ToolReferenceImagesWidget::updateVisibility(bool hasSelection)
0227 {
0228     // hide UI elements if nothing is selected.
0229     d->ui->referenceImageLocationCombobox->setVisible(hasSelection);
0230     d->ui->chkKeepAspectRatio->setVisible(hasSelection);
0231     d->ui->saveLocationLabel->setVisible(hasSelection);
0232     d->ui->opacitySlider->setVisible(hasSelection);
0233     d->ui->saturationSlider->setVisible(hasSelection);
0234 
0235     // show a label indicating that a selection is required to show options
0236     d->ui->referenceImageOptionsLabel->setVisible(!hasSelection);
0237 
0238     if (hasSelection) {
0239         KoSelection* selection = d->tool->koSelection();
0240         QList<KoShape*> shapes = selection->selectedEditableShapes();
0241         bool usesLocalFile = true;
0242 
0243         Q_FOREACH(KoShape *shape, shapes) {
0244             KisReferenceImage *reference = dynamic_cast<KisReferenceImage*>(shape);
0245 
0246             if (reference) {
0247                 usesLocalFile &= reference->hasLocalFile();
0248             }
0249         }
0250 
0251         QStandardItemModel* model = dynamic_cast<QStandardItemModel*>(d->ui->referenceImageLocationCombobox->model());
0252 
0253         if (model) {
0254             QStandardItem* item = model->item(1);
0255             item->setFlags(usesLocalFile ? item->flags() | Qt::ItemIsEnabled :
0256                                            item->flags() & ~Qt::ItemIsEnabled);
0257         }
0258     }
0259 }