Commit 64bea43e authored by Dmitry Kazakov's avatar Dmitry Kazakov
Browse files

Fixed removing a layer that has clones

Now all the clones reincarnate to usual paint layers so will even be
able to paint on them.

BUG:290380
parent 966108cb
......@@ -21,25 +21,73 @@
#include "kis_image.h"
#include <klocale.h>
#include "kis_layer.h"
#include "kis_clone_layer.h"
#include "kis_paint_layer.h"
KisImageLayerRemoveCommand::KisImageLayerRemoveCommand(KisImageWSP image, KisNodeSP layer)
KisImageLayerRemoveCommand::KisImageLayerRemoveCommand(KisImageWSP image, KisNodeSP node)
: KisImageCommand(i18nc("(qtundo-format)", "Remove Layer"), image)
{
m_layer = layer;
m_prevParent = layer->parent();
m_prevAbove = layer->prevSibling();
m_node = node;
m_prevParent = node->parent();
m_prevAbove = node->prevSibling();
}
void KisImageLayerRemoveCommand::redo()
{
UpdateTarget target(m_image, m_layer, m_image->bounds());
m_image->removeNode(m_layer);
processClones(m_node);
UpdateTarget target(m_image, m_node, m_image->bounds());
m_image->removeNode(m_node);
target.update();
}
void KisImageLayerRemoveCommand::undo()
{
m_image->addNode(m_layer, m_prevParent, m_prevAbove);
m_layer->setDirty(m_image->bounds());
m_image->addNode(m_node, m_prevParent, m_prevAbove);
restoreClones();
m_node->setDirty(m_image->bounds());
}
void KisImageLayerRemoveCommand::restoreClones()
{
while(!m_reincarnatedNodes.isEmpty()) {
KisCloneLayerSP clone = m_clonesList.takeLast();
KisNodeSP newNode = m_reincarnatedNodes.takeLast();
m_image->addNode(clone, newNode->parent(), newNode);
moveChildren(newNode, clone);
m_image->removeNode(newNode);
}
Q_ASSERT(m_clonesList.isEmpty());
Q_ASSERT(m_reincarnatedNodes.isEmpty());
}
void KisImageLayerRemoveCommand::processClones(KisNodeSP node)
{
KisLayerSP layer = dynamic_cast<KisLayer*>(node.data());
if(!layer->hasClones()) return;
foreach(KisCloneLayerWSP _clone, layer->registeredClones()) {
KisCloneLayerSP clone = _clone;
Q_ASSERT(clone);
KisNodeSP newNode = clone->reincarnateAsPaintLayer();
m_image->addNode(newNode, clone->parent(), clone);
moveChildren(clone, newNode);
m_image->removeNode(clone);
m_clonesList.append(clone);
m_reincarnatedNodes.append(newNode);
}
}
void KisImageLayerRemoveCommand::moveChildren(KisNodeSP src, KisNodeSP dst)
{
KisNodeSP child = src->firstChild();
while(child) {
m_image->moveNode(child, dst, dst->lastChild());
child = child->nextSibling();
}
}
......@@ -20,13 +20,15 @@
#ifndef KIS_IMAGE_LAYER_REMOVE_COMMAND_H_
#define KIS_IMAGE_LAYER_REMOVE_COMMAND_H_
#include <QList>
#include <krita_export.h>
#include "kis_types.h"
#include "kis_image_command.h"
/// The command for removing a layer
/// The command for removing a node
class KRITAIMAGE_EXPORT KisImageLayerRemoveCommand : public KisImageCommand
{
......@@ -35,18 +37,25 @@ public:
/**
* Constructor
* @param image The image the command will be working on.
* @param layer the layer to remove
* @param wasParent the parent of the layer
* @param wasAbove the layer above the layer
* @param node the node to remove
*/
KisImageLayerRemoveCommand(KisImageWSP image, KisNodeSP layer);
KisImageLayerRemoveCommand(KisImageWSP image, KisNodeSP node);
virtual void redo();
virtual void undo();
private:
KisNodeSP m_layer;
void restoreClones();
void processClones(KisNodeSP node);
KisNodeSP reincarnateClone(KisCloneLayerSP clone);
void moveChildren(KisNodeSP src, KisNodeSP dst);
private:
KisNodeSP m_node;
KisNodeSP m_prevParent;
KisNodeSP m_prevAbove;
QList<KisCloneLayerSP> m_clonesList;
QList<KisNodeSP> m_reincarnatedNodes;
};
#endif
......@@ -30,6 +30,7 @@
#include "kis_node_visitor.h"
#include "kis_processing_visitor.h"
#include "kis_clone_info.h"
#include "kis_paint_layer.h"
struct KisCloneLayer::Private
......@@ -78,6 +79,17 @@ KisCloneLayer::~KisCloneLayer()
delete m_d;
}
KisNodeSP KisCloneLayer::reincarnateAsPaintLayer() const
{
KisPaintDeviceSP newOriginal = new KisPaintDevice(*original());
KisPaintLayerSP newLayer = new KisPaintLayer(image(), name(), opacity(), newOriginal);
newLayer->setX(x());
newLayer->setY(y());
newLayer->setCompositeOp(compositeOpId());
newLayer->mergeNodeProperties(nodeProperties());
return newLayer;
}
bool KisCloneLayer::allowAsChild(KisNodeSP node) const
{
return node->inherits("KisMask");
......
......@@ -59,6 +59,14 @@ public:
return KisNodeSP(new KisCloneLayer(*this));
}
/**
* When the source layer of the clone is removed from the stack
* we should substitute the clone with a usual paint layer,
* because the source might become unreachable quite soon. This
* method builds a paint layer representation of this clone.
*/
KisNodeSP reincarnateAsPaintLayer() const;
bool allowAsChild(KisNodeSP) const;
KisPaintDeviceSP original() const;
......
......@@ -147,5 +147,42 @@ void KisCloneLayerTest::testOriginalRefresh()
QCOMPARE(root->projection()->exactBounds(), expectedRect);
}
#include "commands/kis_image_commands.h"
void KisCloneLayerTest::testRemoveSourceLayer()
{
KisImageSP image = createImage();
KisNodeSP root = image->root();
KisNodeSP group1 = groupLayer1(image);
/**
* We are checking that noone keeps a pointer to the
* source layer after it is deleted. Uncomment the first
* line to see how perfectly it crashed if removing the
* source directly through the node facade
*/
//image->removeNode(group1);
KUndo2Command *cmd = new KisImageLayerRemoveCommand(image, group1);
cmd->redo();
delete cmd;
// We are veeeery bad! Never do like this! >:)
qDebug() << "Ref. count:" << group1->refCount();
KisNodeWSP group1_wsp = group1;
KisNode *group1_ptr = group1.data();
group1 = 0;
if(group1_wsp.isValid()) {
group1_wsp = 0;
while(group1_ptr->refCount()) group1_ptr->deref();
delete group1_ptr;
}
// Are we crashing?
image->refreshGraph();
image->waitForDone();
}
QTEST_KDEMAIN(KisCloneLayerTest, GUI)
#include "kis_clone_layer_test.moc"
......@@ -30,6 +30,8 @@ private slots:
void testOriginalUpdates();
void testOriginalUpdatesOutOfBounds();
void testOriginalRefresh();
void testRemoveSourceLayer();
};
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment