Link: change sub-element linking mechanism

Previous multi-sub-element linking (e.g. Face, Edge) is supported
through SubElements of type PropertyStringList, which does not support
geometry element update tracking. This patch changes it to use
PropertyXLink's new multi-subname capability.
This commit is contained in:
Zheng, Lei
2019-09-01 19:45:57 +08:00
committed by wmayer
parent 4042d83362
commit f57283f598
6 changed files with 139 additions and 101 deletions

View File

@@ -188,8 +188,7 @@ short LinkBaseExtension::extensionMustExecute(void) {
}
App::GroupExtension *LinkBaseExtension::linkedPlainGroup() const {
auto subs = getSubElementsProperty();
if(subs && subs->getSize())
if(mySubElements.size() && mySubElements[0].size())
return 0;
auto linked = getTrueLinkedObject(false);
if(!linked)
@@ -498,7 +497,7 @@ bool LinkBaseExtension::extensionGetSubObjects(std::vector<std::string> &ret, in
}
return true;
}
if(mySubElement.empty() && getSubElementsValue().empty()) {
if(mySubElements.empty() || mySubElements[0].empty()) {
DocumentObject *linked = getTrueLinkedObject(true);
if(linked) {
if(!_getElementCountValue())
@@ -511,6 +510,8 @@ bool LinkBaseExtension::extensionGetSubObjects(std::vector<std::string> &ret, in
}
}
}
} else if(mySubElements.size()>1) {
ret = mySubElements;
}
return true;
}
@@ -523,13 +524,17 @@ bool LinkBaseExtension::extensionGetSubObject(DocumentObject *&ret, const char *
auto obj = getContainer();
if(!subname || !subname[0]) {
ret = const_cast<DocumentObject*>(obj);
if(pyObj && !_getElementCountValue() && _getElementListValue().empty()) {
if(pyObj && !_getElementCountValue()
&& _getElementListValue().empty() && mySubElements.size()<=1)
{
Base::Matrix4D matNext;
if(mat) matNext = *mat;
auto linked = getTrueLinkedObject(false,mat?&matNext:0,depth);
if(linked && linked!=obj) {
if(mat) *mat = matNext;
linked->getSubObject(mySubElement.c_str(),pyObj,mat,false,depth+1);
linked->getSubObject(
mySubElements.empty()?0:mySubElements.front().c_str(),
pyObj,mat,false,depth+1);
checkGeoElementMap(obj,linked,pyObj,0);
}
}
@@ -544,8 +549,6 @@ bool LinkBaseExtension::extensionGetSubObject(DocumentObject *&ret, const char *
if(elements.size()) {
if(idx>=(int)elements.size() || !elements[idx] || !elements[idx]->getNameInDocument())
return true;
if(!subname || !subname[0])
subname = mySubElement.c_str();
ret = elements[idx]->getSubObject(subname,pyObj,mat,true,depth+1);
// do not resolve the link if this element is the last referenced object
if(!subname || Data::ComplexGeoData::isMappedElement(subname) || !strchr(subname,'.'))
@@ -576,8 +579,6 @@ bool LinkBaseExtension::extensionGetSubObject(DocumentObject *&ret, const char *
Base::Matrix4D matNext;
if(mat) matNext = *mat;
if(!subname || !subname[0])
subname = mySubElement.c_str();
ret = linked->getSubObject(subname,pyObj,mat?&matNext:0,false,depth+1);
std::string postfix;
if(ret) {
@@ -668,15 +669,36 @@ void LinkBaseExtension::extensionOnChanged(const Property *prop) {
}
void LinkBaseExtension::parseSubName() const {
auto xlink = freecad_dynamic_cast<const PropertyXLink>(getLinkedObjectProperty());
const char* subname = xlink?xlink->getSubName():0;
// If user has ever linked to some sub-element, the Link shall always accept
// sub-element linking in the future, which affects how ViewProviderLink
// dropObjectEx() behave. So we will push an empty string later even if no
// sub-element linking this time.
bool hasSubElement = !mySubElements.empty();
mySubElements.clear();
mySubName.clear();
mySubElement.clear();
if(!subname || !subname[0])
auto xlink = freecad_dynamic_cast<const PropertyXLink>(getLinkedObjectProperty());
if(!xlink || xlink->getSubValues().empty()) {
if(hasSubElement)
mySubElements.emplace_back("");
return;
}
const auto &subs = xlink->getSubValues();
auto subname = subs.front().c_str();
auto element = Data::ComplexGeoData::findElementName(subname);
if(!element || !element[0]) {
mySubName = subs[0];
if(hasSubElement)
mySubElements.emplace_back("");
return;
}
mySubElements.push_back(element);
mySubName = std::string(subname,element-subname);
mySubElement = element;
for(std::size_t i=1;i<subs.size();++i) {
auto &sub = subs[i];
element = Data::ComplexGeoData::findElementName(sub.c_str());
if(element && element[0] && boost::starts_with(sub,mySubName))
mySubElements.push_back(element);
}
}
void LinkBaseExtension::slotChangedPlainGroup(const App::DocumentObject &obj, const App::Property &prop) {
@@ -982,8 +1004,6 @@ void LinkBaseExtension::update(App::DocumentObject *parent, const Property *prop
_ChildCache.setValue();
parseSubName();
syncElementList();
}else if(prop == getSubElementsProperty()) {
syncElementList();
}else if(prop == getLinkTransformProperty()) {
auto linkPlacement = getLinkPlacementProperty();
auto placement = getPlacementProperty();
@@ -1019,14 +1039,9 @@ bool LinkBaseExtension::linkTransform() const {
}
void LinkBaseExtension::syncElementList() {
const auto &subElements = getSubElementsValue();
auto sub = getSubElementsProperty();
auto transform = getLinkTransformProperty();
auto link = getLinkedObjectProperty();
auto xlink = freecad_dynamic_cast<const PropertyXLink>(link);
std::string subname;
if(xlink)
subname = xlink->getSubName();
auto owner = getContainer();
auto ownerID = owner?owner->getID():0;
@@ -1038,9 +1053,6 @@ void LinkBaseExtension::syncElementList() {
element->myOwner = ownerID;
element->SubElements.setStatus(Property::Hidden,sub!=0);
element->SubElements.setStatus(Property::Immutable,sub!=0);
element->LinkTransform.setStatus(Property::Hidden,transform!=0);
element->LinkTransform.setStatus(Property::Immutable,transform!=0);
if(transform && element->LinkTransform.getValue()!=transform->getValue())
@@ -1048,13 +1060,16 @@ void LinkBaseExtension::syncElementList() {
element->LinkedObject.setStatus(Property::Hidden,link!=0);
element->LinkedObject.setStatus(Property::Immutable,link!=0);
if(link) {
if(element->LinkedObject.getValue()!=link->getValue() ||
subname != element->LinkedObject.getSubName() ||
subElements != element->SubElements.getValue())
if(xlink) {
if(element->LinkedObject.getValue()!=xlink->getValue() ||
element->LinkedObject.getSubValues()!=xlink->getSubValues())
{
element->setLink(-1,link->getValue(),subname.c_str(),subElements);
element->LinkedObject.setValue(xlink->getValue(), xlink->getSubValues());
}
} else if(element->LinkedObject.getValue()!=link->getValue() ||
element->LinkedObject.getSubValues().size())
{
element->setLink(-1,link->getValue());
}
}
}
@@ -1167,10 +1182,6 @@ void LinkBaseExtension::setLink(int index, DocumentObject *obj,
// Here means we are assigning a Link
auto xlink = freecad_dynamic_cast<PropertyXLink>(linkProp);
auto subElementProp = getSubElementsProperty();
if(subElements.size() && !subElementProp)
LINK_THROW(Base::RuntimeError,"No SubElements Property configured");
if(obj) {
if(!obj->getNameInDocument())
LINK_THROW(Base::ValueError,"Invalid document object");
@@ -1180,18 +1191,23 @@ void LinkBaseExtension::setLink(int index, DocumentObject *obj,
}
}
if(subname && subname[0] && !xlink)
LINK_THROW(Base::RuntimeError,"SubName link requires PropertyXLink");
if(subElementProp && subElements.size()) {
subElementProp->setStatus(Property::User3, true);
subElementProp->setValue(subElements);
subElementProp->setStatus(Property::User3, false);
}
if(xlink)
xlink->setValue(obj,subname);
else
if(!xlink) {
if(subElements.size() || (subname && subname[0]))
LINK_THROW(Base::RuntimeError,"SubName/SubElement link requires PropertyXLink");
linkProp->setValue(obj);
return;
}
std::vector<std::string> subs;
if(subElements.size()) {
subs.reserve(subElements.size());
for(const auto &s : subElements) {
subs.emplace_back(subname?subname:"");
subs.back() += s;
}
} else if(subname && subname[0])
subs.emplace_back(subname);
xlink->setValue(obj,std::move(subs));
}
void LinkBaseExtension::detachElement(DocumentObject *obj) {