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