diff --git a/src/App/GeoFeatureGroupExtension.cpp b/src/App/GeoFeatureGroupExtension.cpp index 86aef31170..70ecdb4678 100644 --- a/src/App/GeoFeatureGroupExtension.cpp +++ b/src/App/GeoFeatureGroupExtension.cpp @@ -364,7 +364,7 @@ bool GeoFeatureGroupExtension::extensionGetSubObject(DocumentObject *&ret, const *mat *= const_cast(this)->placement().getValue().toMatrix(); }else if((dot=strchr(subname,'.'))) { if(subname[0]!='$') - ret = Group.find(std::string(subname,dot)); + ret = Group.findUsingMap(std::string(subname,dot)); else{ std::string name = std::string(subname+1,dot); for(auto child : Group.getValues()) { diff --git a/src/App/GroupExtension.cpp b/src/App/GroupExtension.cpp index 3859b8594a..7e47743846 100644 --- a/src/App/GroupExtension.cpp +++ b/src/App/GroupExtension.cpp @@ -381,7 +381,7 @@ bool GroupExtension::extensionGetSubObject(DocumentObject *&ret, const char *sub if(!dot) return false; if(subname[0]!='$') - ret = Group.find(std::string(subname,dot)); + ret = Group.findUsingMap(std::string(subname,dot)); else{ std::string name = std::string(subname+1,dot); for(auto child : Group.getValues()) { diff --git a/src/App/Link.cpp b/src/App/Link.cpp index 4a9933378f..174548f676 100644 --- a/src/App/Link.cpp +++ b/src/App/Link.cpp @@ -1147,7 +1147,7 @@ int LinkBaseExtension::getElementIndex(const char *subname, const char **psubnam // Try search by element objects' name std::string name(subname,dot); if(_ChildCache.getSize()) { - auto obj=_ChildCache.find(name,&idx); + auto obj=_ChildCache.findUsingMap(name,&idx); if(obj) { auto group = obj->getExtensionByType(true,false); if(group) { diff --git a/src/App/PropertyExpressionEngine.cpp b/src/App/PropertyExpressionEngine.cpp index 0eac59bf2a..02f21654ec 100644 --- a/src/App/PropertyExpressionEngine.cpp +++ b/src/App/PropertyExpressionEngine.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -1079,3 +1080,48 @@ void PropertyExpressionEngine::onRelabeledDocument(const App::Document &doc) e.second.expression->visit(v); } } + +void PropertyExpressionEngine::getLinksTo(std::vector& identifiers, + App::DocumentObject* obj, + const char* subname, + bool all) const +{ + Expression::DepOption option = + all ? Expression::DepOption::DepAll : Expression::DepOption::DepNormal; + + App::SubObjectT objT(obj, subname); + auto sobj = objT.getSubObject(); + auto subElement = objT.getOldElementName(); + + for (auto& [expressionId, expressionInfo] : expressions) { + const auto& deps = expressionInfo.expression->getDeps(option); + auto it = deps.find(obj); + if (it == deps.end()) { + continue; + } + auto [docObj, map] = *it; + for (auto& [key, paths] : map) { + if (!subname) { + identifiers.push_back(expressionId); + break; + } + bool found = false; + for (const auto& path : paths) { + if (path.getSubObjectName() == subname) { + identifiers.push_back(expressionId); + found = true; + break; + } + App::SubObjectT sobjT(obj, path.getSubObjectName().c_str()); + if (sobjT.getSubObject() == sobj && sobjT.getOldElementName() == subElement) { + identifiers.push_back(expressionId); + found = true; + break; + } + } + if (found) { + break; + } + } + } +} diff --git a/src/App/PropertyExpressionEngine.h b/src/App/PropertyExpressionEngine.h index 05f9febd3e..248a1c1323 100644 --- a/src/App/PropertyExpressionEngine.h +++ b/src/App/PropertyExpressionEngine.h @@ -160,6 +160,11 @@ public: void afterRestore() override; void onContainerRestored() override; + void getLinksTo(std::vector &identifiers, + App::DocumentObject *obj, + const char *subname=nullptr, + bool all=false) const override; + /* Python interface */ PyObject *getPyObject() override; void setPyObject(PyObject *) override; diff --git a/src/App/PropertyLinks.cpp b/src/App/PropertyLinks.cpp index d68a371f8d..b7f81199cc 100644 --- a/src/App/PropertyLinks.cpp +++ b/src/App/PropertyLinks.cpp @@ -37,6 +37,7 @@ #include "Document.h" #include "DocumentObject.h" #include "DocumentObjectPy.h" +#include "DocumentObserver.h" #include "ObjectIdentifier.h" #include "ElementNamingUtils.h" #include "GeoFeature.h" @@ -71,6 +72,14 @@ void PropertyLinkBase::setAllowExternal(bool allow) { setFlag(LinkAllowExternal,allow); } +void PropertyLinkBase::setSilentRestore(bool allow) { + setFlag(LinkSilentRestore,allow); +} + +void PropertyLinkBase::setReturnNewElement(bool enable) { + setFlag(LinkNewElement, enable); +} + void PropertyLinkBase::hasSetValue() { auto owner = dynamic_cast(getContainer()); if(owner) @@ -508,7 +517,7 @@ bool PropertyLinkBase::_updateElementReference(DocumentObject *feature, (void)obj; (void)reverse; (void)notify; - shadow.second = sub; + shadow.oldName = sub; return false; #endif } @@ -791,6 +800,17 @@ void PropertyLink::getLinks(std::vector &objs, objs.push_back(_pcLink); } +void PropertyLink::getLinksTo(std::vector& identifiers, + App::DocumentObject* obj, + const char* subname, + bool all) const +{ + (void)subname; + if ((all || _pcScope != LinkScope::Hidden) && obj && _pcLink == obj) { + identifiers.emplace_back(*this); + } +} + void PropertyLink::breakLink(App::DocumentObject *obj, bool clear) { if(_pcLink == obj || (clear && getContainer()==obj)) setValue(nullptr); @@ -1075,7 +1095,25 @@ unsigned int PropertyLinkList::getMemSize() const return static_cast(_lValueList.size() * sizeof(App::DocumentObject *)); } -DocumentObject *PropertyLinkList::find(const std::string &name, int *pindex) const { + +DocumentObject *PropertyLinkList::find(const char *name, int *pindex) const { + const int DONT_MAP_UNDER = 10; + if (!name) + return nullptr; + if(_lValueList.size() <= DONT_MAP_UNDER ) { + int index = -1; + for (auto obj : _lValueList) { + ++index; + if(obj && obj->getNameInDocument() + && boost::equals(name, obj->getNameInDocument())) { + if(pindex) + *pindex = index; + return obj; + } + } + return nullptr; + } + // We're using a map. Do we need to (re)create it? if(_nameMap.empty() || _nameMap.size()>_lValueList.size()) { _nameMap.clear(); for(int i=0;i<(int)_lValueList.size();++i) { @@ -1084,6 +1122,7 @@ DocumentObject *PropertyLinkList::find(const std::string &name, int *pindex) con _nameMap[obj->getNameInDocument()] = i; } } + // Now lookup up in that map auto it = _nameMap.find(name); if(it == _nameMap.end()) return nullptr; @@ -1091,6 +1130,17 @@ DocumentObject *PropertyLinkList::find(const std::string &name, int *pindex) con return _lValueList[it->second]; } +DocumentObject *PropertyLinkList::findUsingMap(const std::string &name, int *pindex) const { + if (_nameMap.size() == _lValueList.size()) { + auto it = _nameMap.find(name); + if(it == _nameMap.end()) + return nullptr; + if(pindex) *pindex = it->second; + return _lValueList[it->second]; + } + return find(name.c_str(), pindex); +} + void PropertyLinkList::getLinks(std::vector &objs, bool all, std::vector *subs, bool newStyle) const { @@ -1105,6 +1155,25 @@ void PropertyLinkList::getLinks(std::vector &objs, } } +void PropertyLinkList::getLinksTo(std::vector& identifiers, + App::DocumentObject* obj, + const char* subname, + bool all) const +{ + (void)subname; + if (!obj || (!all && _pcScope == LinkScope::Hidden)) { + return; + } + int i = -1; + for (auto o : _lValueList) { + ++i; + if (o == obj) { + identifiers.emplace_back(*this, i); + break; + } + } +} + void PropertyLinkList::breakLink(App::DocumentObject *obj, bool clear) { if(clear && getContainer()==obj) { setValues({}); @@ -1287,7 +1356,7 @@ PyObject *PropertyLinkSub::getPyObject() tup[0] = Py::asObject(_pcLinkSub->getPyObject()); #ifdef FC_USE_TNP_FIX int i = 0; - for (auto &sub : getSubValues(true)) + for (auto &sub : getSubValues(testFlag(LinkNewElement))) list[i++] = Py::String(sub); #else for(unsigned int i = 0;i<_cSubList.size(); i++) @@ -1565,6 +1634,58 @@ std::string PropertyLinkBase::tryImportSubName(const App::DocumentObject *obj, c return {}; } +void PropertyLinkBase::_getLinksTo(std::vector& identifiers, + App::DocumentObject* obj, + const char* subname, + const std::vector& subs, + const std::vector& shadows) const +{ + if (!subname) { + identifiers.emplace_back(*this); + return; + } + App::SubObjectT objT(obj, subname); + auto subObject = objT.getSubObject(); + auto subElement = objT.getOldElementName(); + + int i = -1; + for (const auto& sub : subs) { + ++i; + if (sub == subname) { + identifiers.emplace_back(*this); + return; + } + if (!subObject) { + continue; + } + // There is a subobject and the subname doesn't match our current entry + App::SubObjectT sobjT(obj, sub.c_str()); + if (sobjT.getSubObject() == subObject && sobjT.getOldElementName() == subElement) { + identifiers.emplace_back(*this); + return; + } + // The oldElementName ( short, I.E. "Edge5" ) doesn't match. + if (i < (int)shadows.size()) { + const auto& [shadowNewName, shadowOldName] = shadows[i]; + if (shadowNewName == subname || shadowOldName == subname) { + identifiers.emplace_back(*this); + return; + } + if (!subObject) { + continue; + } + App::SubObjectT shadowobjT(obj, + shadowNewName.empty() ? shadowOldName.c_str() + : shadowNewName.c_str()); + if (shadowobjT.getSubObject() == subObject + && shadowobjT.getOldElementName() == subElement) { + identifiers.emplace_back(*this); + return; + } + } + } +} + #define ATTR_SHADOWED "shadowed" #define ATTR_SHADOW "shadow" #define ATTR_MAPPED "mapped" @@ -1793,6 +1914,18 @@ void PropertyLinkSub::getLinks(std::vector &objs, } } +void PropertyLinkSub::getLinksTo(std::vector& identifiers, + App::DocumentObject* obj, + const char* subname, + bool all) const +{ + if (all || _pcScope != LinkScope::Hidden) { + if (obj && obj == _pcLinkSub) { + _getLinksTo(identifiers, obj, subname, _cSubList, _ShadowSubList); + } + } +} + void PropertyLinkSub::breakLink(App::DocumentObject *obj, bool clear) { if(obj == _pcLinkSub || (clear && getContainer()==obj)) setValue(nullptr); @@ -2763,6 +2896,49 @@ void PropertyLinkSubList::getLinks(std::vector &objs, } } +void PropertyLinkSubList::getLinksTo(std::vector& identifiers, + App::DocumentObject* obj, + const char* subname, + bool all) const +{ + if (!obj || (!all && _pcScope == LinkScope::Hidden)) { + return; + } + App::SubObjectT objT(obj, subname); + auto subObject = objT.getSubObject(); + auto subElement = objT.getOldElementName(); + + int i = -1; + for (const auto& o : _lValueList) { + ++i; + if (o != obj) { + continue; + } + if (!subname || (i < (int)_lSubList.size() && subname == _lSubList[i])) { + identifiers.emplace_back(*this, i); + continue; + } + if (!subObject || i < (int)_lSubList.size()) { + continue; + } + App::SubObjectT sobjT(obj, _lSubList[i].c_str()); + if (sobjT.getSubObject() == subObject && sobjT.getOldElementName() == subElement) { + identifiers.emplace_back(*this); + continue; + } + if (i < (int)_ShadowSubList.size()) { + const auto& shadow = _ShadowSubList[i]; + App::SubObjectT sobjT(obj, + shadow.newName.empty() ? shadow.oldName.c_str() + : shadow.newName.c_str()); + if (sobjT.getSubObject() == subObject && sobjT.getOldElementName() == subElement) { + identifiers.emplace_back(*this); + continue; + } + } + } +} + void PropertyLinkSubList::breakLink(App::DocumentObject *obj, bool clear) { std::vector values; std::vector subs; @@ -3502,6 +3678,8 @@ int PropertyXLink::checkRestore(std::string *msg) const { return 0; } if(!_pcLink) { + if (testFlag(LinkSilentRestore)) + return 0; if(testFlag(LinkAllowPartial) && (!docInfo->pcDoc || docInfo->pcDoc->testStatus(App::Document::PartialDoc))) @@ -3598,6 +3776,7 @@ void PropertyXLink::Save (Base::Writer &writer) const { << "pcDoc?docInfo->pcDoc->LastModifiedDate.getValue():"") << "\" name=\"" << objectName; + } if(testFlag(LinkAllowPartial)) @@ -3665,6 +3844,7 @@ void PropertyXLink::Restore(Base::XMLReader &reader) stampAttr = reader.getAttribute("stamp"); if(reader.hasAttribute("file")) file = reader.getAttribute("file"); + setFlag(LinkAllowPartial, reader.hasAttribute("partial") && reader.getAttributeAsInteger("partial")); @@ -4013,6 +4193,17 @@ void PropertyXLink::getLinks(std::vector &objs, } } +void PropertyXLink::getLinksTo(std::vector &identifiers, + App::DocumentObject *obj, + const char *subname, + bool all) const { + if (all || _pcScope != LinkScope::Hidden) { + if (obj && obj == _pcLink) { + _getLinksTo(identifiers, obj, subname, _SubList, _ShadowSubList); + } + } +} + bool PropertyXLink::adjustLink(const std::set &inList) { if (_pcScope==LinkScope::Hidden) return false; @@ -4667,6 +4858,75 @@ void PropertyXLinkSubList::getLinks(std::vector &objs, } } +// Same algorithm as _getLinksTo above, but returns all matches +void PropertyXLinkSubList::_getLinksToList( + std::vector& identifiers, + App::DocumentObject* obj, + const char* subname, + const std::vector& subs, + const std::vector& shadows) const +{ + if (!subname) { + identifiers.emplace_back(*this); + return; + } + App::SubObjectT objT(obj, subname); + auto subObject = objT.getSubObject(); + auto subElement = objT.getOldElementName(); + + int i = -1; + for (const auto& sub : subs) { + ++i; + if (sub == subname) { + identifiers.emplace_back(*this, i); + continue; + } + if (!subObject) { + continue; + } + // There is a subobject and the subname doesn't match our current entry + App::SubObjectT sobjT(obj, sub.c_str()); + if (sobjT.getSubObject() == subObject && sobjT.getOldElementName() == subElement) { + identifiers.emplace_back(*this, i); + continue; + } + // The oldElementName ( short, I.E. "Edge5" ) doesn't match. + if (i < (int)shadows.size()) { + const auto& [shadowNewName, shadowOldName] = shadows[i]; + if (shadowNewName == subname || shadowOldName == subname) { + identifiers.emplace_back(*this, i); + continue; + } + if (!subObject) { + continue; + } + App::SubObjectT shadowobjT(obj, + shadowNewName.empty() ? shadowOldName.c_str() + : shadowNewName.c_str()); + if (shadowobjT.getSubObject() == subObject + && shadowobjT.getOldElementName() == subElement) { + identifiers.emplace_back(*this, i); + continue; + } + } + } +} + +void PropertyXLinkSubList::getLinksTo(std::vector& identifiers, + App::DocumentObject* obj, + const char* subname, + bool all) const +{ + if (all || _pcScope != LinkScope::Hidden) { + for (auto& l : _Links) { + // This is the same algorithm as _getLinksTo, but returns lists, not single entries + if (obj && obj == l._pcLink) { + _getLinksToList(identifiers, obj, subname, l._SubList, l._ShadowSubList); + } + } + } +} + void PropertyXLinkSubList::breakLink(App::DocumentObject *obj, bool clear) { if(clear && getContainer()==obj) { setValue(nullptr); @@ -4858,8 +5118,10 @@ void PropertyXLinkContainer::afterRestore() { if(info.docLabel != obj->getDocument()->Label.getValue()) _DocMap[App::quote(info.docLabel)] = obj->getDocument()->Label.getValue(); } - if(_Deps.insert(std::make_pair(obj,info.xlink->getScope()==LinkScope::Hidden)).second) + if(_Deps.insert(std::make_pair(obj,info.xlink->getScope()==LinkScope::Hidden)).second) { _XLinks[obj->getFullName()] = std::move(info.xlink); + onAddDep(obj); + } } _XLinkRestores.reset(); } @@ -4881,6 +5143,7 @@ void PropertyXLinkContainer::breakLink(App::DocumentObject *obj, bool clear) { else if (!it->second) obj->_removeBackLink(owner); _Deps.erase(it); + onRemoveDep(obj); hasSetValue(); return; } @@ -5003,14 +5266,30 @@ void PropertyXLinkContainer::Restore(Base::XMLReader &reader) { void PropertyXLinkContainer::aboutToSetChildValue(App::Property &prop) { auto xlink = dynamic_cast(&prop); if(xlink && xlink->testFlag(LinkDetached)) { - if(_Deps.erase(const_cast(xlink->getValue()))) - onBreakLink(xlink->getValue()); + auto obj = const_cast(xlink->getValue()); + if(_Deps.erase(obj)) { + _onBreakLink(xlink->getValue()); + onRemoveDep(obj); + } } } void PropertyXLinkContainer::onBreakLink(DocumentObject *) { } +void PropertyXLinkContainer::_onBreakLink(DocumentObject *obj) { + try { + onBreakLink(obj); + } catch (Base::Exception &e) { + e.ReportException(); + FC_ERR("Exception on breaking link property " << getFullName()); + } catch (std::exception &e) { + FC_ERR("Exception on breaking link property " << getFullName() << ": " << e.what()); + } catch (...) { + FC_ERR("Exception on breaking link property " << getFullName()); + } +} + PropertyXLink *PropertyXLinkContainer::createXLink() { return new PropertyXLink(false,this); } diff --git a/src/App/PropertyLinks.h b/src/App/PropertyLinks.h index 29b7a7e547..dd17cc3448 100644 --- a/src/App/PropertyLinks.h +++ b/src/App/PropertyLinks.h @@ -31,7 +31,6 @@ #include #include -// #include "GeoFeature.h" #include "Property.h" namespace Base { @@ -184,6 +183,19 @@ public: virtual void getLinks(std::vector &objs, bool all=false, std::vector *subs=nullptr, bool newStyle=true) const = 0; + /** Obtain identifiers from this link property that link to a give object + * @param identifiers: holds the returned identifier to reference the given object + * @param obj: the referenced object + * @param subname: optional subname reference + * @param all: if true, then return all the references regardless of + * this LinkScope. If false, then return only if the LinkScope + * is not hidden. + */ + virtual void getLinksTo(std::vector &identifiers, + App::DocumentObject *obj, + const char *subname=nullptr, + bool all=false) const = 0; + /** Called to reset this link property * * @param obj: reset link property if it is linked to this object @@ -546,6 +558,8 @@ public: LinkAllowPartial, LinkRestoreLabel, LinkSyncSubObject, // used by DlgPropertyLink + LinkNewElement, // return new element name in getPyObject + LinkSilentRestore, // do not report error on restore (e.g. missing external link) }; inline bool testFlag(int flag) const { return _Flags.test((std::size_t)flag); @@ -553,6 +567,12 @@ public: virtual void setAllowPartial(bool enable) { (void)enable; } + void setReturnNewElement(bool enable); + + void setSilentRestore(bool enable); + + boost::signals2::signal signalUpdateElementReference; + protected: void hasSetValue() override; @@ -562,6 +582,13 @@ protected: _Flags.set((std::size_t)flag,value); } + void _getLinksTo( + std::vector &identifiers, + App::DocumentObject *obj, + const char *subname, + const std::vector &subs, + const std::vector &shadows) const; + private: std::set _LabelRefs; std::set _ElementRefs; @@ -631,6 +658,11 @@ public: void getLinks(std::vector &objs, bool all=false, std::vector *subs=nullptr, bool newStyle=true) const override; + void getLinksTo(std::vector &identifiers, + App::DocumentObject *obj, + const char *subname=nullptr, + bool all=false) const override; + void breakLink(App::DocumentObject *obj, bool clear) override; bool adjustLink(const std::set &inList) override; @@ -721,6 +753,11 @@ public: void getLinks(std::vector &objs, bool all=false, std::vector *subs=nullptr, bool newStyle=true) const override; + void getLinksTo(std::vector &identifiers, + App::DocumentObject *obj, + const char *subname=nullptr, + bool all=false) const override; + void breakLink(App::DocumentObject *obj, bool clear) override; bool adjustLink(const std::set &inList) override; @@ -728,11 +765,8 @@ public: Property *CopyOnLinkReplace(const App::DocumentObject *parent, App::DocumentObject *oldObj, App::DocumentObject *newObj) const override; - DocumentObject *find(const std::string &, int *pindex=nullptr) const; - DocumentObject *find(const char *sub, int *pindex=nullptr) const { - if(!sub) return nullptr; - return find(std::string(sub),pindex); - } + DocumentObject *findUsingMap(const std::string &, int *pindex=nullptr) const; + DocumentObject *find(const char *sub, int *pindex=nullptr) const; protected: DocumentObject *getPyValue(PyObject *item) const override; @@ -862,6 +896,11 @@ public: void getLinks(std::vector &objs, bool all=false, std::vector *subs=nullptr, bool newStyle=true) const override; + void getLinksTo(std::vector &identifiers, + App::DocumentObject *obj, + const char *subname=nullptr, + bool all=false) const override; + void breakLink(App::DocumentObject *obj, bool clear) override; bool adjustLink(const std::set &inList) override; @@ -1009,6 +1048,11 @@ public: void getLinks(std::vector &objs, bool all=false, std::vector *subs=nullptr, bool newStyle=true) const override; + void getLinksTo(std::vector &identifiers, + App::DocumentObject *obj, + const char *subname=nullptr, + bool all=false) const override; + void breakLink(App::DocumentObject *obj, bool clear) override; bool adjustLink(const std::set &inList) override; @@ -1132,6 +1176,11 @@ public: void getLinks(std::vector &objs, bool all=false, std::vector *subs=nullptr, bool newStyle=true) const override; + void getLinksTo(std::vector &identifiers, + App::DocumentObject *obj, + const char *subname=nullptr, + bool all=false) const override; + bool adjustLink(const std::set &inList) override; const std::vector& getSubValues() const { @@ -1302,6 +1351,11 @@ public: void getLinks(std::vector &objs, bool all=false, std::vector *subs=nullptr, bool newStyle=true) const override; + void getLinksTo(std::vector &identifiers, + App::DocumentObject *obj, + const char *subname=nullptr, + bool all=false) const override; + void breakLink(App::DocumentObject *obj, bool clear) override; bool adjustLink(const std::set &inList) override; @@ -1317,6 +1371,14 @@ public: void setSyncSubObject(bool enable); +protected: + void _getLinksToList( + std::vector &identifiers, + App::DocumentObject *obj, + const char *subname, + const std::vector &subs, + const std::vector &shadows) const; + protected: std::list _Links; }; @@ -1369,6 +1431,8 @@ protected: void updateDeps(std::map &&newDeps); void clearDeps(); + void _onBreakLink(App::DocumentObject *obj); + protected: std::map _Deps; std::map > _XLinks; diff --git a/src/Mod/Part/App/PropertyGeometryList.cpp b/src/Mod/Part/App/PropertyGeometryList.cpp index 23f9660721..2f33c4d958 100644 --- a/src/Mod/Part/App/PropertyGeometryList.cpp +++ b/src/Mod/Part/App/PropertyGeometryList.cpp @@ -27,6 +27,7 @@ #include #include "PropertyGeometryList.h" +#include "GeometryMigrationExtension.h" #include "GeometryPy.h" #include "Part2DObject.h" @@ -84,26 +85,43 @@ void PropertyGeometryList::setValue(const Geometry* lValue) void PropertyGeometryList::setValues(const std::vector& lValue) { auto copy = lValue; - for(auto &geo : copy) // copy of the individual geometry pointers - geo = geo->clone(); - - setValues(std::move(copy)); + aboutToSetValue(); + std::sort(_lValueList.begin(), _lValueList.end()); + for (auto & geo : copy) { + auto range = std::equal_range(_lValueList.begin(), _lValueList.end(), geo); + // clone if the new entry does not exist in the original value list, or + // else, simply reuse it (i.e. erase it so that it won't get deleted below). + if (range.first == range.second) + geo = geo->clone(); + else + _lValueList.erase(range.first, range.second); + } + for (auto v : _lValueList) + delete v; + _lValueList = std::move(copy); + hasSetValue(); } void PropertyGeometryList::setValues(std::vector &&lValue) { + // Unlike above, the moved version of setValues() indicates the caller want + // us to manager the memory of the passed in values. So no need clone. aboutToSetValue(); - std::set valueSet(_lValueList.begin(),_lValueList.end()); - for(auto v : lValue) - valueSet.erase(v); + std::sort(_lValueList.begin(), _lValueList.end()); + for (auto geo : lValue) { + auto range = std::equal_range(_lValueList.begin(), _lValueList.end(), geo); + _lValueList.erase(range.first, range.second); + } + for (auto geo : _lValueList) + delete geo; _lValueList = std::move(lValue); - for(auto v : valueSet) - delete v; hasSetValue(); } void PropertyGeometryList::set1Value(int idx, std::unique_ptr &&lValue) { + if (!lValue) + return; if(idx>=(int)_lValueList.size()) throw Base::IndexError("Index out of bound"); aboutToSetValue(); @@ -171,6 +189,12 @@ void PropertyGeometryList::trySaveGeometry(Geometry * geom, Base::Writer &writer // Not all geometry classes implement Save() and throw an exception instead try { geom->Save(writer); + for( auto & ext : geom->getExtensions() ) { + auto extension = ext.lock(); + auto gpe = freecad_dynamic_cast(extension.get()); + if (gpe) + gpe->postSave(writer); + } } catch (const Base::NotImplementedError& e) { Base::Console().Warning(std::string("PropertyGeometryList"), "Not yet implemented: %s\n", e.what()); @@ -181,6 +205,17 @@ void PropertyGeometryList::tryRestoreGeometry(Geometry * geom, Base::XMLReader & { // Not all geometry classes implement Restore() and throw an exception instead try { + if (!reader.getAttributeAsInteger("migrated", "0") && reader.hasAttribute("id")) { + auto ext = std::make_unique(); + ext->setId(reader.getAttributeAsInteger("id")); + if(reader.hasAttribute("ref")) { + const char *ref = reader.getAttribute("ref"); + int index = reader.getAttributeAsInteger("refIndex", "1"); + unsigned long flags = (unsigned long)reader.getAttributeAsUnsigned("flags"); + ext->setReference(ref, index, flags); + } + geom->setExtension(std::move(ext)); + } geom->Restore(reader); } catch (const Base::NotImplementedError& e) { @@ -193,8 +228,16 @@ void PropertyGeometryList::Save(Writer &writer) const writer.Stream() << writer.ind() << "" << endl; writer.incInd(); for (int i = 0; i < getSize(); i++) { - writer.Stream() << writer.ind() << "getTypeId().getName() << "\">" << endl;; + writer.Stream() << writer.ind() << "getTypeId().getName() << "\"" << endl; + for( auto &e : _lValueList[i]->getExtensions() ) { + auto ext = e.lock(); + auto gpe = freecad_dynamic_cast(ext.get()); + if (gpe) + gpe->preSave(writer); + } + writer.Stream() << " migrated=\"1\">\n"; + writer.incInd(); trySaveGeometry(_lValueList[i], writer); writer.decInd(); @@ -263,3 +306,10 @@ unsigned int PropertyGeometryList::getMemSize() const size += _lValueList[i]->getMemSize(); return size; } + +void PropertyGeometryList::moveValues(PropertyGeometryList &&other) +{ + setValues(std::move(other._lValueList)); +} + + diff --git a/src/Mod/Part/App/PropertyGeometryList.h b/src/Mod/Part/App/PropertyGeometryList.h index a8aa343512..c9b6498232 100644 --- a/src/Mod/Part/App/PropertyGeometryList.h +++ b/src/Mod/Part/App/PropertyGeometryList.h @@ -64,8 +64,10 @@ public: void setValues(const std::vector&); void setValues(std::vector&&); + void moveValues(PropertyGeometryList &&other); + /// index operator - const Geometry *operator[] (const int idx) const { + Geometry *operator[] (const int idx) const { return _lValueList[idx]; } diff --git a/src/Mod/Part/App/PropertyTopoShape.cpp b/src/Mod/Part/App/PropertyTopoShape.cpp index 4b5fb2f413..82b4050f01 100644 --- a/src/Mod/Part/App/PropertyTopoShape.cpp +++ b/src/Mod/Part/App/PropertyTopoShape.cpp @@ -303,8 +303,14 @@ void PropertyPartShape::Save (Base::Writer &writer) const writer.Stream() << " file=\"" << writer.addFile(getFileName(binary?".bin":".brp").c_str(), this) << "\"/>\n"; + } else if(binary) { + writer.Stream() << " binary=\"1\">\n"; + _Shape.exportBinary(writer.beginCharStream(Base::CharStreamFormat::Base64Encoded)); + writer.endCharStream() << writer.ind() << "\n"; } else { - writer.Stream() << "/>\n"; + writer.Stream() << " brep=\"1\">\n"; + _Shape.exportBrep(writer.beginCharStream(Base::CharStreamFormat::Raw)<<'\n'); + writer.endCharStream() << '\n' << writer.ind() << "\n"; } if(_SaveHasher) { diff --git a/src/Mod/PartDesign/App/Feature.cpp b/src/Mod/PartDesign/App/Feature.cpp index 1614a9c94b..d45d37590a 100644 --- a/src/Mod/PartDesign/App/Feature.cpp +++ b/src/Mod/PartDesign/App/Feature.cpp @@ -423,7 +423,7 @@ App::DocumentObject *Feature::getSubObject(const char *subname, if (dot) { auto body = PartDesign::Body::findBodyOf(this); if (body) { - auto feat = body->Group.find(std::string(subname, dot)); + auto feat = body->Group.findUsingMap(std::string(subname, dot)); if (feat) { Base::Matrix4D _mat; if (!transform) { diff --git a/src/Mod/Spreadsheet/App/PropertySheet.cpp b/src/Mod/Spreadsheet/App/PropertySheet.cpp index 742f380d6d..4473de6e00 100644 --- a/src/Mod/Spreadsheet/App/PropertySheet.cpp +++ b/src/Mod/Spreadsheet/App/PropertySheet.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -2267,3 +2268,52 @@ bool PropertySheet::hasSpan() const { return !mergedCells.empty(); } + +void PropertySheet::getLinksTo(std::vector& identifiers, + App::DocumentObject* obj, + const char* subname, + bool all) const +{ + Expression::DepOption option = + all ? Expression::DepOption::DepAll : Expression::DepOption::DepNormal; + + App::SubObjectT objT(obj, subname); + auto subObject = objT.getSubObject(); + auto subElement = objT.getOldElementName(); + + auto owner = Base::freecad_dynamic_cast(getContainer()); + for (const auto& [cellName, cellExpression] : data) { + if (auto expr = cellExpression->getExpression()) { + const auto& deps = expr->getDeps(option); + auto it = deps.find(obj); + if (it == deps.end()) { + continue; + } + const auto [docObj, depsList] = *it; + for (auto& [depName, paths] : depsList) { + if (!subname) { + identifiers.emplace_back(owner, cellName.toString().c_str()); + break; + } + bool found = false; + for (const auto& path : paths) { + if (path.getSubObjectName() == subname) { + identifiers.emplace_back(owner, cellName.toString().c_str()); + found = true; + break; + } + App::SubObjectT sobjT(obj, path.getSubObjectName().c_str()); + if (sobjT.getSubObject() == subObject + && sobjT.getOldElementName() == subElement) { + identifiers.emplace_back(owner, cellName.toString().c_str()); + found = true; + break; + } + } + if (found) { + break; + } + } + } + } +} diff --git a/src/Mod/Spreadsheet/App/PropertySheet.h b/src/Mod/Spreadsheet/App/PropertySheet.h index 500e78d48c..2e0cd268bb 100644 --- a/src/Mod/Spreadsheet/App/PropertySheet.h +++ b/src/Mod/Spreadsheet/App/PropertySheet.h @@ -78,6 +78,11 @@ public: void Restore(Base::XMLReader& reader) override; + void getLinksTo(std::vector& identifiers, + App::DocumentObject* obj, + const char* subname = nullptr, + bool all = false) const override; + void copyCells(Base::Writer& writer, const std::vector& ranges) const; void pasteCells(Base::XMLReader& reader, App::Range dstRange);