diff --git a/include/inamespace.h b/include/inamespace.h index c4ac39bfd3..2fbdfebbd3 100644 --- a/include/inamespace.h +++ b/include/inamespace.h @@ -1,5 +1,6 @@ #pragma once +#include #include "inode.h" #include "imodule.h" @@ -91,7 +92,17 @@ class INamespace * \em not actually import the given scene graph's names into this * namespace. */ - virtual void ensureNoConflicts(const scene::INodePtr& root) = 0; + virtual void ensureNoConflicts(const scene::INodePtr& foreignRoot) = 0; + + /** + * Specialised variant of the above ensureNoConflicts(foreignRoot): + * Prepares only the given list of nodes (that are member of the foreign root) such that + * their names don't conflict with any name in this namespace. + * + * Every Namespaced node will get a new unique name assigned, but only if it's + * actually colliding with a local name, otherwise it will remain unchanged. + */ + virtual void ensureNoConflicts(const scene::INodePtr& foreignRoot, const std::set& foreignNodes) = 0; }; typedef std::shared_ptr INamespacePtr; diff --git a/radiantcore/map/namespace/Namespace.cpp b/radiantcore/map/namespace/Namespace.cpp index 0c6e3682a4..43fb271d1b 100644 --- a/radiantcore/map/namespace/Namespace.cpp +++ b/radiantcore/map/namespace/Namespace.cpp @@ -242,20 +242,43 @@ void Namespace::nameChanged(const std::string& oldName, const std::string& newNa } } -void Namespace::ensureNoConflicts(const scene::INodePtr& root) +void Namespace::ensureNoConflicts(const scene::INodePtr& foreignRoot) +{ + // Collect all namespaced items from the foreign root + GatherNamespacedWalker walker; + foreignRoot->traverse(walker); + + ensureNoConflicts(foreignRoot, walker.result); +} + +void Namespace::ensureNoConflicts(const scene::INodePtr& foreignRoot, const std::set& foreignNodes) +{ + // Filter out all namespaced items from the given scene node list + std::set foreignItems; + + for (const auto& node : foreignNodes) + { + auto namespaced = Node_getNamespaced(node); + + if (namespaced) + { + foreignItems.emplace(std::move(namespaced)); + } + } + + ensureNoConflicts(foreignRoot, foreignItems); +} + +void Namespace::ensureNoConflicts(const scene::INodePtr& foreignRoot, const std::set& foreignNodes) { // Instantiate a new, temporary namespace for the nodes below root Namespace foreignNamespace; // Move all nodes below (and including) root into this temporary namespace - foreignNamespace.connect(root); - - // Collect all namespaced items from the foreign root - GatherNamespacedWalker walker; - root->traverse(walker); + foreignNamespace.connect(foreignRoot); - rDebug() << "Namespace::ensureNoConflicts(): imported set of " - << walker.result.size() << " namespaced nodes" << std::endl; + rDebug() << "Namespace::ensureNoConflicts(): importing set of " + << foreignNodes.size() << " namespaced nodes" << std::endl; // Build a union set containing all imported names and all existing names. // We need to know all existing names to ensure that newly created names are @@ -265,29 +288,29 @@ void Namespace::ensureNoConflicts(const scene::INodePtr& root) // Process each object in the to-be-imported tree of nodes, ensuring that it // has a unique name - for (const NamespacedPtr& n : walker.result) + for (const auto& foreignNode : foreignNodes) { // If the imported node conflicts with a name in THIS namespace, then it // needs to be given a new name which is unique in BOTH namespaces. - if (_uniqueNames.nameExists(n->getName())) + if (_uniqueNames.nameExists(foreignNode->getName())) { // Name exists in the target namespace, get a new name - std::string uniqueName = allNames.insertUnique(n->getName()); + std::string uniqueName = allNames.insertUnique(foreignNode->getName()); - rMessage() << "Namespace::ensureNoConflicts(): '" << n->getName() - << "' already exists in this namespace. Rename it to '" - << uniqueName << "'\n"; + rMessage() << "Namespace::ensureNoConflicts(): '" << foreignNode->getName() + << "' already exists in this namespace. Rename it to '" + << uniqueName << "'\n"; // Change the name of the imported node, this should trigger all // observers in the foreign namespace - n->changeName(uniqueName); + foreignNode->changeName(uniqueName); } else { // Name does not exist yet, insert it into the local combined // namespace (but not our destination namespace, this will be // populated in the subsequent call to connect()). - allNames.insert(n->getName()); + allNames.insert(foreignNode->getName()); } } @@ -296,5 +319,5 @@ void Namespace::ensureNoConflicts(const scene::INodePtr& root) // nodes into this namespace without name conflicts // Disconnect the root from the foreign namespace again, it will be destroyed now - foreignNamespace.disconnect(root); + foreignNamespace.disconnect(foreignRoot); } diff --git a/radiantcore/map/namespace/Namespace.h b/radiantcore/map/namespace/Namespace.h index 619629b659..de31ea0112 100644 --- a/radiantcore/map/namespace/Namespace.h +++ b/radiantcore/map/namespace/Namespace.h @@ -9,6 +9,7 @@ class Namespace : public INamespace { +private: // The set of unique names in this namespace UniqueNameSet _uniqueNames; @@ -29,5 +30,9 @@ class Namespace : virtual void addNameObserver(const std::string& name, NameObserver& observer) override; virtual void removeNameObserver(const std::string& name, NameObserver& observer) override; virtual void nameChanged(const std::string& oldName, const std::string& newName) override; - virtual void ensureNoConflicts(const scene::INodePtr& root) override; + virtual void ensureNoConflicts(const scene::INodePtr& foreignRoot) override; + virtual void ensureNoConflicts(const scene::INodePtr& foreignRoot, const std::set& foreignNodes) override; + +private: + void ensureNoConflicts(const scene::INodePtr& foreignRoot, const std::set& foreignNodes); };