diff --git a/Engine/EffectInstance.cpp b/Engine/EffectInstance.cpp index acb7d418d3..17e0281f64 100644 --- a/Engine/EffectInstance.cpp +++ b/Engine/EffectInstance.cpp @@ -553,6 +553,12 @@ EffectInstance::getScriptName_mt_safe() const return getNode()->getScriptName_mt_safe(); } +std::string +EffectInstance::getFullyQualifiedName() const +{ + return getNode()->getFullyQualifiedName(); +} + int EffectInstance::getRenderViewsCount() const { diff --git a/Engine/EffectInstance.h b/Engine/EffectInstance.h index ae6a524f0e..e3ce0f0a96 100644 --- a/Engine/EffectInstance.h +++ b/Engine/EffectInstance.h @@ -291,6 +291,7 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON **/ const std::string & getScriptName() const WARN_UNUSED_RETURN; virtual std::string getScriptName_mt_safe() const OVERRIDE FINAL WARN_UNUSED_RETURN; + virtual std::string getFullyQualifiedName() const OVERRIDE FINAL WARN_UNUSED_RETURN; virtual void onScriptNameChanged(const std::string& /*fullyQualifiedName*/) {} /** diff --git a/Engine/Knob.cpp b/Engine/Knob.cpp index 8dac84bb61..1083d7903f 100644 --- a/Engine/Knob.cpp +++ b/Engine/Knob.cpp @@ -56,6 +56,8 @@ #include "Engine/StringAnimationManager.h" #include "Engine/TLSHolder.h" #include "Engine/TimeLine.h" +#include "Engine/TrackMarker.h" +#include "Engine/TrackerContext.h" #include "Engine/Transform.h" #include "Engine/ViewIdx.h" #include "Engine/ViewerInstance.h" @@ -149,6 +151,125 @@ KnobI::slaveTo(int dimension, return slaveToInternal(dimension, other, otherDimension, eValueChangedReasonNatronInternalEdited, ignoreMasterPersistence); } +static KnobIPtr +findMaster(const KnobIPtr & knob, + const NodesList & allNodes, + const std::string& masterKnobName, + const std::string& masterNodeName, + const std::string& masterTrackName, + const std::map& oldNewScriptNamesMapping) +{ + ///we need to cycle through all the nodes of the project to find the real master + NodePtr masterNode; + std::string masterNodeNameToFind = masterNodeName; + + qDebug() << "Link slave/master for" << knob->getName().c_str() << "restoring linkage" << masterNodeNameToFind.c_str() << '/' << masterTrackName.c_str() << '/' << masterKnobName.c_str(); + + /* + When copy pasting, the new node copied has a script-name different from what is inside the serialization because 2 + nodes cannot co-exist with the same script-name. We keep in the map the script-names mapping + */ + std::map::const_iterator foundMapping = oldNewScriptNamesMapping.find(masterNodeName); + + if ( foundMapping != oldNewScriptNamesMapping.end() ) { + masterNodeNameToFind = foundMapping->second; + } + + for (NodesList::const_iterator it2 = allNodes.begin(); it2 != allNodes.end(); ++it2) { + qDebug() << "Trying node" << (*it2)->getScriptName().c_str() << '/' << (*it2)->getFullyQualifiedName().c_str(); + if ( (*it2)->getFullyQualifiedName() == masterNodeNameToFind ) { + masterNode = *it2; + break; + } + } + if (!masterNode) { + qDebug() << "Link slave/master for" << knob->getName().c_str() << "could not find master node" << masterNodeNameToFind.c_str() << "trying without the group name (Natron project files before 2.3.16)..."; + + for (NodesList::const_iterator it2 = allNodes.begin(); it2 != allNodes.end(); ++it2) { + qDebug() << "Trying node" << (*it2)->getScriptName().c_str() << '/' << (*it2)->getFullyQualifiedName().c_str(); + if ( (*it2)->getScriptName() == masterNodeNameToFind ) { + masterNode = *it2; + qDebug() << "Got node" << (*it2)->getFullyQualifiedName().c_str(); + break; + } + } + } + if (!masterNode) { + qDebug() << "Link slave/master for" << knob->getName().c_str() << "could not find master node" << masterNodeNameToFind.c_str(); + + return KnobIPtr(); + } + + if ( !masterTrackName.empty() ) { + TrackerContextPtr context = masterNode->getTrackerContext(); + if (context) { + TrackMarkerPtr masterTrack = context->getMarkerByName(masterTrackName); + if (!masterTrack) { + qDebug() << "Link slave/master for" << knob->getName().c_str() << "could not find track" << masterNodeNameToFind.c_str() << '/' << masterTrackName.c_str(); + } else { + KnobIPtr masterKnob = masterTrack->getKnobByName(masterKnobName); + if (!masterKnob) { + qDebug() << "Link slave/master for" << knob->getName().c_str() << "cound not find knob in track" << masterNodeNameToFind.c_str() << '/' << masterTrackName.c_str() << '/' << masterKnobName.c_str(); + } + return masterKnob; + } + } + } else { + ///now that we have the master node, find the corresponding knob + const std::vector & otherKnobs = masterNode->getKnobs(); + for (std::size_t j = 0; j < otherKnobs.size(); ++j) { + qDebug() << "Trying knob" << otherKnobs[j]->getName().c_str(); + // The other knob doesn't need to be persistent (it may be a pushbutton) + if (otherKnobs[j]->getName() == masterKnobName) { + return otherKnobs[j]; + } + } + qDebug() << "Link slave/master for" << knob->getName().c_str() << "cound not find knob" << masterNodeNameToFind.c_str() << '/' << masterTrackName.c_str() << '/' << masterKnobName.c_str(); + } + + qDebug() << "Link slave/master for" << knob->getName().c_str() << "failed to restore linkage" << masterNodeNameToFind.c_str() << '/' << masterTrackName.c_str() << '/' << masterKnobName.c_str(); + + return KnobIPtr(); +} + +void +KnobI::restoreLinks(const NodesList & allNodes, + const std::map& oldNewScriptNamesMapping, + bool throwOnFailure) +{ + int i = 0; + KnobIPtr thisKnob = shared_from_this(); + for (std::vector::const_iterator l = _links.begin(); l != _links.end(); ++l) { + + KnobIPtr linkedKnob = findMaster(thisKnob, allNodes, l->knobName, l->nodeName, l->trackName, oldNewScriptNamesMapping); + if (!linkedKnob) { + if (throwOnFailure) { + throw std::runtime_error(std::string("KnobI::restoreLinks(): Could not restore link to node/knob/track ") + l->nodeName + '/' + l->knobName + '/' + l->trackName); + } else { + continue; + } + } + if (linkedKnob == thisKnob) { + qDebug() << "Link for" << getName().c_str() + << "failed to restore the following linkage:" + << "node=" << l->nodeName.c_str() + << "knob=" << l->knobName.c_str() + << "track=" << l->trackName.c_str() + << "because it returned the same Knob. Probably a pre-2.3.16 project with Group clones"; + if (throwOnFailure) { + throw std::runtime_error(std::string("KnobI::restoreLinks(): Could not restore link to node/knob/track ") + l->nodeName + '/' + l->knobName + '/' + l->trackName + " because it returned the same Knob. Probably a pre-2.3.16 project with Group clones."); + } else { + continue; + } + } else if (l->dimension == -1) { + setKnobAsAliasOfThis(linkedKnob, true); + } else { + slaveTo(i, linkedKnob, l->dimension); + } + } + _links.clear(); +} + void KnobI::onKnobSlavedTo(int dimension, const KnobIPtr & other, diff --git a/Engine/Knob.h b/Engine/Knob.h index 6a0c325a9b..d1511ef350 100644 --- a/Engine/Knob.h +++ b/Engine/Knob.h @@ -1187,6 +1187,32 @@ class KnobI * @param ignoreMasterPersistence If true the master will not be serialized. **/ bool slaveTo(int dimension, const KnobIPtr & other, int otherDimension, bool ignoreMasterPersistence = false); + + + void storeLink(int dimension, + const std::string& knobName, + const std::string& nodeName, + const std::string& trackName) + { + _links.push_back(Link({dimension, knobName, nodeName, trackName})); + } + + void restoreLinks(const NodesList & allNodes, + const std::map& oldNewScriptNamesMapping, + bool throwOnFailure); + +private: + struct Link { + int dimension; // -1 means alias + std::string knobName; + std::string nodeName; + std::string trackName; + }; + + std::vector _links; + +public: + virtual bool isMastersPersistenceIgnored() const = 0; virtual KnobIPtr createDuplicateOnHolder(KnobHolder* otherHolder, const boost::shared_ptr& page, @@ -2839,6 +2865,8 @@ class NamedKnobHolder } virtual std::string getScriptName_mt_safe() const = 0; + + virtual std::string getFullyQualifiedName() const = 0; }; diff --git a/Engine/KnobSerialization.cpp b/Engine/KnobSerialization.cpp index f6262ec685..08ed45d4d9 100644 --- a/Engine/KnobSerialization.cpp +++ b/Engine/KnobSerialization.cpp @@ -129,10 +129,10 @@ ValueSerialization::initForSave(const KnobIPtr & knob, TrackMarker* isMarker = dynamic_cast(holder); if (isMarker) { _master.masterTrackName = isMarker->getScriptName_mt_safe(); - _master.masterNodeName = isMarker->getContext()->getNode()->getScriptName_mt_safe(); + _master.masterNodeName = isMarker->getContext()->getNode()->getFullyQualifiedName(); } else { // coverity[dead_error_line] - _master.masterNodeName = holder ? holder->getScriptName_mt_safe() : ""; + _master.masterNodeName = holder ? holder->getFullyQualifiedName() : ""; } _master.masterKnobName = m.second->getName(); } else { @@ -193,68 +193,9 @@ KnobSerialization::createKnob(const std::string & typeName, return ret; } -static KnobIPtr -findMaster(const KnobIPtr & knob, - const NodesList & allNodes, - const std::string& masterKnobName, - const std::string& masterNodeName, - const std::string& masterTrackName, - const std::map& oldNewScriptNamesMapping) -{ - ///we need to cycle through all the nodes of the project to find the real master - NodePtr masterNode; - std::string masterNodeNameToFind = masterNodeName; - - /* - When copy pasting, the new node copied has a script-name different from what is inside the serialization because 2 - nodes cannot co-exist with the same script-name. We keep in the map the script-names mapping - */ - std::map::const_iterator foundMapping = oldNewScriptNamesMapping.find(masterNodeName); - - if ( foundMapping != oldNewScriptNamesMapping.end() ) { - masterNodeNameToFind = foundMapping->second; - } - - for (NodesList::const_iterator it2 = allNodes.begin(); it2 != allNodes.end(); ++it2) { - if ( (*it2)->getScriptName() == masterNodeNameToFind ) { - masterNode = *it2; - break; - } - } - if (!masterNode) { - qDebug() << "Link slave/master for " << knob->getName().c_str() << " failed to restore the following linkage: " << masterNodeNameToFind.c_str(); - - return KnobIPtr(); - } - - if ( !masterTrackName.empty() ) { - TrackerContextPtr context = masterNode->getTrackerContext(); - if (context) { - TrackMarkerPtr marker = context->getMarkerByName(masterTrackName); - if (marker) { - return marker->getKnobByName(masterKnobName); - } - } - } else { - ///now that we have the master node, find the corresponding knob - const std::vector & otherKnobs = masterNode->getKnobs(); - for (std::size_t j = 0; j < otherKnobs.size(); ++j) { - if ( (otherKnobs[j]->getName() == masterKnobName) && otherKnobs[j]->getIsPersistent() ) { - return otherKnobs[j]; - break; - } - } - } - - qDebug() << "Link slave/master for " << knob->getName().c_str() << " failed to restore the following linkage: " << masterNodeNameToFind.c_str(); - - return KnobIPtr(); -} void -KnobSerialization::restoreKnobLinks(const KnobIPtr & knob, - const NodesList & allNodes, - const std::map& oldNewScriptNamesMapping) +KnobSerialization::storeKnobLinks(const KnobIPtr & knob) { int i = 0; @@ -266,24 +207,20 @@ KnobSerialization::restoreKnobLinks(const KnobIPtr & knob, const std::string& aliasKnobName = _masters.front().masterKnobName; const std::string& aliasNodeName = _masters.front().masterNodeName; const std::string& masterTrackName = _masters.front().masterTrackName; - KnobIPtr alias = findMaster(knob, allNodes, aliasKnobName, aliasNodeName, masterTrackName, oldNewScriptNamesMapping); - if (alias) { - knob->setKnobAsAliasOfThis(alias, true); - } + knob->storeLink(-1, aliasKnobName, aliasNodeName, masterTrackName); } } else { for (std::list::iterator it = _masters.begin(); it != _masters.end(); ++it) { if (it->masterDimension != -1) { - KnobIPtr master = findMaster(knob, allNodes, it->masterKnobName, it->masterNodeName, it->masterTrackName, oldNewScriptNamesMapping); - if (master) { - knob->slaveTo(i, master, it->masterDimension); - } + knob->storeLink(it->masterDimension, it->masterKnobName, it->masterNodeName, it->masterTrackName); } ++i; } } } + + void KnobSerialization::restoreExpressions(const KnobIPtr & knob, const std::map& oldNewScriptNamesMapping) diff --git a/Engine/KnobSerialization.h b/Engine/KnobSerialization.h index 48ef9af4db..dc82962a86 100644 --- a/Engine/KnobSerialization.h +++ b/Engine/KnobSerialization.h @@ -928,11 +928,9 @@ class KnobSerialization ~KnobSerialization() { delete _extraData; } /** - * @brief This function cannot be called until all knobs of the project have been created. + * @brief Store the links, which can be restored once all Nodes and Knobs have been created, using KnobI::restoreLinks() **/ - void restoreKnobLinks(const KnobIPtr & knob, - const NodesList & allNodes, - const std::map& oldNewScriptNamesMapping); + void storeKnobLinks(const KnobIPtr & knob); /** * @brief This function cannot be called until all knobs of the project have been created. diff --git a/Engine/Node.h b/Engine/Node.h index 8734032982..1abdc34857 100644 --- a/Engine/Node.h +++ b/Engine/Node.h @@ -165,9 +165,13 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON ///This cannot be done in loadKnobs as to call this all the nodes in the project must have ///been loaded first. - void restoreKnobsLinks(const NodeSerialization & serialization, - const NodesList & allNodes, - const std::map& oldNewScriptNamesMapping); + void storeKnobsLinks(const NodeSerialization & serialization, + const std::map& oldNewScriptNamesMapping); + + // Once all nodes are created, we can restore the links that were previously saved by storeKnobsLinks() + void restoreKnobsLinks(const NodesList & allNodes, + const std::map& oldNewScriptNamesMapping, + bool throwOnFailure); void restoreUserKnobs(const NodeSerialization& serialization); diff --git a/Engine/NodeGroupSerialization.cpp b/Engine/NodeGroupSerialization.cpp index 58e1f784d3..1d6837509c 100644 --- a/Engine/NodeGroupSerialization.cpp +++ b/Engine/NodeGroupSerialization.cpp @@ -395,11 +395,6 @@ NodeCollectionSerialization::restoreFromSerialization(const std::list::const_iterator it = serializedNodes.begin(); it != serializedNodes.end(); ++it) { ///Now that the graph is setup, restore expressions - NodesList nodes = group->getNodes(); - if (isNodeGroup) { - nodes.push_back( isNodeGroup->getNode() ); - } - { std::map oldNewScriptNamesMapping; for (std::map::const_iterator it = createdNodes.begin(); it != createdNodes.end(); ++it) { @@ -407,7 +402,8 @@ NodeCollectionSerialization::restoreFromSerialization(const std::listfirst->restoreKnobsLinks(*it->second, nodes, oldNewScriptNamesMapping); + it->first->storeKnobsLinks(*it->second, oldNewScriptNamesMapping); + // restoreKnobsLinks() is called on all nodes at the end of ProjectPrivate::restoreFromSerialization() } } diff --git a/Engine/NodeMain.cpp b/Engine/NodeMain.cpp index 8d3f3fe57e..e74f3056fd 100644 --- a/Engine/NodeMain.cpp +++ b/Engine/NodeMain.cpp @@ -654,9 +654,8 @@ Node::Implementation::restoreUserKnobsRecursive(const std::list& oldNewScriptNamesMapping) +Node::Implementation::storeKnobLinksRecursive(const GroupKnobSerialization* group, + const std::map& oldNewScriptNamesMapping) { const std::list& children = group->getChildren(); @@ -665,7 +664,7 @@ Node::Implementation::restoreKnobLinksRecursive(const GroupKnobSerialization* gr KnobSerialization* isRegular = dynamic_cast( it->get() ); assert(isGrp || isRegular); if (isGrp) { - restoreKnobLinksRecursive(isGrp, allNodes, oldNewScriptNamesMapping); + storeKnobLinksRecursive(isGrp, oldNewScriptNamesMapping); } else if (isRegular) { KnobIPtr knob = _publicInterface->getKnobByName( isRegular->getName() ); if (!knob) { @@ -678,16 +677,16 @@ Node::Implementation::restoreKnobLinksRecursive(const GroupKnobSerialization* gr appPTR->writeToErrorLog_mt_safe(QString::fromUtf8( _publicInterface->getScriptName_mt_safe().c_str() ), QDateTime::currentDateTime(), err, false, c); continue; } - isRegular->restoreKnobLinks(knob, allNodes, oldNewScriptNamesMapping); + isRegular->storeKnobLinks(knob); isRegular->restoreExpressions(knob, oldNewScriptNamesMapping); } } } + void -Node::restoreKnobsLinks(const NodeSerialization & serialization, - const NodesList & allNodes, - const std::map& oldNewScriptNamesMapping) +Node::storeKnobsLinks(const NodeSerialization & serialization, + const std::map& oldNewScriptNamesMapping) { ////Only called by the main-thread assert( QThread::currentThread() == qApp->thread() ); @@ -706,13 +705,50 @@ Node::restoreKnobsLinks(const NodeSerialization & serialization, appPTR->writeToErrorLog_mt_safe(QString::fromUtf8( getScriptName_mt_safe().c_str() ), QDateTime::currentDateTime(), err, false, c); continue; } - (*it)->restoreKnobLinks(knob, allNodes, oldNewScriptNamesMapping); + (*it)->storeKnobLinks(knob); (*it)->restoreExpressions(knob, oldNewScriptNamesMapping); } const std::list& userKnobs = serialization.getUserPages(); for (std::list::const_iterator it = userKnobs.begin(); it != userKnobs.end(); ++it) { - _imp->restoreKnobLinksRecursive( (*it).get(), allNodes, oldNewScriptNamesMapping ); + _imp->storeKnobLinksRecursive( (*it).get(), oldNewScriptNamesMapping ); + } +} + + +void +Node::Implementation::restoreKnobLinksRecursive(const NodesList & allNodes, + const KnobGroupPtr& group, + const std::map& oldNewScriptNamesMapping, + bool throwOnFailure) +{ + const std::vector& children = group->getChildren(); + + for (std::vector::const_iterator it = children.begin(); it != children.end(); ++it) { + (*it)->restoreLinks(allNodes, oldNewScriptNamesMapping, throwOnFailure); + KnobGroupPtr isGroup = boost::dynamic_pointer_cast(*it); + if (isGroup) { + restoreKnobLinksRecursive(allNodes, isGroup, oldNewScriptNamesMapping, throwOnFailure); + } + } +} + + +void +Node::restoreKnobsLinks(const NodesList & allNodes, + const std::map& oldNewScriptNamesMapping, + bool throwOnFailure) +{ + ////Only called by the main-thread + assert( QThread::currentThread() == qApp->thread() ); + + std::vector knobs = getKnobs(); + for (std::vector::iterator it = knobs.begin(); it != knobs.end(); ++it) { + (*it)->restoreLinks(allNodes, oldNewScriptNamesMapping, throwOnFailure); + KnobGroupPtr group = boost::dynamic_pointer_cast(*it); + if (group) { + _imp->restoreKnobLinksRecursive(allNodes, group, oldNewScriptNamesMapping, throwOnFailure); + } } } diff --git a/Engine/NodePrivate.h b/Engine/NodePrivate.h index 101e069159..77f0049b11 100644 --- a/Engine/NodePrivate.h +++ b/Engine/NodePrivate.h @@ -277,9 +277,13 @@ struct Node::Implementation const KnobGroupPtr& group, const KnobPagePtr& page); - void restoreKnobLinksRecursive(const GroupKnobSerialization* group, - const NodesList & allNodes, - const std::map& oldNewScriptNamesMapping); + void storeKnobLinksRecursive(const GroupKnobSerialization* group, + const std::map& oldNewScriptNamesMapping); + + void restoreKnobLinksRecursive(const NodesList & allNodes, + const KnobGroupPtr& group, + const std::map& oldNewScriptNamesMapping, + bool throwOnFailure); void ifGroupForceHashChangeOfInputs(); diff --git a/Engine/Project.cpp b/Engine/Project.cpp index 69de506cfc..0b58bbb41b 100644 --- a/Engine/Project.cpp +++ b/Engine/Project.cpp @@ -335,6 +335,15 @@ Project::loadProjectInternal(const QString & path, if (!bgProject) { getApp()->loadProjectGui(isAutoSave, iArchive); } + } catch (const std::exception &e) { + const ProjectBeingLoadedInfo& pInfo = getApp()->getProjectBeingLoadedInfo(); + if (pInfo.vMajor > NATRON_VERSION_MAJOR || + (pInfo.vMajor == NATRON_VERSION_MAJOR && pInfo.vMinor > NATRON_VERSION_MINOR) || + (pInfo.vMajor == NATRON_VERSION_MAJOR && pInfo.vMinor == NATRON_VERSION_MINOR && pInfo.vRev > NATRON_VERSION_REVISION)) { + QString message = tr("This project was saved with a more recent version (%1.%2.%3) of %4. Projects are not forward compatible and may only be opened in a version of %4 equal or more recent than the version that saved it.").arg(pInfo.vMajor).arg(pInfo.vMinor).arg(pInfo.vRev).arg(QString::fromUtf8(NATRON_APPLICATION_NAME)); + throw std::runtime_error(message.toStdString()); + } + throw std::runtime_error( tr("Unrecognized or damaged project file:").toStdString() + ' ' + e.what()); } catch (...) { const ProjectBeingLoadedInfo& pInfo = getApp()->getProjectBeingLoadedInfo(); if (pInfo.vMajor > NATRON_VERSION_MAJOR || diff --git a/Engine/ProjectPrivate.cpp b/Engine/ProjectPrivate.cpp index 1f42f1c95d..db8d6d9652 100644 --- a/Engine/ProjectPrivate.cpp +++ b/Engine/ProjectPrivate.cpp @@ -105,6 +105,7 @@ ProjectPrivate::restoreFromSerialization(const ProjectSerialization & obj, { /*1st OFF RESTORE THE PROJECT KNOBS*/ bool ok; + std::vector linksErrors; { CreatingNodeTreeFlag_RAII creatingNodeTreeFlag( _publicInterface->getApp() ); @@ -208,6 +209,22 @@ ProjectPrivate::restoreFromSerialization(const ProjectSerialization & obj, } } + // restore the Knob links stored in the Knobs during NodeCollectionSerialization::restoreFromSerialization() + NodesList allNodes; + const std::map oldNewScriptNamesMapping; + _publicInterface->getNodes_recursive(allNodes, false); + for (NodesList::iterator n = allNodes.begin(); n != allNodes.end(); ++n) { + // find all KnobIs in the node, restore links + std::vector knobs = (*n)->getKnobs(); // copy the knobs vector + for (std::vector::iterator k = knobs.begin(); k != knobs.end(); ++k) { + // Even if there are links errors, do our best to restore the project, then display an error dialog. + try { + (*k)->restoreLinks(allNodes, oldNewScriptNamesMapping, true); // error if links cannot be restored + } catch (const std::exception& e) { + linksErrors.push_back( e.what() ); + } + } + } _publicInterface->getApp()->updateProjectLoadStatus( tr("Restoring graph stream preferences...") ); } // CreatingNodeTreeFlag_RAII creatingNodeTreeFlag(_publicInterface->getApp()); @@ -227,6 +244,15 @@ ProjectPrivate::restoreFromSerialization(const ProjectSerialization & obj, _publicInterface->recomputeFrameRangeFromReaders(); } + // Even if there are links errors, do our best to restore the project, then display an error dialog. + if ( !linksErrors.empty() ) { + const char* const delim = "\n"; + + std::ostringstream imploded; + std::copy(linksErrors.begin(), linksErrors.end(), + std::ostream_iterator(imploded, delim)); + throw std::runtime_error( imploded.str() ); + } return ok; } // restoreFromSerialization diff --git a/Engine/TrackMarker.cpp b/Engine/TrackMarker.cpp index b6335b9949..81333ea4ce 100644 --- a/Engine/TrackMarker.cpp +++ b/Engine/TrackMarker.cpp @@ -37,6 +37,7 @@ #include "Engine/Image.h" #include "Engine/ImagePlaneDesc.h" #include "Engine/Node.h" +#include "Engine/NodeGroup.h" #include "Engine/TrackerContext.h" #include "Engine/TimeLine.h" #include "Engine/TrackerSerialization.h" @@ -430,6 +431,47 @@ TrackMarker::getScriptName_mt_safe() const return _imp->trackScriptName; } +// copy-pasted from NodeName.cpp +static void +prependGroupNameRecursive(const NodePtr& group, + std::string& name) +{ + name.insert(0, "."); + name.insert( 0, group->getScriptName_mt_safe() ); + NodeCollectionPtr hasParentGroup = group->getGroup(); + NodeGroupPtr isGrp = boost::dynamic_pointer_cast(hasParentGroup); + if (isGrp) { + prependGroupNameRecursive(isGrp->getNode(), name); + } +} + +// copied from NodeName.cpp:Node::getFullyQualifiedNameInternal() +std::string +TrackMarker::getFullyQualifiedName() const +{ + QMutexLocker l(&_imp->trackMutex); + TrackerContextPtr context = getContext(); + + NodePtr node = context->getNode(); + std::string ret = _imp->trackScriptName; + NodePtr parent = node->getParentMultiInstance(); + + if (parent) { + prependGroupNameRecursive(parent, ret); + } else { + NodeCollectionPtr hasParentGroup = node->getGroup(); + NodeGroup* isGrp = dynamic_cast( hasParentGroup.get() ); + if (isGrp) { + NodePtr grpNode = isGrp->getNode(); + if (grpNode) { + prependGroupNameRecursive(grpNode, ret); + } + } + } + + return ret; +} + void TrackMarker::setLabel(const std::string& label) { diff --git a/Engine/TrackMarker.h b/Engine/TrackMarker.h index 22754c8974..28aace7940 100644 --- a/Engine/TrackMarker.h +++ b/Engine/TrackMarker.h @@ -131,6 +131,7 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON bool setScriptName(const std::string& name); virtual std::string getScriptName_mt_safe() const OVERRIDE FINAL WARN_UNUSED_RETURN; + virtual std::string getFullyQualifiedName() const OVERRIDE FINAL WARN_UNUSED_RETURN; void setLabel(const std::string& label); std::string getLabel() const; diff --git a/Gui/MultiInstancePanel.cpp b/Gui/MultiInstancePanel.cpp index 0e2e4edb67..1f7d55e0fa 100644 --- a/Gui/MultiInstancePanel.cpp +++ b/Gui/MultiInstancePanel.cpp @@ -452,6 +452,12 @@ MultiInstancePanel::getScriptName_mt_safe() const return _imp->getMainInstance()->getScriptName_mt_safe(); } +std::string +MultiInstancePanel::getFullyQualifiedName() const +{ + return _imp->getMainInstance()->getFullyQualifiedName(); +} + void MultiInstancePanel::initializeKnobs() { diff --git a/Gui/MultiInstancePanel.h b/Gui/MultiInstancePanel.h index e23744ded2..2c3980a896 100644 --- a/Gui/MultiInstancePanel.h +++ b/Gui/MultiInstancePanel.h @@ -77,6 +77,7 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON const std::list > & getInstances() const; virtual std::string getScriptName_mt_safe() const OVERRIDE FINAL; + virtual std::string getFullyQualifiedName() const OVERRIDE FINAL; NodePtr getMainInstance() const; NodeGuiPtr getMainInstanceGui() const; diff --git a/Gui/NodeGraph40.cpp b/Gui/NodeGraph40.cpp index 03a9a031b1..b08b669abe 100644 --- a/Gui/NodeGraph40.cpp +++ b/Gui/NodeGraph40.cpp @@ -321,13 +321,13 @@ NodeGraph::cloneSelectedNodes(const QPointF& scenePos) NodesList allNodes; - getGui()->getApp()->getProject()->getActiveNodes(&allNodes); - + getGui()->getApp()->getProject()->getActiveNodesExpandGroups(&allNodes); //Restore links once all children are created for alias knobs/expressions std::list::iterator itS = serializations.begin(); for (std::list ::iterator it = newNodesList.begin(); it != newNodesList.end(); ++it, ++itS) { - (*it)->getNode()->restoreKnobsLinks(**itS, allNodes, oldNewScriptNameMapping); + (*it)->getNode()->storeKnobsLinks(**itS, oldNewScriptNameMapping); + (*it)->getNode()->restoreKnobsLinks(allNodes, oldNewScriptNameMapping, true); // clone should never fail } diff --git a/Gui/NodeGraph45.cpp b/Gui/NodeGraph45.cpp index 3143fba6f3..f9b68e7edb 100644 --- a/Gui/NodeGraph45.cpp +++ b/Gui/NodeGraph45.cpp @@ -676,16 +676,12 @@ NodeGraph::copyNodesAndCreateInGroup(const NodesGuiList& nodes, //Restore links once all children are created for alias knobs/expressions NodesList allNodes; - group->getActiveNodes(&allNodes); - // If the group is a Group node, append to all nodes reachable through links - NodeGroup* isGroupNode = dynamic_cast(group.get()); - if (isGroupNode) { - allNodes.push_back(isGroupNode->getNode()); - } + getGui()->getApp()->getProject()->getActiveNodesExpandGroups(&allNodes); std::list::const_iterator itSerialization = clipboard.nodes.begin(); for (std::list > ::iterator it = createdNodes.begin(); it != createdNodes.end(); ++it, ++itSerialization) { - it->second->getNode()->restoreKnobsLinks(**itSerialization, allNodes, oldNewScriptNamesMapping); + it->second->getNode()->storeKnobsLinks(**itSerialization, oldNewScriptNamesMapping); + it->second->getNode()->restoreKnobsLinks(allNodes, oldNewScriptNamesMapping, true); // may not fail } } diff --git a/Gui/NodeGraphPrivate10.cpp b/Gui/NodeGraphPrivate10.cpp index 58cf762b90..6ab3746af5 100644 --- a/Gui/NodeGraphPrivate10.cpp +++ b/Gui/NodeGraphPrivate10.cpp @@ -119,16 +119,15 @@ NodeGraphPrivate::pasteNodesInternal(const NodeClipBoard & clipboard, restoreConnections(internalNodesClipBoard, *newNodes, oldNewScriptNamesMap); NodesList allNodes; - _publicInterface->getGui()->getApp()->getProject()->getActiveNodes(&allNodes); - + _publicInterface->getGui()->getApp()->getProject()->getActiveNodesExpandGroups(&allNodes); //Restore links once all children are created for alias knobs/expressions for (std::list > ::iterator it = newNodesMap.begin(); it != newNodesMap.end(); ++it) { - it->second->restoreKnobsLinks(*(it->first), allNodes, oldNewScriptNamesMap); + it->second->storeKnobsLinks(*(it->first), oldNewScriptNamesMap); + it->second->restoreKnobsLinks(allNodes, oldNewScriptNamesMap, false); // may fail } } - if (newNodesList.size() > 1) { ///Only compute datas if we're pasting more than 1 node _publicInterface->getGui()->getApp()->getProject()->forceComputeInputDependentDataOnAllTrees(); @@ -211,15 +210,7 @@ NodeGraphPrivate::pasteNode(const NodeSerializationPtr & internalSerialization, //All nodes that are reachable via expressions NodesList allNodes; - n->getGroup()->getActiveNodes(&allNodes); - - ///Add the node group itself - { - NodeGroup* isContainerGroup = dynamic_cast( n->getGroup().get() ); - if (isContainerGroup) { - allNodes.push_back( isContainerGroup->getNode() ); - } - } + _publicInterface->getGui()->getApp()->getProject()->getActiveNodesExpandGroups(&allNodes); //We don't want the clone to have the same hash as the original n->incrementKnobsAge(); @@ -285,13 +276,11 @@ NodeGraphPrivate::pasteNode(const NodeSerializationPtr & internalSerialization, //Restore links once all children are created for alias knobs/expressions for (std::list > ::iterator it = newNodesMap.begin(); it != newNodesMap.end(); ++it) { - it->second->restoreKnobsLinks(*(it->first), allNodes, *oldNewScriptNameMapping); + it->second->storeKnobsLinks(*(it->first), *oldNewScriptNameMapping); + it->second->restoreKnobsLinks(allNodes, *oldNewScriptNameMapping, false); // may fail } - - } - return gui; } // NodeGraphPrivate::pasteNode diff --git a/Gui/NodeGraphUndoRedo.cpp b/Gui/NodeGraphUndoRedo.cpp index 90d26f8426..779e255bc0 100644 --- a/Gui/NodeGraphUndoRedo.cpp +++ b/Gui/NodeGraphUndoRedo.cpp @@ -1217,13 +1217,11 @@ LoadNodePresetsCommand::redo() } NodesList allNodes; - internalNode->getGroup()->getActiveNodes(&allNodes); - NodeGroup* isGroup = internalNode->isEffectGroup(); - if (isGroup) { - isGroup->getActiveNodes(&allNodes); - } + internalNode->getApp()->getProject()->getActiveNodesExpandGroups(&allNodes); + std::map oldNewScriptNames; - internalNode->restoreKnobsLinks(*_newSerializations.front(), allNodes, oldNewScriptNames); + internalNode->storeKnobsLinks(*_newSerializations.front(), oldNewScriptNames); + internalNode->restoreKnobsLinks(allNodes, oldNewScriptNames, true); // may not fail internalNode->getEffectInstance()->incrHashAndEvaluate(true, true); internalNode->getApp()->triggerAutoSave(); _firstRedoCalled = true; diff --git a/Gui/PythonPanels.cpp b/Gui/PythonPanels.cpp index 74ace619b8..e6286ac431 100644 --- a/Gui/PythonPanels.cpp +++ b/Gui/PythonPanels.cpp @@ -84,6 +84,12 @@ DialogParamHolder::getScriptName_mt_safe() const return _imp->uniqueID; } +std::string +DialogParamHolder::getFullyQualifiedName() const +{ + return _imp->uniqueID; +} + void DialogParamHolder::setParamChangedCallback(const QString& callback) { diff --git a/Gui/PythonPanels.h b/Gui/PythonPanels.h index 6291b21260..df7c42c923 100644 --- a/Gui/PythonPanels.h +++ b/Gui/PythonPanels.h @@ -67,6 +67,8 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON virtual std::string getScriptName_mt_safe() const OVERRIDE FINAL; + virtual std::string getFullyQualifiedName() const OVERRIDE FINAL; + void setParamChangedCallback(const QString& callback); private: