1 /*
2 * Copyright (c) 2019 Tusooa Zhu <tusooa@vista.aero>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include "KisDlgChangeCloneSource.h"
20
21 #include <kis_clone_layer.h>
22 #include <kis_image.h>
23 #include <kis_undo_adapter.h>
24 #include <kis_processing_applicator.h>
25 #include <KisImageSignals.h>
26 #include <kis_signals_blocker.h>
27
28 #include "KisViewManager.h"
29 #include "KisChangeCloneLayersCommand.h"
30
31 struct KisDlgChangeCloneSource::Private
32 {
PrivateKisDlgChangeCloneSource::Private33 Private(QList<KisCloneLayerSP> layers, KisViewManager *view)
34 : cloneLayers(layers)
35 , view(view)
36 , image(view->image())
37 , applicator(new KisProcessingApplicator(image, 0,
38 KisProcessingApplicator::NONE,
39 /* emitSignals = */ KisImageSignalVector() << ModifiedSignal,
40 kundo2_i18n("Change Clone Layers")))
41 , modified(false) {}
42
43 QList<KisCloneLayerSP> cloneLayers;
44 KisViewManager *view;
45 KisImageSP image;
46 QList<KisLayerSP> validTargets;
47 Ui::WdgChangeCloneSource ui;
48 QScopedPointer<KisProcessingApplicator> applicator;
49 bool modified;
50
51 void addToTargetListRecursively(KisNodeSP node, bool addSelf = true);
52 void filterOutAncestorsAndClonesRecursively(KisLayerSP layer);
53 KisLayerSP getSelectedTargetLayer();
54 KUndo2Command *createCommand(KisLayerSP targetLayer);
55 };
56
addToTargetListRecursively(KisNodeSP node,bool addSelf)57 void KisDlgChangeCloneSource::Private::addToTargetListRecursively(KisNodeSP node, bool addSelf)
58 {
59 if (!node) {
60 return;
61 }
62 if (addSelf) {
63 KisLayerSP layer(qobject_cast<KisLayer *>(node.data()));
64 if (layer) {
65 validTargets << layer;
66 }
67 }
68 for (KisNodeSP childNode = node->lastChild(); childNode; childNode = childNode->prevSibling()) {
69 KisLayerSP childLayer(qobject_cast<KisLayer *>(childNode.data()));
70 if (childLayer) {
71 addToTargetListRecursively(childLayer);
72 }
73 }
74
75 }
76
filterOutAncestorsAndClonesRecursively(KisLayerSP layer)77 void KisDlgChangeCloneSource::Private::filterOutAncestorsAndClonesRecursively(KisLayerSP layer)
78 {
79 validTargets.removeOne(layer);
80 // remove `layer` and its ancestors
81 KisLayerSP parent = qobject_cast<KisLayer *>(layer->parent().data());
82 if (parent) {
83 filterOutAncestorsAndClonesRecursively(parent);
84 }
85
86 // remove all clones of `layer`, and their ancestors
87 Q_FOREACH (KisCloneLayerSP clone, layer->registeredClones()) {
88 filterOutAncestorsAndClonesRecursively(clone);
89 }
90 }
91
getSelectedTargetLayer()92 KisLayerSP KisDlgChangeCloneSource::Private::getSelectedTargetLayer()
93 {
94 int index = ui.cmbSourceLayer->currentIndex();
95 if (index != -1) {
96 return validTargets.at(index);
97 } else {
98 return 0;
99 }
100 }
101
createCommand(KisLayerSP targetLayer)102 KUndo2Command *KisDlgChangeCloneSource::Private::createCommand(KisLayerSP targetLayer)
103 {
104 return new KisChangeCloneLayersCommand(cloneLayers, targetLayer);
105 }
106
KisDlgChangeCloneSource(QList<KisCloneLayerSP> layers,KisViewManager * view,QWidget * parent)107 KisDlgChangeCloneSource::KisDlgChangeCloneSource(QList<KisCloneLayerSP> layers, KisViewManager *view, QWidget *parent)
108 : KoDialog(parent)
109 , d(new Private(layers, view))
110 {
111 KIS_SAFE_ASSERT_RECOVER_RETURN(!layers.isEmpty());
112
113 connect(d->image.data(), &KisImage::sigStrokeCancellationRequested,
114 this, &KisDlgChangeCloneSource::reject);
115 connect(d->image.data(), &KisImage::sigUndoDuringStrokeRequested,
116 this, &KisDlgChangeCloneSource::reject);
117 connect(d->image.data(), &KisImage::sigStrokeEndRequested,
118 this, &KisDlgChangeCloneSource::accept);
119
120 setButtons(Ok | Cancel);
121 setDefaultButton(Ok);
122
123 QWidget *widget = new QWidget(this);
124 d->ui.setupUi(widget);
125 setMainWidget(widget);
126
127 connect(d->ui.cmbSourceLayer, QOverload<int>::of(&QComboBox::currentIndexChanged),
128 this, &KisDlgChangeCloneSource::slotCancelChangesAndSetNewTarget);
129
130 updateTargetLayerList();
131 }
132
~KisDlgChangeCloneSource()133 KisDlgChangeCloneSource::~KisDlgChangeCloneSource()
134 {
135 dbgUI << "dialog destroyed";
136 if (d->applicator) {
137 if (result() == QDialog::Accepted && d->modified) {
138 dbgUI << "Accepted";
139 d->applicator->end();
140 } else {
141 dbgUI << "Rejected";
142 d->applicator->cancel();
143 }
144 }
145 }
146
updateTargetLayerList()147 void KisDlgChangeCloneSource::updateTargetLayerList()
148 {
149 KisSignalsBlocker b(d->ui.cmbSourceLayer);
150
151 if (!d->image) {
152 return;
153 }
154 KisNodeSP root = d->image->root();
155 d->validTargets.clear();
156 d->addToTargetListRecursively(root, /* addSelf = */ false);
157
158 KisLayerSP commonCopyFrom(d->cloneLayers.first()->copyFrom());
159
160 Q_FOREACH (KisCloneLayerSP clone, d->cloneLayers) {
161 // filter out invalid targets:
162 // selected clone layers, their ancestors;
163 // the clone layers' registered clone, the clones' ancestors.
164 d->filterOutAncestorsAndClonesRecursively(clone);
165
166 // assume that clone->copyFrom() != 0
167 if (clone->copyFrom() != commonCopyFrom) {
168 commonCopyFrom = 0;
169 }
170 }
171
172 d->ui.cmbSourceLayer->clear();
173 Q_FOREACH (KisNodeSP node, d->validTargets) {
174 d->ui.cmbSourceLayer->addItem(node->name());
175 }
176
177 if (commonCopyFrom) {
178 d->ui.cmbSourceLayer->setCurrentIndex(d->validTargets.indexOf(commonCopyFrom));
179 } else {
180 d->ui.cmbSourceLayer->setCurrentIndex(-1);
181 }
182 }
183
slotCancelChangesAndSetNewTarget()184 void KisDlgChangeCloneSource::slotCancelChangesAndSetNewTarget()
185 {
186 KisLayerSP targetLayer = d->getSelectedTargetLayer();
187 if (targetLayer) {
188 d->applicator->applyCommand(d->createCommand(targetLayer));
189 d->modified = true;
190 }
191 }
192
193