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 }