Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_node_juggler_compressed.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <QHash>
10#include <QSharedPointer>
11#include <QPointer>
12#include <QMutexLocker>
13
14#include "kis_global.h"
15#include "kis_image.h"
20#include "kis_command_utils.h"
21#include "kis_layer_utils.h"
22#include "kis_node_manager.h"
23#include "kis_layer.h"
24#include "kis_generator_layer.h"
25#include "kis_selection_mask.h"
26
27
41 MoveNodeStruct(KisImageSP _image, KisNodeSP _node, KisNodeSP _parent, KisNodeSP _above)
42 : image(_image),
43 node(_node),
44 newParent(_parent),
45 newAbove(_above),
46 oldParent(_node->parent()),
47 oldAbove(_node->prevSibling()),
50 {
51 }
52
53 bool tryMerge(const MoveNodeStruct &rhs) {
54 if (rhs.node != node) return false;
55
56 bool result = true;
57
58 if (newParent == rhs.oldParent) {
59 // 'rhs' is newer
60 newParent = rhs.newParent;
61 newAbove = rhs.newAbove;
62 } else if (oldParent == rhs.newParent) {
63 // 'this' is newer
64 oldParent = rhs.oldParent;
65 oldAbove = rhs.oldAbove;
66 } else {
67 warnKrita << "MoveNodeStruct: Trying to merge unsequential moves!";
68 result = false;
69 }
70
71 return result;
72 }
73
83
93
98
103};
104
106typedef QHash<KisNodeSP, MoveNodeStructSP> MovedNodesHash;
107
108
123
124 QMutex m_mutex;
125
127
128public:
130 : m_parentJuggler(parentJuggler) {}
131
132private:
133
135 static void setSuppressRefresh(MoveNodeStructSP update, bool value) {
136 update->suppressNewParentRefresh = value;
137 }
139 return lhs->newParent < rhs->newParent;
140 }
141 };
142
144 static void setSuppressRefresh(MoveNodeStructSP update, bool value) {
145 update->suppressOldParentRefresh = value;
146 }
148 return lhs->oldParent < rhs->oldParent;
149 }
150 };
151
152 template <typename CollisionPolicy>
155 std::copy(hash->begin(), hash->end(), std::back_inserter(updates));
156 std::sort(updates.begin(), updates.end(), CollisionPolicy::compare);
157
158 auto rangeBegin = updates.begin();
159
160 while (rangeBegin != updates.end()) {
161 auto rangeEnd = std::upper_bound(rangeBegin, updates.end(), *rangeBegin, CollisionPolicy::compare);
162
163 for (auto it = rangeBegin; it != std::prev(rangeEnd); ++it) {
164 CollisionPolicy::setSuppressRefresh(*it, true);
165 }
166 CollisionPolicy::setSuppressRefresh(*std::prev(rangeEnd), false);
167
168 rangeBegin = rangeEnd;
169 }
170 }
171
173 resolveParentCollisionsImpl<NewParentCollisionPolicy>(hash);
174 resolveParentCollisionsImpl<OldParentCollisionPolicy>(hash);
175 }
176
177 static void addToHashLazy(MovedNodesHash *hash, MoveNodeStructSP moveStruct) {
178 if (hash->contains(moveStruct->node)) {
179 bool result = hash->value(moveStruct->node)->tryMerge(*moveStruct);
181 } else {
182 hash->insert(moveStruct->node, moveStruct);
183 }
184 }
185
186public:
187
189 QMutexLocker l(&m_mutex);
190
191 if (m_movedNodesInitial.isEmpty()) return;
192
193 MovedNodesHash::const_iterator it = m_movedNodesInitial.constBegin();
194 MovedNodesHash::const_iterator end = m_movedNodesInitial.constEnd();
195
196 for (; it != end; ++it) {
197 it.value()->doRedoUpdates();
199 }
200
202
203 m_movedNodesInitial.clear();
204 }
205
207 {
208 QMutexLocker l(&m_mutex);
211
212 // the juggler might directly forward the signal to processUnhandledUpdates,
213 // which would also like to get a lock, so we should release it beforehand
214 }
215 if (m_parentJuggler) {
216 Q_EMIT m_parentJuggler->requestUpdateAsyncFromCommand();
217 }
218 }
219
221 QMutexLocker l(&m_mutex);
222
223 if (m_movedNodesUpdated.isEmpty()) return;
224
225 MovedNodesHash::const_iterator it = m_movedNodesUpdated.constBegin();
226 MovedNodesHash::const_iterator end = m_movedNodesUpdated.constEnd();
227
228 for (; it != end; ++it) {
230 it.value()->doRedoUpdates();
231 } else {
232 it.value()->doUndoUpdates();
233 }
234 }
235 }
236};
237
239
245{
246public:
247 UpdateMovedNodesCommand(BatchMoveUpdateDataSP updateData, bool finalize, KUndo2Command *parent = 0)
248 : FlipFlopCommand(finalize, parent),
249 m_updateData(updateData)
250 {
251 }
252
253 void partB() override {
254 State currentState = getState();
255
256 if (currentState == FINALIZING && isFirstRedo()) {
262 m_updateData->processUnhandledUpdates();
263 } else {
270 m_updateData->emitFinalUpdates(currentState);
271 }
272 }
273private:
275};
276
281{
282public:
284 const QList<KisSelectionMaskSP> &activeAfter,
285 bool finalize, KUndo2Command *parent = 0)
286 : FlipFlopCommand(finalize, parent),
287 m_activeBefore(activeBefore),
288 m_activeAfter(activeAfter)
289 {
290 }
291
292 void partA() override {
293 QList<KisSelectionMaskSP> *newActiveMasks;
294
295 if (getState() == FINALIZING) {
296 newActiveMasks = &m_activeAfter;
297 } else {
298 newActiveMasks = &m_activeBefore;
299 }
300
301 Q_FOREACH (KisSelectionMaskSP mask, *newActiveMasks) {
302 mask->setActive(false);
303 }
304 }
305
306 void partB() override {
307 QList<KisSelectionMaskSP> *newActiveMasks;
308
309 if (getState() == FINALIZING) {
310 newActiveMasks = &m_activeAfter;
311 } else {
312 newActiveMasks = &m_activeBefore;
313 }
314
315 Q_FOREACH (KisSelectionMaskSP mask, *newActiveMasks) {
316 mask->setActive(true);
317 }
318 }
319
320private:
323};
324
325
331 KisImageSP image,
332 const KisNodeList &nodes,
333 KisNodeSP activeNode,
334 bool lower)
335 : m_updateData(updateData),
336 m_image(image),
337 m_nodes(nodes),
338 m_activeNode(activeNode),
339 m_lower (lower) {}
340
346
348 bool hasLayers = false;
349 bool hasMasks = false;
350
351 Q_FOREACH (KisNodeSP node, nodes) {
352 hasLayers |= bool(qobject_cast<KisLayer*>(node.data()));
353 hasMasks |= bool(qobject_cast<KisMask*>(node.data()));
354 }
355
356 return hasLayers && hasMasks ? Mixed :
357 hasLayers ? AllLayers :
358 AllMasks;
359 }
360
362 if (!parent->isEditable(false)) return false;
363
364 Q_FOREACH (KisNodeSP node, nodes) {
365 if (!parent->allowAsChild(node)) return false;
366 }
367
368 return true;
369 }
370
371 void populateChildCommands() override {
373 KisNodeSP headNode = m_lower ? sortedNodes.first() : sortedNodes.last();
374 const NodesType nodesType = getNodesType(sortedNodes);
375
376 KisNodeSP parent = headNode->parent();
377 KisNodeSP grandParent = parent ? parent->parent() : 0;
378
379 if (!parent->isEditable(false)) return;
380
381 KisNodeSP newAbove;
382 KisNodeSP newParent;
383
384 if (m_lower) {
385 KisNodeSP prevNode = headNode->prevSibling();
386
387 if (prevNode) {
388 if (allowsAsChildren(prevNode, sortedNodes) &&
389 !prevNode->collapsed()) {
390
391 newAbove = prevNode->lastChild();
392 newParent = prevNode;
393 } else {
394 newAbove = prevNode->prevSibling();
395 newParent = parent;
396 }
397 } else if ((nodesType == AllLayers && grandParent) ||
398 (nodesType == AllMasks && grandParent && grandParent->parent())) {
399 newAbove = parent->prevSibling();
400 newParent = grandParent;
401
402 } else if (nodesType == AllMasks &&
403 grandParent && !grandParent->parent() &&
404 (prevNode = parent->prevSibling()) &&
405 prevNode->inherits("KisLayer")) {
406
407 newAbove = prevNode->lastChild();
408 newParent = prevNode; // NOTE: this is an updated 'prevNode'!
409 }
410 } else {
411 KisNodeSP nextNode = headNode->nextSibling();
412
413 if (nextNode) {
414 if (allowsAsChildren(nextNode, sortedNodes) &&
415 !nextNode->collapsed()) {
416 newAbove = 0;
417 newParent = nextNode;
418 } else {
419 newAbove = nextNode;
420 newParent = parent;
421 }
422 } else if ((nodesType == AllLayers && grandParent) ||
423 (nodesType == AllMasks && grandParent && grandParent->parent())) {
424 newAbove = parent;
425 newParent = grandParent;
426
427 } else if (nodesType == AllMasks &&
428 grandParent && !grandParent->parent() &&
429 (nextNode = parent->nextSibling()) &&
430 nextNode->inherits("KisLayer")) {
431
432 newAbove = 0;
433 newParent = nextNode; // NOTE: this is an updated 'nextNode'!
434 }
435 }
436
437 if (!newParent ||
438 !newParent->isEditable(false)) return;
439
440 addCommand(new KisLayerUtils::KeepNodesSelectedCommand(sortedNodes, sortedNodes,
442 m_image, false));
443
444 KisNodeSP currentAbove = newAbove;
445 Q_FOREACH (KisNodeSP node, sortedNodes) {
446 if (node->parent() != newParent && !newParent->allowAsChild(node)) {
447 continue;
448 }
449
450 MoveNodeStructSP moveStruct = toQShared(new MoveNodeStruct(m_image, node, newParent, currentAbove));
451 addCommand(new KisImageLayerMoveCommand(m_image, node, newParent, currentAbove, false));
452 m_updateData->addInitialUpdate(moveStruct);
453 currentAbove = node;
454 }
455
456 addCommand(new KisLayerUtils::KeepNodesSelectedCommand(sortedNodes, sortedNodes,
458 m_image, true));
459 }
460
461private:
467};
468
470 enum Mode {
473 ADD
474 };
475
477 KisImageSP image,
478 const KisNodeList &nodes,
479 KisNodeSP dstParent,
480 KisNodeSP dstAbove,
481 KisNodeSP activeNode,
482 Mode mode)
483 : m_updateData(updateData),
484 m_image(image),
485 m_nodes(nodes),
486 m_dstParent(dstParent),
487 m_dstAbove(dstAbove),
488 m_activeNode(activeNode),
489 m_mode(mode) {}
490
491 void populateChildCommands() override {
493
494 if (filteredNodes.isEmpty()) return;
495
496 KisNodeSP newAbove = filteredNodes.last();
497
498 // make sure we don't add the new layer into a locked group
499 while (newAbove->parent() && !newAbove->parent()->isEditable(false)) {
500 newAbove = newAbove->parent();
501 }
502
503 KisNodeSP newParent = newAbove->parent();
504
505 // override parent if provided externally
506 if (m_dstParent) {
507 newAbove = m_dstAbove;
508 newParent = m_dstParent;
509 }
510
511 const int indexOfActiveNode = filteredNodes.indexOf(m_activeNode);
512 QList<KisSelectionMaskSP> activeMasks = findActiveSelectionMasks(filteredNodes);
513
514 // we will deactivate the masks before processing, so we should
515 // save their list in a convenient form
516 QSet<KisNodeSP> activeMaskNodes;
517 Q_FOREACH (KisSelectionMaskSP mask, activeMasks) {
518 activeMaskNodes.insert(mask);
519 }
520
521 const bool haveActiveMasks = !activeMasks.isEmpty();
522
523 if (!newParent) return;
524
527 m_image, false));
528
529 if (haveActiveMasks) {
541 false));
542 }
543
544 KisNodeList newNodes;
545 QList<KisSelectionMaskSP> newActiveMasks;
546 KisNodeSP currentAbove = newAbove;
547 Q_FOREACH (KisNodeSP node, filteredNodes) {
548 if (m_mode == COPY || m_mode == ADD) {
549 KisNodeSP newNode;
550
551 if (m_mode == COPY) {
552 newNode = node->clone();
554 } else {
555 newNode = node;
556 }
557
558 newNodes << newNode;
559 if (haveActiveMasks && activeMaskNodes.contains(node)) {
560 KisSelectionMaskSP mask = dynamic_cast<KisSelectionMask*>(newNode.data());
561 newActiveMasks << mask;
562 }
563
564 MoveNodeStructSP moveStruct = toQShared(new MoveNodeStruct(m_image, newNode, newParent, currentAbove));
565 m_updateData->addInitialUpdate(moveStruct);
566
568 newParent,
569 currentAbove,
570 false, false));
571
572 currentAbove = newNode;
573 } else if (m_mode == MOVE) {
574 KisNodeSP newNode = node;
575
576 newNodes << newNode;
577 if (haveActiveMasks && activeMaskNodes.contains(node)) {
578 KisSelectionMaskSP mask = dynamic_cast<KisSelectionMask*>(newNode.data());
579 newActiveMasks << mask;
580 }
581
582 MoveNodeStructSP moveStruct = toQShared(new MoveNodeStruct(m_image, newNode, newParent, currentAbove));
583 m_updateData->addInitialUpdate(moveStruct);
584
586 newParent,
587 currentAbove,
588 false));
589 currentAbove = newNode;
590 }
591 }
592
593
594 if (haveActiveMasks) {
600 newActiveMasks,
601 true));
602 }
603
604 KisNodeSP newActiveNode = newNodes[qBound(0, indexOfActiveNode, newNodes.size() - 1)];
605
607 KisNodeSP(), newActiveNode,
608 m_image, true));
609 }
610private:
612 KisSelectionMask *mask = dynamic_cast<KisSelectionMask*>(node.data());
613 return mask && mask->active() ? mask : 0;
614 }
615
616
619 foreach (KisNodeSP node, nodes) {
621 if (mask) {
622 masks << mask;
623 }
624 }
625 return masks;
626 }
627private:
635};
636
639 KisImageSP image,
640 const KisNodeList &nodes,
641 KisNodeSP activeNode)
642 : m_updateData(updateData),
643 m_image(image),
644 m_nodes(nodes),
645 m_activeNode(activeNode){}
646
647 void populateChildCommands() override {
648 KisNodeList filteredNodes = m_nodes;
649 KisLayerUtils::filterMergeableNodes(filteredNodes, true);
651
652 if (filteredNodes.isEmpty()) return;
653
654 Q_FOREACH (KisNodeSP node, filteredNodes) {
656 m_updateData->addInitialUpdate(moveStruct);
657 }
658
661 m_image, false));
662
663 safeRemoveMultipleNodes(filteredNodes, m_image);
664
667 m_image, true));
668 }
669protected:
670 void addCommandImpl(KUndo2Command *cmd) override {
671 addCommand(cmd);
672 }
673
674private:
679};
680
682{
683 Private(KisNodeJugglerCompressed *juggler, const KUndo2MagicString &_actionName, KisImageSP _image, KisNodeManager *_nodeManager, int _timeout)
684 : actionName(_actionName),
685 image(_image),
686 nodeManager(_nodeManager),
687 compressor(_timeout, KisSignalCompressor::FIRST_ACTIVE_POSTPONE_NEXT),
688 selfDestructionCompressor(3 * _timeout, KisSignalCompressor::POSTPONE),
689 updateData(new BatchMoveUpdateData(juggler)),
690 autoDelete(false),
691 isStarted(false)
692 {}
693
697 QScopedPointer<KisProcessingApplicator> applicator;
698
701
703
706};
707
709 : m_d(new Private(this, actionName, image, nodeManager, timeout))
710{
711
712 KisImageSignalVector emitSignals;
713
714 m_d->applicator.reset(
715 new KisProcessingApplicator(m_d->image, 0,
717 emitSignals,
718 actionName));
719 connect(this, SIGNAL(requestUpdateAsyncFromCommand()), SLOT(startTimers()));
720 connect(&m_d->compressor, SIGNAL(timeout()), SLOT(slotUpdateTimeout()));
721
722 connect(m_d->image, SIGNAL(sigStrokeCancellationRequested()), SLOT(slotEndStrokeRequested()));
723 connect(m_d->image, SIGNAL(sigUndoDuringStrokeRequested()), SLOT(slotUndoDuringStrokeRequested()));
724 connect(m_d->image, SIGNAL(sigStrokeEndRequestedActiveNodeFiltered()), SLOT(slotEndStrokeRequested()));
725 connect(m_d->image, SIGNAL(sigAboutToBeDeleted()), SLOT(slotImageAboutToBeDeleted()));
726
727 m_d->applicator->applyCommand(
728 new UpdateMovedNodesCommand(m_d->updateData, false));
729 m_d->isStarted = true;
730}
731
733{
734 KIS_ASSERT_RECOVER(!m_d->applicator) {
735 m_d->applicator->end();
736 m_d->applicator.reset();
737 }
738}
739
741{
742 return actionName == m_d->actionName;
743}
744
746{
747 KisNodeSP activeNode = m_d->nodeManager ? m_d->nodeManager->activeNode() : 0;
748
749 m_d->applicator->applyCommand(
750 new LowerRaiseLayer(m_d->updateData,
751 m_d->image,
752 nodes, activeNode, true),
754
755}
756
758{
759 KisNodeSP activeNode = m_d->nodeManager ? m_d->nodeManager->activeNode() : 0;
760
761 m_d->applicator->applyCommand(
762 new LowerRaiseLayer(m_d->updateData,
763 m_d->image,
764 nodes, activeNode, false),
766}
767
769{
770 KisNodeSP activeNode = m_d->nodeManager ? m_d->nodeManager->activeNode() : 0;
771
772 m_d->applicator->applyCommand(
773 new RemoveLayers(m_d->updateData,
774 m_d->image,
775 nodes, activeNode),
777}
778
780{
781 KisNodeSP activeNode = m_d->nodeManager ? m_d->nodeManager->activeNode() : 0;
782
783 m_d->applicator->applyCommand(
784 new DuplicateLayers(m_d->updateData,
785 m_d->image,
786 nodes,
787 KisNodeSP(), KisNodeSP(),
788 activeNode,
791}
792
794{
795 KisNodeSP activeNode = m_d->nodeManager ? m_d->nodeManager->activeNode() : 0;
796
797 m_d->applicator->applyCommand(
798 new DuplicateLayers(m_d->updateData,
799 m_d->image,
800 nodes,
801 dstParent, dstAbove,
802 activeNode,
805}
806
808{
809 KisNodeSP activeNode = m_d->nodeManager ? m_d->nodeManager->activeNode() : 0;
810
811 m_d->applicator->applyCommand(
812 new DuplicateLayers(m_d->updateData,
813 m_d->image,
814 nodes,
815 dstParent, dstAbove,
816 activeNode,
819}
820
822{
823 KisNodeSP activeNode = m_d->nodeManager ? m_d->nodeManager->activeNode() : 0;
824
825 m_d->applicator->applyCommand(
826 new DuplicateLayers(m_d->updateData,
827 m_d->image,
828 nodes,
829 dstParent, dstAbove,
830 activeNode,
833}
834
836{
837 m_d->applicator->applyCommand(new KisImageLayerMoveCommand(m_d->image, node, parent, above, false),
839
840 MoveNodeStructSP moveStruct = toQShared(new MoveNodeStruct(m_d->image, node, parent, above));
841
842 m_d->updateData->addInitialUpdate(moveStruct);
843}
844
846{
847 m_d->compressor.start();
848
849 if (m_d->autoDelete) {
850 m_d->selfDestructionCompressor.start();
851 }
852}
853
855{
856 // The juggler could have been already finished explicitly
857 // by slotEndStrokeRequested(). In such a case the final updates
858 // will be issued by the last command of the stroke.
859
860 if (!m_d->updateData || !m_d->isStarted) return;
861
862 BatchMoveUpdateDataSP updateData = m_d->updateData;
863
869 m_d->applicator->applyCommand(
871 [updateData] () {
872 updateData->processUnhandledUpdates();
873 return nullptr;
874 }));
875}
876
878{
879 if (!m_d->isStarted) return;
880
881 m_d->applicator->applyCommand(
882 new UpdateMovedNodesCommand(m_d->updateData, true));
883
884 m_d->applicator->end();
885 cleanup();
886}
887
889{
890 m_d->applicator.reset();
891 m_d->compressor.stop();
892 m_d->image.clear();
893 m_d->updateData.clear();
894 m_d->isStarted = false;
895
896 if (m_d->autoDelete) {
897 m_d->selfDestructionCompressor.stop();
898 this->deleteLater();
899 }
900}
901
903{
904 m_d->autoDelete = value;
905 connect(&m_d->selfDestructionCompressor, SIGNAL(timeout()), SLOT(end()));
906}
907
909{
910 if (!m_d->isStarted) return;
911 end();
912}
913
915{
916 if (!m_d->isStarted) return;
917
928 KisImageSP image = m_d->image;
929 end();
930 image->waitForDone();
931}
932
934{
935 if (!m_d->isStarted) return;
936
941 end();
942}
943
945{
946 return !m_d->isStarted;
947}
float value(const T *src, size_t ch)
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
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)
BatchMoveUpdateData(KisNodeJugglerCompressed *parentJuggler)
void emitFinalUpdates(KisCommandUtils::FlipFlopCommand::State state)
static void resolveParentCollisions(MovedNodesHash *hash)
The command for adding a layer.
The command for layer moves inside the layer stack.
void waitForDone()
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 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 lowerNode(const KisNodeList &nodes)
const QScopedPointer< Private > m_d
bool canMergeAction(const KUndo2MagicString &actionName)
void moveNode(KisNodeSP node, KisNodeSP parent, KisNodeSP above)
UpdateMovedNodesCommand(BatchMoveUpdateDataSP updateData, bool finalize, KUndo2Command *parent=0)
#define KIS_ASSERT_RECOVER(cond)
Definition kis_assert.h:55
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
#define warnKrita
Definition kis_debug.h:87
QSharedPointer< MoveNodeStruct > MoveNodeStructSP
QHash< KisNodeSP, MoveNodeStructSP > MovedNodesHash
QSharedPointer< BatchMoveUpdateData > BatchMoveUpdateDataSP
QSharedPointer< T > toQShared(T *ptr)
QList< KisNodeSP > KisNodeList
Definition kis_types.h:264
KisSharedPtr< KisNode > KisNodeSP
Definition kis_types.h:86
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)
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...
QScopedPointer< KisProcessingApplicator > applicator
Private(KisNodeJugglerCompressed *juggler, const KUndo2MagicString &_actionName, KisImageSP _image, KisNodeManager *_nodeManager, int _timeout)
KisNodeSP prevSibling() const
Definition kis_node.cpp:402
virtual KisNodeSP clone() const =0
KisNodeWSP parent
Definition kis_node.cpp:86
KisNodeSP lastChild() const
Definition kis_node.cpp:367
KisNodeSP nextSibling() const
Definition kis_node.cpp:408
virtual void setDirty()
Definition kis_node.cpp:577
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)
BatchMoveUpdateDataSP m_updateData
bool tryMerge(const MoveNodeStruct &rhs)
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)