Hi there,
I came across a little problem about cloning nested instances and TransferGoal().
Let my try to describe the problem. I have the following setup:
"nested instance" is pointing to "direct instance" and this in turn pointing to the reference object.
Now I am trying to make the instances "editable" - quotation marks because it's just relinking & cloning linked objects which has the same effect in the end and saves a bit time and a few lines of code.
My approach is as follows: Get the linked object and replace it with the instance object currently processed. So to speak: Clone the reference, insert it and remove the original instance object. This is because it also works with deeply nested instance hierarchies.
This all works quite well, but there is an annoying problem that I cannot get rid of. When calling undo, the nested instance loses its link. I tried several approaches like using AliasTrans, adding undos in the helper routines, etc. I really have no clue, what is going on. Maybe it is a bug?
Here's my actual code, so I didn't condense it. Hope, it doesn't matter.
From Command_MakeEditable.h
(CommandData Plugin)
Bool Execute(BaseDocument* doc) override
{
if (!doc)
return false;
// Detect Key modifiers
BaseContainer state;
GetInputState(BFM_INPUT_MOUSE, BFM_INPUT_MOUSELEFT, state);
const auto bCtrl = (state.GetInt32(BFM_INPUT_QUALIFIER) & QCTRL) != 0;
doc->StartUndo();
// Create Array that holds all objects to operate on
const AutoAlloc<AtomArray> activeObjects;
doc->GetActiveObjects(*activeObjects, GETACTIVEOBJECTFLAGS::CHILDREN);
// empty? quit.
if (!activeObjects)
return false;
for (auto i = 0; i < activeObjects->GetCount(); ++i)
{
auto obj = static_cast<BaseObject*>(activeObjects->GetIndex(i));
if (!obj)
continue;
// Make editable magic
if (obj->IsInstanceOf(Oinstance))
{
// Convert a single instance
auto convertedInstance = g_MakeInstanceEditable(doc, obj, bCtrl);
if (convertedInstance == nullptr) // Something went wrong, skip
continue;
// Insert it into the document
doc->InsertObject(convertedInstance, obj->GetUp(), obj->GetPred());
doc->AddUndo(UNDOTYPE::NEWOBJ, convertedInstance);
// Select the new object
doc->AddUndo(UNDOTYPE::BITS, convertedInstance);
convertedInstance->SetBit(BIT_ACTIVE);
// Update links
doc->AddUndo(UNDOTYPE::CHANGE, convertedInstance);
obj->TransferGoal(convertedInstance, false);
// Remove the original instance object to finally replace it with the converted one
doc->AddUndo(UNDOTYPE::DELETEOBJ, obj);
obj->Remove();
BaseObject::Free(obj);
}
}
doc->EndUndo();
EventAdd();
return true;
}
The helper functions:
inline BaseObject* g_MakeInstanceEditable(BaseDocument* doc, BaseObject* obj, const bool deep = false)
{
if (obj == nullptr || !doc)
return nullptr;
if (obj->IsInstanceOf(Oinstance))
{
// Retrieve the linked or root object
// obj is instance and root is requested, so simply return a copy of the root object
auto refObj = g_GetInstanceRef(obj, deep);
if (!refObj)
return nullptr;
return static_cast<BaseObject*>(refObj->GetClone(COPYFLAGS::NONE, nullptr));
}
return nullptr;
}
// Get shallow or deeply linked reference objects
/**
* @brief Retrieve the direct or root linked object of an instance. Also works with nested instances
* @param obj the instance object
* @param deep if true, the root object of nested instances is returned
* @return the reference or root object. Can return nullptr.
*/
inline BaseObject* g_GetInstanceRef(BaseObject* obj, const Bool deep = false)
{
if (!obj->IsInstanceOf(Oinstance))
return nullptr;
// Retrieve Link from instance
GeData data;
if (!obj->GetParameter(DescID(INSTANCEOBJECT_LINK), data, DESCFLAGS_GET::NONE))
return nullptr;
// The document needs to be provided for GetLinkAtom()
const auto doc = obj->GetDocument();
if (!doc)
return nullptr;
// Get the Atom
auto linkedEntity = static_cast<BaseObject*>(data.GetLinkAtom(doc, Obase));
// Get down to the reference object if root object is requested
while (deep && linkedEntity->IsInstanceOf(Oinstance))
{
if (!linkedEntity->GetParameter(DescID(INSTANCEOBJECT_LINK), data, DESCFLAGS_GET::NONE))
return nullptr;
linkedEntity = static_cast<BaseObject*>(data.GetLinkAtom(doc, Obase));
}
return linkedEntity;
}
Any help here would be highly appreciated!
Thanks in advance,
Robert