File indexing completed on 2024-10-27 04:07:33

0001 /*
0002  *  SPDX-FileCopyrightText: 2011 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_dummies_facade_base.h"
0008 
0009 #include "kis_image.h"
0010 #include "kis_node_dummies_graph.h"
0011 #include "kis_layer_utils.h"
0012 #include <KisSynchronizedConnection.h>
0013 
0014 struct KisDummiesFacadeBase::Private
0015 {
0016 public:
0017     KisImageWSP image;
0018     KisNodeSP savedRootNode;
0019 
0020     KisSynchronizedConnection<KisNodeSP> activateNodeConnection;
0021     KisSynchronizedConnection<KisNodeSP> nodeChangedConnection;
0022     KisSynchronizedConnection<KisNodeSP,KisNodeSP,KisNodeSP> addNodeConnection;
0023     KisSynchronizedConnection<KisNodeSP> removeNodeConnection;
0024 
0025     /**
0026      * pendingNodeSet contains the set of nodes that will be present in the
0027      * dummies graph after all the synchronized events are processed by the GUI
0028      * thread. This set is used to reset the graph when the image changes its root
0029      * during the 'flatten' operation.
0030      */
0031     QList<KisNodeSP> pendingNodeSet;
0032     QMutex pendingNodeSetLock;
0033 };
0034 
0035 
0036 KisDummiesFacadeBase::KisDummiesFacadeBase(QObject *parent)
0037     : QObject(parent),
0038       m_d(new Private())
0039 {
0040     m_d->activateNodeConnection.connectOutputSlot(this, &KisDummiesFacadeBase::slotNodeActivationRequested);
0041     m_d->nodeChangedConnection.connectOutputSlot(this, &KisDummiesFacadeBase::slotNodeChanged);
0042     m_d->addNodeConnection.connectOutputSlot(this, &KisDummiesFacadeBase::slotContinueAddNode);
0043     m_d->removeNodeConnection.connectOutputSlot(this, &KisDummiesFacadeBase::slotContinueRemoveNode);
0044 }
0045 
0046 KisDummiesFacadeBase::~KisDummiesFacadeBase()
0047 {
0048     delete m_d;
0049 }
0050 
0051 void KisDummiesFacadeBase::setImage(KisImageWSP image)
0052 {
0053     if (m_d->image) {
0054         emit sigActivateNode(0);
0055         m_d->image->disconnect(this);
0056         m_d->image->disconnect(&m_d->nodeChangedConnection);
0057         m_d->image->disconnect(&m_d->activateNodeConnection);
0058 
0059         KisNodeList nodesToRemove;
0060 
0061         {
0062             QMutexLocker l(&m_d->pendingNodeSetLock);
0063             std::swap(nodesToRemove, m_d->pendingNodeSet);
0064             m_d->pendingNodeSet.clear();
0065         }
0066 
0067         for (auto it = std::make_reverse_iterator(nodesToRemove.end());
0068              it != std::make_reverse_iterator(nodesToRemove.begin());
0069              ++it) {
0070 
0071             m_d->removeNodeConnection.start(*it);
0072         }
0073     }
0074 
0075     m_d->image = image;
0076 
0077     if (image) {
0078         slotNodeAdded(image->root());
0079 
0080         connect(image, SIGNAL(sigNodeAddedAsync(KisNodeSP)),
0081                 SLOT(slotNodeAdded(KisNodeSP)), Qt::DirectConnection);
0082         connect(image, SIGNAL(sigRemoveNodeAsync(KisNodeSP)),
0083                 SLOT(slotRemoveNode(KisNodeSP)), Qt::DirectConnection);
0084         connect(image, SIGNAL(sigLayersChangedAsync()),
0085                 SLOT(slotLayersChanged()), Qt::DirectConnection);
0086 
0087         m_d->nodeChangedConnection.connectInputSignal(image, &KisImage::sigNodeChanged);
0088         m_d->activateNodeConnection.connectInputSignal(image, &KisImage::sigNodeAddedAsync);
0089 
0090         m_d->activateNodeConnection.start(findFirstLayer(image->root()));
0091     }
0092 }
0093 
0094 KisImageWSP KisDummiesFacadeBase::image() const
0095 {
0096     return m_d->image;
0097 }
0098 
0099 KisNodeSP KisDummiesFacadeBase::findFirstLayer(KisNodeSP root)
0100 {
0101     KisNodeSP child = root->firstChild();
0102     while(child && !child->inherits("KisLayer")) {
0103         child = child->nextSibling();
0104     }
0105     return child;
0106 }
0107 
0108 void KisDummiesFacadeBase::slotNodeChanged(KisNodeSP node)
0109 {
0110     KisNodeDummy *dummy = dummyForNode(node);
0111 
0112     /**
0113      * In some "buggy" code the node-changed signal may be emitted
0114      * before the node will become a part of the node graph. It is
0115      * a bug, we a really minor one. It should not cause any data
0116      * losses to the user.
0117      */
0118     KIS_SAFE_ASSERT_RECOVER_RETURN(dummy);
0119 
0120     emit sigDummyChanged(dummy);
0121 }
0122 
0123 void KisDummiesFacadeBase::slotLayersChanged()
0124 {
0125     setImage(m_d->image);
0126 }
0127 
0128 void KisDummiesFacadeBase::slotNodeActivationRequested(KisNodeSP node)
0129 {
0130     if (!node || !node->graphListener()) return;
0131 
0132     if (!node->inherits("KisSelectionMask") &&
0133         !node->inherits("KisReferenceImagesLayer") &&
0134         !node->inherits("KisDecorationsWrapperLayer")) {
0135 
0136         emit sigActivateNode(node);
0137     }
0138 }
0139 
0140 void KisDummiesFacadeBase::slotNodeAdded(KisNodeSP node)
0141 {
0142     {
0143         QMutexLocker l(&m_d->pendingNodeSetLock);
0144         m_d->pendingNodeSet.append(node);
0145     }
0146 
0147     m_d->addNodeConnection.start(node, node->parent(), node->prevSibling());
0148 
0149     KisNodeSP childNode = node->firstChild();
0150     while (childNode) {
0151         slotNodeAdded(childNode);
0152         childNode = childNode->nextSibling();
0153     }
0154 }
0155 
0156 void KisDummiesFacadeBase::slotRemoveNode(KisNodeSP node)
0157 {
0158     {
0159         QMutexLocker l(&m_d->pendingNodeSetLock);
0160         KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->pendingNodeSet.contains(node));
0161     }
0162 
0163     KisNodeSP childNode = node->lastChild();
0164     while (childNode) {
0165         slotRemoveNode(childNode);
0166         childNode = childNode->prevSibling();
0167     }
0168 
0169     {
0170         QMutexLocker l(&m_d->pendingNodeSetLock);
0171         m_d->pendingNodeSet.removeOne(node);
0172     }
0173     m_d->removeNodeConnection.start(node);
0174 }
0175 
0176 void KisDummiesFacadeBase::slotContinueAddNode(KisNodeSP node, KisNodeSP parent, KisNodeSP aboveThis)
0177 {
0178     KisNodeDummy *parentDummy = parent ? dummyForNode(parent) : 0;
0179     KisNodeDummy *aboveThisDummy = aboveThis ? dummyForNode(aboveThis) : 0;
0180     // Add one because this node does not exist yet
0181     int index = parentDummy && aboveThisDummy ?
0182         parentDummy->indexOf(aboveThisDummy) + 1 : 0;
0183     emit sigBeginInsertDummy(parentDummy, index, node->metaObject()->className());
0184 
0185     addNodeImpl(node, parent, aboveThis);
0186 
0187     emit sigEndInsertDummy(dummyForNode(node));
0188 }
0189 
0190 void KisDummiesFacadeBase::slotContinueRemoveNode(KisNodeSP node)
0191 {
0192     KisNodeDummy *dummy = dummyForNode(node);
0193     emit sigBeginRemoveDummy(dummy);
0194 
0195     removeNodeImpl(node);
0196 
0197     emit sigEndRemoveDummy();
0198 }