10#include <QSharedPointer>
12#include <QMutexLocker>
67 warnKrita <<
"MoveNodeStruct: Trying to merge unsequential moves!";
136 update->suppressNewParentRefresh =
value;
139 return lhs->newParent < rhs->newParent;
145 update->suppressOldParentRefresh =
value;
148 return lhs->oldParent < rhs->oldParent;
152 template <
typename CollisionPolicy>
155 std::copy(hash->begin(), hash->end(), std::back_inserter(updates));
156 std::sort(updates.begin(), updates.end(), CollisionPolicy::compare);
158 auto rangeBegin = updates.begin();
160 while (rangeBegin != updates.end()) {
161 auto rangeEnd = std::upper_bound(rangeBegin, updates.end(), *rangeBegin, CollisionPolicy::compare);
163 for (
auto it = rangeBegin; it != std::prev(rangeEnd); ++it) {
164 CollisionPolicy::setSuppressRefresh(*it,
true);
166 CollisionPolicy::setSuppressRefresh(*std::prev(rangeEnd),
false);
168 rangeBegin = rangeEnd;
173 resolveParentCollisionsImpl<NewParentCollisionPolicy>(hash);
174 resolveParentCollisionsImpl<OldParentCollisionPolicy>(hash);
178 if (hash->contains(moveStruct->node)) {
179 bool result = hash->value(moveStruct->node)->tryMerge(*moveStruct);
182 hash->insert(moveStruct->node, moveStruct);
196 for (; it != end; ++it) {
197 it.value()->doRedoUpdates();
228 for (; it != end; ++it) {
230 it.value()->doRedoUpdates();
232 it.value()->doUndoUpdates();
348 bool hasLayers =
false;
349 bool hasMasks =
false;
352 hasLayers |= bool(qobject_cast<KisLayer*>(node.
data()));
353 hasMasks |= bool(qobject_cast<KisMask*>(node.
data()));
356 return hasLayers && hasMasks ?
Mixed :
362 if (!parent->isEditable(
false))
return false;
365 if (!parent->allowAsChild(node))
return false;
377 KisNodeSP grandParent = parent ? parent->parent() : 0;
379 if (!parent->isEditable(
false))
return;
392 newParent = prevNode;
397 }
else if ((nodesType ==
AllLayers && grandParent) ||
398 (nodesType ==
AllMasks && grandParent && grandParent->
parent())) {
399 newAbove = parent->prevSibling();
400 newParent = grandParent;
403 grandParent && !grandParent->
parent() &&
404 (prevNode = parent->prevSibling()) &&
405 prevNode->inherits(
"KisLayer")) {
408 newParent = prevNode;
417 newParent = nextNode;
422 }
else if ((nodesType ==
AllLayers && grandParent) ||
423 (nodesType ==
AllMasks && grandParent && grandParent->
parent())) {
425 newParent = grandParent;
428 grandParent && !grandParent->
parent() &&
429 (nextNode = parent->nextSibling()) &&
430 nextNode->inherits(
"KisLayer")) {
433 newParent = nextNode;
445 Q_FOREACH (
KisNodeSP node, sortedNodes) {
494 if (filteredNodes.isEmpty())
return;
496 KisNodeSP newAbove = filteredNodes.last();
500 newAbove = newAbove->
parent();
511 const int indexOfActiveNode = filteredNodes.indexOf(
m_activeNode);
516 QSet<KisNodeSP> activeMaskNodes;
518 activeMaskNodes.insert(mask);
521 const bool haveActiveMasks = !activeMasks.isEmpty();
523 if (!newParent)
return;
529 if (haveActiveMasks) {
547 Q_FOREACH (
KisNodeSP node, filteredNodes) {
552 newNode = node->
clone();
559 if (haveActiveMasks && activeMaskNodes.contains(node)) {
561 newActiveMasks << mask;
572 currentAbove = newNode;
577 if (haveActiveMasks && activeMaskNodes.contains(node)) {
579 newActiveMasks << mask;
589 currentAbove = newNode;
594 if (haveActiveMasks) {
604 KisNodeSP newActiveNode = newNodes[qBound(0, indexOfActiveNode, newNodes.size() - 1)];
613 return mask && mask->
active() ? mask : 0;
652 if (filteredNodes.isEmpty())
return;
654 Q_FOREACH (
KisNodeSP node, filteredNodes) {
709 : m_d(new
Private(this, actionName, image, nodeManager, timeout))
714 m_d->applicator.reset(
727 m_d->applicator->applyCommand(
729 m_d->isStarted =
true;
735 m_d->applicator->end();
736 m_d->applicator.reset();
742 return actionName ==
m_d->actionName;
747 KisNodeSP activeNode =
m_d->nodeManager ?
m_d->nodeManager->activeNode() : 0;
749 m_d->applicator->applyCommand(
752 nodes, activeNode,
true),
759 KisNodeSP activeNode =
m_d->nodeManager ?
m_d->nodeManager->activeNode() : 0;
761 m_d->applicator->applyCommand(
764 nodes, activeNode,
false),
770 KisNodeSP activeNode =
m_d->nodeManager ?
m_d->nodeManager->activeNode() : 0;
772 m_d->applicator->applyCommand(
781 KisNodeSP activeNode =
m_d->nodeManager ?
m_d->nodeManager->activeNode() : 0;
783 m_d->applicator->applyCommand(
795 KisNodeSP activeNode =
m_d->nodeManager ?
m_d->nodeManager->activeNode() : 0;
797 m_d->applicator->applyCommand(
809 KisNodeSP activeNode =
m_d->nodeManager ?
m_d->nodeManager->activeNode() : 0;
811 m_d->applicator->applyCommand(
823 KisNodeSP activeNode =
m_d->nodeManager ?
m_d->nodeManager->activeNode() : 0;
825 m_d->applicator->applyCommand(
842 m_d->updateData->addInitialUpdate(moveStruct);
847 m_d->compressor.start();
849 if (
m_d->autoDelete) {
850 m_d->selfDestructionCompressor.start();
860 if (!
m_d->updateData || !
m_d->isStarted)
return;
869 m_d->applicator->applyCommand(
872 updateData->processUnhandledUpdates();
879 if (!
m_d->isStarted)
return;
881 m_d->applicator->applyCommand(
884 m_d->applicator->end();
890 m_d->applicator.reset();
891 m_d->compressor.stop();
893 m_d->updateData.clear();
894 m_d->isStarted =
false;
896 if (
m_d->autoDelete) {
897 m_d->selfDestructionCompressor.stop();
905 connect(&
m_d->selfDestructionCompressor, SIGNAL(timeout()), SLOT(
end()));
910 if (!
m_d->isStarted)
return;
916 if (!
m_d->isStarted)
return;
935 if (!
m_d->isStarted)
return;
946 return !
m_d->isStarted;
float value(const T *src, size_t ch)
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
QList< KisSelectionMaskSP > m_activeBefore
QList< KisSelectionMaskSP > m_activeAfter
ActivateSelectionMasksCommand(const QList< KisSelectionMaskSP > &activeBefore, const QList< KisSelectionMaskSP > &activeAfter, bool finalize, KUndo2Command *parent=0)
static void resolveParentCollisionsImpl(MovedNodesHash *hash)
void addInitialUpdate(MoveNodeStructSP moveStruct)
QPointer< KisNodeJugglerCompressed > m_parentJuggler
static void addToHashLazy(MovedNodesHash *hash, MoveNodeStructSP moveStruct)
void processUnhandledUpdates()
BatchMoveUpdateData(KisNodeJugglerCompressed *parentJuggler)
MovedNodesHash m_movedNodesUpdated
void emitFinalUpdates(KisCommandUtils::FlipFlopCommand::State state)
MovedNodesHash m_movedNodesInitial
static void resolveParentCollisions(MovedNodesHash *hash)
The command for adding a layer.
The command for layer moves inside the layer stack.
void refreshGraphAsync(KisNodeSP root, const QVector< QRect > &rects, const QRect &cropRect, KisProjectionUpdateFlags flags=KisProjectionUpdateFlag::None) override
QRect bounds() const override
void safeRemoveMultipleNodes(KisNodeList nodes, KisImageSP image)
void slotImageAboutToBeDeleted()
void removeNode(const KisNodeList &nodes)
KisNodeJugglerCompressed(const KUndo2MagicString &actionName, KisImageSP image, KisNodeManager *nodeManager, int timeout)
void raiseNode(const KisNodeList &nodes)
void addNode(const KisNodeList &nodes, KisNodeSP dstParent, KisNodeSP dstAbove)
void duplicateNode(const KisNodeList &nodes)
void copyNode(const KisNodeList &nodes, KisNodeSP dstParent, KisNodeSP dstAbove)
void setAutoDelete(bool value)
~KisNodeJugglerCompressed() override
void requestUpdateAsyncFromCommand()
void slotEndStrokeRequested()
void lowerNode(const KisNodeList &nodes)
const QScopedPointer< Private > m_d
bool canMergeAction(const KUndo2MagicString &actionName)
void moveNode(KisNodeSP node, KisNodeSP parent, KisNodeSP above)
void slotUndoDuringStrokeRequested()
BatchMoveUpdateDataSP m_updateData
UpdateMovedNodesCommand(BatchMoveUpdateDataSP updateData, bool finalize, KUndo2Command *parent=0)
#define KIS_ASSERT_RECOVER(cond)
#define KIS_ASSERT_RECOVER_NOOP(cond)
QSharedPointer< MoveNodeStruct > MoveNodeStructSP
QHash< KisNodeSP, MoveNodeStructSP > MovedNodesHash
QSharedPointer< BatchMoveUpdateData > BatchMoveUpdateDataSP
QSharedPointer< T > toQShared(T *ptr)
QList< KisNodeSP > KisNodeList
KisSharedPtr< KisNode > KisNodeSP
void addCopyOfNameTag(KisNodeSP node)
void filterUnlockedNodes(KisNodeList &nodes)
KisNodeList sortAndFilterAnyMergeableNodesSafe(const KisNodeList &nodes, KisImageSP image)
void filterMergeableNodes(KisNodeList &nodes, bool allowMasks)
static void setSuppressRefresh(MoveNodeStructSP update, bool value)
static bool compare(MoveNodeStructSP lhs, MoveNodeStructSP rhs)
static void setSuppressRefresh(MoveNodeStructSP update, bool value)
static bool compare(MoveNodeStructSP lhs, MoveNodeStructSP rhs)
void populateChildCommands() override
BatchMoveUpdateDataSP m_updateData
DuplicateLayers(BatchMoveUpdateDataSP updateData, KisImageSP image, const KisNodeList &nodes, KisNodeSP dstParent, KisNodeSP dstAbove, KisNodeSP activeNode, Mode mode)
QList< KisSelectionMaskSP > findActiveSelectionMasks(KisNodeList nodes)
KisSelectionMaskSP toActiveSelectionMask(KisNodeSP node)
bool isEditable(bool checkVisibility=true) const
The AggregateCommand struct is a command with delayed initialization. On first redo() populateChildCo...
void addCommand(KUndo2Command *cmd)
FlipFlopCommand(State initialState, KUndo2Command *parent=0)
The LambdaCommand struct is a shorthand for creation of AggregateCommand commands using C++ lambda fe...
KUndo2MagicString actionName
KisSignalCompressor compressor
QScopedPointer< KisProcessingApplicator > applicator
KisSignalCompressor selfDestructionCompressor
KisNodeManager * nodeManager
Private(KisNodeJugglerCompressed *juggler, const KUndo2MagicString &_actionName, KisImageSP _image, KisNodeManager *_nodeManager, int _timeout)
BatchMoveUpdateDataSP updateData
KisNodeSP prevSibling() const
virtual KisNodeSP clone() const =0
KisNodeSP lastChild() const
KisNodeSP nextSibling() const
virtual bool allowAsChild(KisNodeSP) const =0
void setActive(bool active)
bool allowsAsChildren(KisNodeSP parent, KisNodeList nodes)
NodesType getNodesType(KisNodeList nodes)
LowerRaiseLayer(BatchMoveUpdateDataSP updateData, KisImageSP image, const KisNodeList &nodes, KisNodeSP activeNode, bool lower)
void populateChildCommands() override
BatchMoveUpdateDataSP m_updateData
bool tryMerge(const MoveNodeStruct &rhs)
bool suppressNewParentRefresh
bool suppressOldParentRefresh
MoveNodeStruct(KisImageSP _image, KisNodeSP _node, KisNodeSP _parent, KisNodeSP _above)
void addCommandImpl(KUndo2Command *cmd) override
void populateChildCommands() override
BatchMoveUpdateDataSP m_updateData
RemoveLayers(BatchMoveUpdateDataSP updateData, KisImageSP image, const KisNodeList &nodes, KisNodeSP activeNode)