PropertyLinks: refactor property link API
* Create new class PropertyLinkBase as the common parent class for all type of links. See the class document for details of its API. * Added new link scope 'Hidden' that is ignored during normal object dependency calculation, but still keep tracks of object remove, relabel, etc. * There is a new concept called 'Shadow subname' introduced in this patch, which will only be meaningful in future topological naming feature. See [here](https://git.io/fjXKR) for more details. * DocumentObject added a new API adjustRelativeLink() and a helper resolveRelativeLink() function.
This commit is contained in:
@@ -1577,18 +1577,28 @@ void Application::initTypes(void)
|
||||
App ::PropertyUUID ::init();
|
||||
App ::PropertyFont ::init();
|
||||
App ::PropertyStringList ::init();
|
||||
App ::PropertyLinkBase ::init();
|
||||
App ::PropertyLinkListBase ::init();
|
||||
App ::PropertyLink ::init();
|
||||
App ::PropertyLinkChild ::init();
|
||||
App ::PropertyLinkGlobal ::init();
|
||||
App ::PropertyLinkHidden ::init();
|
||||
App ::PropertyLinkSub ::init();
|
||||
App ::PropertyLinkSubChild ::init();
|
||||
App ::PropertyLinkSubGlobal ::init();
|
||||
App ::PropertyLinkSubHidden ::init();
|
||||
App ::PropertyLinkList ::init();
|
||||
App ::PropertyLinkListChild ::init();
|
||||
App ::PropertyLinkListGlobal ::init();
|
||||
App ::PropertyLinkListHidden ::init();
|
||||
App ::PropertyLinkSubList ::init();
|
||||
App ::PropertyLinkSubListChild ::init();
|
||||
App ::PropertyLinkSubListGlobal ::init();
|
||||
App ::PropertyLinkSubListHidden ::init();
|
||||
App ::PropertyXLink ::init();
|
||||
App ::PropertyXLinkSub ::init();
|
||||
App ::PropertyXLinkSubList ::init();
|
||||
App ::PropertyXLinkContainer ::init();
|
||||
App ::PropertyMatrix ::init();
|
||||
App ::PropertyVector ::init();
|
||||
App ::PropertyVectorDistance ::init();
|
||||
@@ -1608,6 +1618,7 @@ void Application::initTypes(void)
|
||||
App ::PropertyFile ::init();
|
||||
App ::PropertyFileIncluded ::init();
|
||||
App ::PropertyPythonObject ::init();
|
||||
App ::PropertyExpressionContainer ::init();
|
||||
App ::PropertyExpressionEngine ::init();
|
||||
|
||||
// Extension classes
|
||||
|
||||
@@ -1012,6 +1012,91 @@ DocumentObject *DocumentObject::resolve(const char *subname,
|
||||
return obj;
|
||||
}
|
||||
|
||||
DocumentObject *DocumentObject::resolveRelativeLink(std::string &subname,
|
||||
DocumentObject *&link, std::string &linkSub) const
|
||||
{
|
||||
if(!link || !link->getNameInDocument() || !getNameInDocument())
|
||||
return 0;
|
||||
auto ret = const_cast<DocumentObject*>(this);
|
||||
if(link != ret) {
|
||||
auto sub = subname.c_str();
|
||||
auto nextsub = sub;
|
||||
for(auto dot=strchr(nextsub,'.');dot;nextsub=dot+1,dot=strchr(nextsub,'.')) {
|
||||
std::string subcheck(sub,nextsub-sub);
|
||||
subcheck += link->getNameInDocument();
|
||||
subcheck += '.';
|
||||
if(getSubObject(subcheck.c_str())==link) {
|
||||
ret = getSubObject(std::string(sub,dot+1-sub).c_str());
|
||||
if(!ret)
|
||||
return 0;
|
||||
subname = std::string(dot+1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t pos=0,linkPos=0;
|
||||
std::string linkssub,ssub;
|
||||
do {
|
||||
linkPos = linkSub.find('.',linkPos);
|
||||
if(linkPos == std::string::npos) {
|
||||
link = 0;
|
||||
return 0;
|
||||
}
|
||||
++linkPos;
|
||||
pos = subname.find('.',pos);
|
||||
if(pos == std::string::npos) {
|
||||
subname.clear();
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
++pos;
|
||||
}while(subname.compare(0,pos,linkSub,0,linkPos)==0);
|
||||
|
||||
if(pos != std::string::npos) {
|
||||
ret = getSubObject(subname.substr(0,pos).c_str());
|
||||
if(!ret) {
|
||||
link = 0;
|
||||
return 0;
|
||||
}
|
||||
subname = subname.substr(pos);
|
||||
}
|
||||
if(linkPos) {
|
||||
link = link->getSubObject(linkSub.substr(0,linkPos).c_str());
|
||||
if(!link)
|
||||
return 0;
|
||||
linkSub = linkSub.substr(linkPos);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool DocumentObject::adjustRelativeLinks(
|
||||
const std::set<App::DocumentObject *> &inList,
|
||||
std::set<App::DocumentObject *> *visited)
|
||||
{
|
||||
if(visited)
|
||||
visited->insert(this);
|
||||
|
||||
bool touched = false;
|
||||
std::vector<Property*> props;
|
||||
getPropertyList(props);
|
||||
for(auto prop : props) {
|
||||
auto linkProp = Base::freecad_dynamic_cast<PropertyLinkBase>(prop);
|
||||
if(linkProp && linkProp->adjustLink(inList))
|
||||
touched = true;
|
||||
}
|
||||
if(visited) {
|
||||
for(auto obj : getOutList()) {
|
||||
if(!visited->count(obj)) {
|
||||
if(obj->adjustRelativeLinks(inList,visited))
|
||||
touched = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return touched;
|
||||
}
|
||||
|
||||
const std::string &DocumentObject::hiddenMarker() {
|
||||
static std::string marker("!hide");
|
||||
return marker;
|
||||
|
||||
@@ -388,6 +388,70 @@ public:
|
||||
std::string *childName=0, const char **subElement=0,
|
||||
PyObject **pyObj=0, Base::Matrix4D *mat=0, bool transform=true, int depth=0) const;
|
||||
|
||||
/** Resolve a link reference that is relative to this object reference
|
||||
*
|
||||
* @param subname: on input, this is the subname reference to the object
|
||||
* that is to be assigned a link. On output, the reference may be offseted
|
||||
* to be rid off any common parent.
|
||||
* @param link: on input, this is the top parent of the link reference. On
|
||||
* output, it may be altered to one of its child to be rid off any common
|
||||
* parent.
|
||||
* @param linkSub: on input, this the subname of the link reference. On
|
||||
* output, it may be offseted to be rid off any common parent.
|
||||
*
|
||||
* @return The corrected top parent of the object that is to be assigned the
|
||||
* link. If the output 'subname' is empty, then return the object itself.
|
||||
*
|
||||
* To avoid any cyclic reference, an object must not be assign a link to any
|
||||
* of the object in its parent. This function can be used to resolve any
|
||||
* common parents of an object and its link target.
|
||||
*
|
||||
* For example, with the following object hierarchy
|
||||
*
|
||||
* Group
|
||||
* |--Group001
|
||||
* | |--Box
|
||||
* | |--Cylinder
|
||||
* |--Group002
|
||||
* |--Box001
|
||||
* |--Cylinder001
|
||||
*
|
||||
* If you want add a link of Group.Group002.Box001 to Group.Group001, you
|
||||
* can call with the following parameter (which are usually obtained from
|
||||
* Selection.getSelectionEx(), check usage in TreeWidget::onDropEvent()):
|
||||
* std::string subname("Group002.");
|
||||
* auto link = Group;
|
||||
* std::string linkSub("Group001.Box001.");
|
||||
* parent = Group.resolveRelativeLink(subname,link,linkSub);
|
||||
*
|
||||
* The resolving result is as follow:
|
||||
* return -> Group001
|
||||
* subname -> ""
|
||||
* link -> Group002
|
||||
* linkSub -> "Box001."
|
||||
*
|
||||
* The common parent 'Group' is removed.
|
||||
*/
|
||||
App::DocumentObject *resolveRelativeLink(std::string &subname,
|
||||
App::DocumentObject *&link, std::string &linkSub) const;
|
||||
|
||||
/** Called to adjust link properties to avoid cyclic links
|
||||
*
|
||||
* @param inList: the recursive in-list of the future parent object,
|
||||
* including the parent itself.
|
||||
* @param visited: optional set holding the visited objects. If null then
|
||||
* only this object is adjusted, or else all object inside the out-list of
|
||||
* this object will be checked.
|
||||
*
|
||||
* @return Return whether the object has been modified
|
||||
*
|
||||
* This function tries to adjust any relative link properties (i.e. link
|
||||
* properties that can hold subnames) to avoid cyclic when added to the
|
||||
* future parent.
|
||||
*/
|
||||
virtual bool adjustRelativeLinks(const std::set<App::DocumentObject*> &inList,
|
||||
std::set<App::DocumentObject*> *visited=0);
|
||||
|
||||
/** Allow object to redirect a subname path
|
||||
*
|
||||
* @param ss: input as the current subname path from \a topParent leading
|
||||
@@ -404,7 +468,7 @@ public:
|
||||
virtual bool redirectSubName(std::ostringstream &ss,
|
||||
DocumentObject *topParent, DocumentObject *child) const;
|
||||
|
||||
/** Sepecial marker to mark the object has hidden
|
||||
/** Sepecial marker to mark the object as hidden
|
||||
*
|
||||
* It is used by Gui::ViewProvider::getElementColors(), but exposed here
|
||||
* for convenience
|
||||
|
||||
@@ -176,6 +176,11 @@ non-object sub-element name if any.
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="adjustRelativeLinks">
|
||||
<Documentation>
|
||||
<UserDocu>adjustRelativeLinks(parent,recursive=True) -- auto correct potential cyclic dependencies</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Attribute Name="OutList" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>A list of all objects this object links to.</UserDocu>
|
||||
|
||||
@@ -791,3 +791,19 @@ Py::List DocumentObjectPy::getParents() const {
|
||||
ret.append(Py::TupleN(Py::Object(v.first->getPyObject(),true),Py::String(v.second)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyObject *DocumentObjectPy::adjustRelativeLinks(PyObject *args) {
|
||||
PyObject *pyobj;
|
||||
PyObject *recursive = Py_True;
|
||||
if (!PyArg_ParseTuple(args, "O!|O",&DocumentObjectPy::Type,&pyobj,&recursive))
|
||||
return NULL;
|
||||
PY_TRY {
|
||||
auto obj = static_cast<DocumentObjectPy*>(pyobj)->getDocumentObjectPtr();
|
||||
auto inList = obj->getInListEx(true);
|
||||
inList.insert(obj);
|
||||
std::set<App::DocumentObject *> visited;
|
||||
return Py::new_reference_to(Py::Boolean(
|
||||
getDocumentObjectPtr()->adjustRelativeLinks(inList,
|
||||
PyObject_IsTrue(recursive)?&visited:nullptr)));
|
||||
}PY_CATCH
|
||||
}
|
||||
|
||||
@@ -146,6 +146,12 @@ public:
|
||||
/// Get valid paths for this property; used by auto completer
|
||||
virtual void getPaths(std::vector<App::ObjectIdentifier> & paths) const;
|
||||
|
||||
/// Called at the begining of Document::afterRestore(). See comments there.
|
||||
virtual void afterRestore() {}
|
||||
|
||||
/// Called before calling DocumentObject::onDocumentRestored()
|
||||
virtual void onContainerRestored() {}
|
||||
|
||||
/** Property status handling
|
||||
*/
|
||||
//@{
|
||||
@@ -189,6 +195,11 @@ public:
|
||||
/// Paste the value from the property (mainly for Undo/Redo and transactions)
|
||||
virtual void Paste(const Property &from) = 0;
|
||||
|
||||
/// Called when a child property has changed value
|
||||
virtual void hasSetChildValue(Property &) {}
|
||||
/// Called before a child property changing value
|
||||
virtual void aboutToSetChildValue(Property &) {}
|
||||
|
||||
friend class PropertyContainer;
|
||||
friend struct PropertyData;
|
||||
friend class DynamicProperty;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user