Commit ded5b957 authored by Dmitry Kazakov's avatar Dmitry Kazakov
Browse files

Fix merging invisible layers

They should be just deleted and not merged

BUG:359707
parent 5f35f446
......@@ -309,6 +309,35 @@ namespace KisLayerUtils {
const KisMetaData::MergeStrategy *m_strategy;
};
KeepNodesSelectedCommand::KeepNodesSelectedCommand(const KisNodeList &selectedBefore,
const KisNodeList &selectedAfter,
KisNodeSP activeBefore,
KisNodeSP activeAfter,
KisImageSP image,
bool finalize, KUndo2Command *parent)
: FlipFlopCommand(finalize, parent),
m_selectedBefore(selectedBefore),
m_selectedAfter(selectedAfter),
m_activeBefore(activeBefore),
m_activeAfter(activeAfter),
m_image(image)
{
}
void KeepNodesSelectedCommand::end() {
KisImageSignalType type;
if (isFinalizing()) {
type = ComplexNodeReselectionSignal(m_activeAfter, m_selectedAfter);
} else {
type = ComplexNodeReselectionSignal(m_activeBefore, m_selectedBefore);
}
m_image->signalRouter()->emitNotification(type);
}
KisLayerSP constructDefaultLayer(KisImageSP image) {
return new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace());
}
RemoveNodeHelper::~RemoveNodeHelper()
{
}
......@@ -323,6 +352,8 @@ namespace KisLayerUtils {
* the clone --- first, the source --- last.
*/
void RemoveNodeHelper::safeRemoveMultipleNodes(QList<KisNodeSP> nodes, KisImageSP image) {
const bool lastLayer = scanForLastLayer(image, nodes);
while (!nodes.isEmpty()) {
QList<KisNodeSP>::iterator it = nodes.begin();
......@@ -336,6 +367,14 @@ namespace KisLayerUtils {
}
}
}
if (lastLayer) {
KisLayerSP newLayer = constructDefaultLayer(image);
addCommandImpl(new KisImageLayerAddCommand(image, newLayer,
image->root(),
KisNodeSP(),
false, false));
}
}
bool RemoveNodeHelper::checkIsSourceForClone(KisNodeSP src, const QList<KisNodeSP> &nodes) {
......@@ -352,6 +391,58 @@ namespace KisLayerUtils {
return false;
}
bool RemoveNodeHelper::scanForLastLayer(KisImageWSP image, KisNodeList nodesToRemove) {
bool removeLayers = false;
Q_FOREACH(KisNodeSP nodeToRemove, nodesToRemove) {
if (dynamic_cast<KisLayer*>(nodeToRemove.data())) {
removeLayers = true;
break;
}
}
if (!removeLayers) return false;
bool lastLayer = true;
KisNodeSP node = image->root()->firstChild();
while (node) {
if (!nodesToRemove.contains(node) &&
dynamic_cast<KisLayer*>(node.data())) {
lastLayer = false;
break;
}
node = node->nextSibling();
}
return lastLayer;
}
SimpleRemoveLayers::SimpleRemoveLayers(const KisNodeList &nodes,
KisImageSP image,
const KisNodeList &selectedNodes,
KisNodeSP activeNode)
: m_nodes(nodes),
m_image(image),
m_selectedNodes(selectedNodes),
m_activeNode(activeNode)
{
}
void SimpleRemoveLayers::populateChildCommands() {
if (m_nodes.isEmpty()) return;
addCommand(new KisLayerUtils::KeepNodesSelectedCommand(m_selectedNodes, KisNodeList(),
m_activeNode, KisNodeSP(),
m_image, false));
safeRemoveMultipleNodes(m_nodes, m_image);
addCommand(new KisLayerUtils::KeepNodesSelectedCommand(m_selectedNodes, KisNodeList(),
m_activeNode, KisNodeSP(),
m_image, true));
}
void SimpleRemoveLayers::addCommandImpl(KUndo2Command *cmd) {
addCommand(cmd);
}
struct CleanUpNodes : private RemoveNodeHelper, public KisCommandUtils::AggregateCommand {
CleanUpNodes(MergeDownInfoBaseSP info, KisNodeSP putAfter)
: m_info(info), m_putAfter(putAfter) {}
......@@ -530,6 +621,10 @@ namespace KisLayerUtils {
KisLayerSP prevLayer = dynamic_cast<KisLayer*>(layer->prevSibling().data());
if (!prevLayer) return;
if (!layer->visible() && !prevLayer->visible()) {
return;
}
KisImageSignalVector emitSignals;
emitSignals << ModifiedSignal;
......@@ -538,30 +633,46 @@ namespace KisLayerUtils {
emitSignals,
kundo2_i18n("Merge Down"));
MergeDownInfoSP info(new MergeDownInfo(image, prevLayer, layer));
if (layer->visible() && prevLayer->visible()) {
MergeDownInfoSP info(new MergeDownInfo(image, prevLayer, layer));
applicator.applyCommand(new FillSelectionMasks(info));
applicator.applyCommand(new CreateMergedLayer(info), KisStrokeJobData::BARRIER);
applicator.applyCommand(new FillSelectionMasks(info));
applicator.applyCommand(new CreateMergedLayer(info), KisStrokeJobData::BARRIER);
if (info->frames.size() > 0) {
foreach (int frame, info->frames) {
applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage));
if (info->frames.size() > 0) {
foreach (int frame, info->frames) {
applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage));
applicator.applyCommand(new AddNewFrame(info, frame));
applicator.applyCommand(new AddNewFrame(info, frame));
applicator.applyCommand(new RefreshHiddenAreas(info));
applicator.applyCommand(new MergeLayers(info), KisStrokeJobData::BARRIER);
applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage));
}
} else {
applicator.applyCommand(new RefreshHiddenAreas(info));
applicator.applyCommand(new MergeLayers(info), KisStrokeJobData::BARRIER);
applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage));
}
} else {
applicator.applyCommand(new RefreshHiddenAreas(info));
applicator.applyCommand(new MergeLayers(info), KisStrokeJobData::BARRIER);
}
applicator.applyCommand(new MergeMetaData(info, strategy), KisStrokeJobData::BARRIER);
applicator.applyCommand(new CleanUpNodes(info, layer),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
applicator.applyCommand(new MergeMetaData(info, strategy), KisStrokeJobData::BARRIER);
applicator.applyCommand(new CleanUpNodes(info, layer),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
} else if (layer->visible()) {
applicator.applyCommand(
new SimpleRemoveLayers(KisNodeList() << prevLayer,
image,
KisNodeList() << layer, layer),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
} else if (prevLayer->visible()) {
applicator.applyCommand(
new SimpleRemoveLayers(KisNodeList() << layer,
image,
KisNodeList() << layer, layer),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
}
applicator.end();
}
......@@ -660,6 +771,33 @@ namespace KisLayerUtils {
return nodes;
}
KisNodeList filterInvisibleNodes(const KisNodeList &nodes, KisNodeList *invisibleNodes, KisNodeSP *putAfter) {
KIS_ASSERT_RECOVER(invisibleNodes) { return nodes; }
KIS_ASSERT_RECOVER(putAfter) { return nodes; }
KisNodeList visibleNodes;
int putAfterIndex = -1;
Q_FOREACH(KisNodeSP node, nodes) {
if (node->visible()) {
visibleNodes << node;
} else {
*invisibleNodes << node;
if (node == *putAfter) {
putAfterIndex = visibleNodes.size() - 1;
}
}
}
if (!visibleNodes.isEmpty() && putAfterIndex >= 0) {
putAfterIndex = qBound(0, putAfterIndex, visibleNodes.size() - 1);
*putAfter = visibleNodes[putAfterIndex];
}
return visibleNodes;
}
void mergeMultipleLayersImpl(KisImageSP image, QList<KisNodeSP> mergedNodes, KisNodeSP putAfter, bool flattenSingleLayer, const KUndo2MagicString &actionName)
{
filterMergableNodes(mergedNodes);
......@@ -681,31 +819,46 @@ namespace KisLayerUtils {
emitSignals,
actionName);
MergeMultipleInfoSP info(new MergeMultipleInfo(image, mergedNodes));
KisNodeList originalNodes = mergedNodes;
KisNodeList invisibleNodes;
mergedNodes = filterInvisibleNodes(originalNodes, &invisibleNodes, &putAfter);
if (!invisibleNodes.isEmpty()) {
applicator.applyCommand(
new SimpleRemoveLayers(invisibleNodes,
image,
KisNodeList(), KisNodeSP()),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
}
if (mergedNodes.size() > 1 || invisibleNodes.isEmpty()) {
MergeMultipleInfoSP info(new MergeMultipleInfo(image, mergedNodes));
applicator.applyCommand(new FillSelectionMasks(info));
applicator.applyCommand(new CreateMergedLayerMultiple(info), KisStrokeJobData::BARRIER);
applicator.applyCommand(new FillSelectionMasks(info));
applicator.applyCommand(new CreateMergedLayerMultiple(info), KisStrokeJobData::BARRIER);
if (info->frames.size() > 0) {
foreach (int frame, info->frames) {
applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage));
if (info->frames.size() > 0) {
foreach (int frame, info->frames) {
applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage));
applicator.applyCommand(new AddNewFrame(info, frame));
applicator.applyCommand(new RefreshHiddenAreas(info));
applicator.applyCommand(new MergeLayersMultiple(info), KisStrokeJobData::BARRIER);
applicator.applyCommand(new AddNewFrame(info, frame));
applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage));
}
} else {
applicator.applyCommand(new RefreshHiddenAreas(info));
applicator.applyCommand(new MergeLayersMultiple(info), KisStrokeJobData::BARRIER);
applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage));
}
} else {
applicator.applyCommand(new RefreshHiddenAreas(info));
applicator.applyCommand(new MergeLayersMultiple(info), KisStrokeJobData::BARRIER);
}
//applicator.applyCommand(new MergeMetaData(info, strategy), KisStrokeJobData::BARRIER);
applicator.applyCommand(new CleanUpNodes(info, putAfter),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
//applicator.applyCommand(new MergeMetaData(info, strategy), KisStrokeJobData::BARRIER);
applicator.applyCommand(new CleanUpNodes(info, putAfter),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
}
applicator.end();
......
......@@ -83,6 +83,31 @@ namespace KisLayerUtils
SharedStorageSP m_storage;
};
/**
* A command to keep correct set of selected/active nodes thoroughout
* the action.
*/
class KRITAIMAGE_EXPORT KeepNodesSelectedCommand : public KisCommandUtils::FlipFlopCommand
{
public:
KeepNodesSelectedCommand(const KisNodeList &selectedBefore,
const KisNodeList &selectedAfter,
KisNodeSP activeBefore,
KisNodeSP activeAfter,
KisImageSP image,
bool finalize, KUndo2Command *parent = 0);
void end();
private:
KisNodeList m_selectedBefore;
KisNodeList m_selectedAfter;
KisNodeSP m_activeBefore;
KisNodeSP m_activeAfter;
KisImageWSP m_image;
};
KRITAIMAGE_EXPORT KisLayerSP constructDefaultLayer(KisImageSP image);
class KRITAIMAGE_EXPORT RemoveNodeHelper {
public:
virtual ~RemoveNodeHelper();
......@@ -91,8 +116,28 @@ namespace KisLayerUtils
void safeRemoveMultipleNodes(QList<KisNodeSP> nodes, KisImageSP image);
private:
bool checkIsSourceForClone(KisNodeSP src, const QList<KisNodeSP> &nodes);
static bool scanForLastLayer(KisImageWSP image, KisNodeList nodesToRemove);
};
struct SimpleRemoveLayers : private KisLayerUtils::RemoveNodeHelper, public KisCommandUtils::AggregateCommand {
SimpleRemoveLayers(const KisNodeList &nodes,
KisImageSP image,
const KisNodeList &selectedNodes,
KisNodeSP activeNode);
void populateChildCommands();
protected:
virtual void addCommandImpl(KUndo2Command *cmd);
private:
KisNodeList m_nodes;
KisImageSP m_image;
KisNodeList m_selectedNodes;
KisNodeSP m_activeNode;
};
class KRITAIMAGE_EXPORT KisSimpleUpdateCommand : public KisCommandUtils::FlipFlopCommand
{
public:
......
......@@ -102,6 +102,7 @@
#include "commands_new/kis_set_layer_style_command.h"
#include "kis_post_execution_undo_adapter.h"
#include "kis_selection_mask.h"
#include "kis_layer_utils.h"
#include "KisSaveGroupVisitor.h"
......@@ -420,7 +421,7 @@ void KisLayerManager::addLayerCommon(KisNodeSP activeNode, KisLayerSP layer, boo
KisLayerSP KisLayerManager::constructDefaultLayer()
{
KisImageWSP image = m_view->image();
return new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace());
return KisLayerUtils::constructDefaultLayer(image);
}
KisLayerSP KisLayerManager::addLayer(KisNodeSP activeNode)
......
......@@ -225,46 +225,6 @@ private:
BatchMoveUpdateDataSP m_updateData;
};
/**
* A command to keep correct set of selected/active nodes thoroughout
* the action.
*/
class KeepNodesSelectedCommand : public KisCommandUtils::FlipFlopCommand
{
public:
KeepNodesSelectedCommand(const KisNodeList &selectedBefore,
const KisNodeList &selectedAfter,
KisNodeSP activeBefore,
KisNodeSP activeAfter,
KisImageSP image,
bool finalize, KUndo2Command *parent = 0)
: FlipFlopCommand(finalize, parent),
m_selectedBefore(selectedBefore),
m_selectedAfter(selectedAfter),
m_activeBefore(activeBefore),
m_activeAfter(activeAfter),
m_image(image)
{
}
void end() {
KisImageSignalType type;
if (isFinalizing()) {
type = ComplexNodeReselectionSignal(m_activeAfter, m_selectedAfter);
} else {
type = ComplexNodeReselectionSignal(m_activeBefore, m_selectedBefore);
}
m_image->signalRouter()->emitNotification(type);
}
private:
KisNodeList m_selectedBefore;
KisNodeList m_selectedAfter;
KisNodeSP m_activeBefore;
KisNodeSP m_activeAfter;
KisImageWSP m_image;
};
/**
* A command to activate newly created selection masks after any action
*/
......@@ -391,9 +351,9 @@ struct LowerRaiseLayer : public KisCommandUtils::AggregateCommand {
if (!newParent) return;
addCommand(new KeepNodesSelectedCommand(sortedNodes, sortedNodes,
m_activeNode, m_activeNode,
m_image, false));
addCommand(new KisLayerUtils::KeepNodesSelectedCommand(sortedNodes, sortedNodes,
m_activeNode, m_activeNode,
m_image, false));
KisNodeSP currentAbove = newAbove;
Q_FOREACH (KisNodeSP node, sortedNodes) {
......@@ -407,9 +367,9 @@ struct LowerRaiseLayer : public KisCommandUtils::AggregateCommand {
currentAbove = node;
}
addCommand(new KeepNodesSelectedCommand(sortedNodes, sortedNodes,
m_activeNode, m_activeNode,
m_image, true));
addCommand(new KisLayerUtils::KeepNodesSelectedCommand(sortedNodes, sortedNodes,
m_activeNode, m_activeNode,
m_image, true));
}
private:
......@@ -466,9 +426,9 @@ struct DuplicateLayers : public KisCommandUtils::AggregateCommand {
if (!newParent) return;
addCommand(new KeepNodesSelectedCommand(filteredNodes, KisNodeList(),
m_activeNode, KisNodeSP(),
m_image, false));
addCommand(new KisLayerUtils::KeepNodesSelectedCommand(filteredNodes, KisNodeList(),
m_activeNode, KisNodeSP(),
m_image, false));
if (haveActiveMasks) {
addCommand(new ActivateSelectionMasksCommand(activeMasks,
......@@ -533,9 +493,9 @@ struct DuplicateLayers : public KisCommandUtils::AggregateCommand {
KisNodeSP newActiveNode = newNodes[qBound(0, indexOfActiveNode, newNodes.size() - 1)];
addCommand(new KeepNodesSelectedCommand(KisNodeList(), newNodes,
KisNodeSP(), newActiveNode,
m_image, true));
addCommand(new KisLayerUtils::KeepNodesSelectedCommand(KisNodeList(), newNodes,
KisNodeSP(), newActiveNode,
m_image, true));
}
private:
KisSelectionMaskSP toActiveSelectionMask(KisNodeSP node) {
......@@ -588,58 +548,21 @@ struct RemoveLayers : private KisLayerUtils::RemoveNodeHelper, public KisCommand
m_updateData->addInitialUpdate(moveStruct);
}
const bool lastLayer = scanForLastLayer(m_image, filteredNodes);
addCommand(new KeepNodesSelectedCommand(filteredNodes, KisNodeList(),
m_activeNode, KisNodeSP(),
m_image, false));
addCommand(new KisLayerUtils::KeepNodesSelectedCommand(filteredNodes, KisNodeList(),
m_activeNode, KisNodeSP(),
m_image, false));
safeRemoveMultipleNodes(filteredNodes, m_image);
if (lastLayer) {
if (m_nodeManager) {
KisLayerSP newLayer = m_nodeManager->constructDefaultLayer();
addCommand(new KisImageLayerAddCommand(m_image, newLayer,
m_image->root(),
KisNodeSP(),
false, false));
} else {
qWarning() << "WARNING: we have deleted the last layer and the node manager is not accessible to create a new one!";
}
}
addCommand(new KeepNodesSelectedCommand(filteredNodes, KisNodeList(),
m_activeNode, KisNodeSP(),
m_image, true));
addCommand(new KisLayerUtils::KeepNodesSelectedCommand(filteredNodes, KisNodeList(),
m_activeNode, KisNodeSP(),
m_image, true));
}
protected:
virtual void addCommandImpl(KUndo2Command *cmd) {
addCommand(cmd);
}
static bool scanForLastLayer(KisImageWSP image, KisNodeList nodesToRemove) {
bool removeLayers = false;
Q_FOREACH(KisNodeSP nodeToRemove, nodesToRemove) {
if (dynamic_cast<KisLayer*>(nodeToRemove.data())) {
removeLayers = true;
break;
}
}
if (!removeLayers) return false;
bool lastLayer = true;
KisNodeSP node = image->root()->firstChild();
while (node) {
if (!nodesToRemove.contains(node) &&
dynamic_cast<KisLayer*>(node.data())) {
lastLayer = false;
break;
}
node = node->nextSibling();
}
return lastLayer;
}
private:
BatchMoveUpdateDataSP m_updateData;
KisNodeManager *m_nodeManager;
......
Supports Markdown
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