From c9232b141a8cffbe1d0173ef0cc05b4b9f6c8198 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Thu, 18 Jul 2024 09:51:42 -0400 Subject: [PATCH 1/3] Toponaming: Transfer in getLinksTo --- src/App/PropertyExpressionEngine.cpp | 44 ++++++++++++ src/App/PropertyExpressionEngine.h | 5 ++ src/App/PropertyLinks.h | 43 ++++++++++++ src/Mod/Part/App/PropertyGeometryList.cpp | 84 ++++++++++++++++++++--- src/Mod/Part/App/PropertyGeometryList.h | 4 +- src/Mod/Part/App/PropertyTopoShape.cpp | 8 ++- src/Mod/Spreadsheet/App/PropertySheet.cpp | 48 +++++++++++++ src/Mod/Spreadsheet/App/PropertySheet.h | 5 ++ 8 files changed, 228 insertions(+), 13 deletions(-) diff --git a/src/App/PropertyExpressionEngine.cpp b/src/App/PropertyExpressionEngine.cpp index 0eac59bf2a..95899113de 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,46 @@ 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 &v : expressions) { + const auto &deps = v.second.expression->getDeps(option); + auto it = deps.find(obj); + if(it==deps.end()) + continue; + for(auto &dep : it->second) { + if (!subname) { + identifiers.push_back(v.first); + break; + } + bool found = false; + for (const auto &path : dep.second) { + if (path.getSubObjectName() == subname) { + identifiers.push_back(v.first); + found = true; + break; + } + App::SubObjectT sobjT(obj, path.getSubObjectName().c_str()); + if (sobjT.getSubObject() == sobj + && sobjT.getOldElementName() == subElement) { + identifiers.push_back(v.first); + found = true; + break; + } + } + if (found) + break; + } + } +} diff --git a/src/App/PropertyExpressionEngine.h b/src/App/PropertyExpressionEngine.h index 05f9febd3e..b571cc764b 100644 --- a/src/App/PropertyExpressionEngine.h +++ b/src/App/PropertyExpressionEngine.h @@ -160,6 +160,11 @@ public: void afterRestore() override; void onContainerRestored() override; + virtual 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.h b/src/App/PropertyLinks.h index 29b7a7e547..99b6955be9 100644 --- a/src/App/PropertyLinks.h +++ b/src/App/PropertyLinks.h @@ -184,6 +184,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 @@ -631,6 +644,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 +739,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; @@ -862,6 +885,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 +1037,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 +1165,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 +1340,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; diff --git a/src/Mod/Part/App/PropertyGeometryList.cpp b/src/Mod/Part/App/PropertyGeometryList.cpp index 23f9660721..27f44c5367 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,55 @@ 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)); +// 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 &v : copy) { + auto range = std::equal_range(_lValueList.begin(), _lValueList.end(), v); + // 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) + v = v->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) { +// aboutToSetValue(); +// std::set valueSet(_lValueList.begin(),_lValueList.end()); +// for(auto v : lValue) +// valueSet.erase(v); +// _lValueList = std::move(lValue); +// for(auto v : valueSet) +// delete v; +// hasSetValue(); + // 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); - _lValueList = std::move(lValue); - for(auto v : valueSet) + std::sort(_lValueList.begin(), _lValueList.end()); + for (auto v : lValue) { + auto range = std::equal_range(_lValueList.begin(), _lValueList.end(), v); + _lValueList.erase(range.first, range.second); + } + for (auto v : _lValueList) delete v; + _lValueList = std::move(lValue); 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 +201,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 &e : geom->getExtensions() ) { + auto ext = e.lock(); + auto gpe = freecad_dynamic_cast(ext.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 +217,17 @@ void PropertyGeometryList::tryRestoreGeometry(Geometry * geom, Base::XMLReader & { // Not all geometry classes implement Restore() and throw an exception instead try { + if (!reader.getAttributeAsInteger("migrated") && 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"); + 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 +240,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() << "\""; + 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 +318,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/Spreadsheet/App/PropertySheet.cpp b/src/Mod/Spreadsheet/App/PropertySheet.cpp index 742f380d6d..02b4d5cdd3 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,50 @@ 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 sobj = objT.getSubObject(); + auto subElement = objT.getOldElementName(); + + auto owner = Base::freecad_dynamic_cast(getContainer()); + for (const auto& v : data) { + if (auto expr = v.second->getExpression()) { + const auto& deps = expr->getDeps(option); + auto it = deps.find(obj); + if (it == deps.end()) { + continue; + } + for (auto& dep : it->second) { + if (!subname) { + identifiers.emplace_back(owner, v.first.toString().c_str()); + break; + } + bool found = false; + for (const auto& path : dep.second) { + if (path.getSubObjectName() == subname) { + identifiers.emplace_back(owner, v.first.toString().c_str()); + found = true; + break; + } + App::SubObjectT sobjT(obj, path.getSubObjectName().c_str()); + if (sobjT.getSubObject() == sobj && sobjT.getOldElementName() == subElement) { + identifiers.emplace_back(owner, v.first.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..9ff409dc41 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; + virtual 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); From 5afdc198742cf519ed75ca1b96b2d319b7729b33 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Thu, 18 Jul 2024 10:23:29 -0400 Subject: [PATCH 2/3] Toponaming: Cleanup --- src/App/GeoFeatureGroupExtension.cpp | 2 +- src/App/GroupExtension.cpp | 2 +- src/App/Link.cpp | 2 +- src/App/PropertyExpressionEngine.cpp | 34 +-- src/App/PropertyExpressionEngine.h | 2 +- src/App/PropertyLinks.cpp | 288 +++++++++++++++++++++- src/App/PropertyLinks.h | 32 ++- src/Mod/Part/App/PropertyGeometryList.cpp | 38 +-- src/Mod/PartDesign/App/Feature.cpp | 2 +- src/Mod/Spreadsheet/App/PropertySheet.cpp | 20 +- src/Mod/Spreadsheet/App/PropertySheet.h | 8 +- 11 files changed, 361 insertions(+), 69 deletions(-) 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 95899113de..02f21654ec 100644 --- a/src/App/PropertyExpressionEngine.cpp +++ b/src/App/PropertyExpressionEngine.cpp @@ -1081,45 +1081,47 @@ void PropertyExpressionEngine::onRelabeledDocument(const App::Document &doc) } } -void PropertyExpressionEngine::getLinksTo(std::vector &identifiers, - App::DocumentObject *obj, - const char *subname, +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; + 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 &v : expressions) { - const auto &deps = v.second.expression->getDeps(option); + for (auto& [expressionId, expressionInfo] : expressions) { + const auto& deps = expressionInfo.expression->getDeps(option); auto it = deps.find(obj); - if(it==deps.end()) + if (it == deps.end()) { continue; - for(auto &dep : it->second) { + } + auto [docObj, map] = *it; + for (auto& [key, paths] : map) { if (!subname) { - identifiers.push_back(v.first); + identifiers.push_back(expressionId); break; } bool found = false; - for (const auto &path : dep.second) { + for (const auto& path : paths) { if (path.getSubObjectName() == subname) { - identifiers.push_back(v.first); + 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(v.first); + if (sobjT.getSubObject() == sobj && sobjT.getOldElementName() == subElement) { + identifiers.push_back(expressionId); found = true; break; } } - if (found) + if (found) { break; + } } } } diff --git a/src/App/PropertyExpressionEngine.h b/src/App/PropertyExpressionEngine.h index b571cc764b..248a1c1323 100644 --- a/src/App/PropertyExpressionEngine.h +++ b/src/App/PropertyExpressionEngine.h @@ -160,7 +160,7 @@ public: void afterRestore() override; void onContainerRestored() override; - virtual void getLinksTo(std::vector &identifiers, + void getLinksTo(std::vector &identifiers, App::DocumentObject *obj, const char *subname=nullptr, bool all=false) const override; diff --git a/src/App/PropertyLinks.cpp b/src/App/PropertyLinks.cpp index d68a371f8d..d7928b8481 100644 --- a/src/App/PropertyLinks.cpp +++ b/src/App/PropertyLinks.cpp @@ -71,6 +71,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) @@ -791,6 +799,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 +1094,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 +1121,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 +1129,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 +1154,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 +1355,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 +1633,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 +1913,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 +2895,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.first.empty() ? shadow.second.c_str() + : shadow.first.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 +3677,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 +3775,7 @@ void PropertyXLink::Save (Base::Writer &writer) const { << "pcDoc?docInfo->pcDoc->LastModifiedDate.getValue():"") << "\" name=\"" << objectName; + } if(testFlag(LinkAllowPartial)) @@ -3665,6 +3843,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 +4192,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 +4857,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 +5117,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 +5142,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 +5265,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 99b6955be9..ce81a5467f 100644 --- a/src/App/PropertyLinks.h +++ b/src/App/PropertyLinks.h @@ -559,6 +559,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); @@ -566,6 +568,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; @@ -575,6 +583,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; @@ -751,11 +766,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; @@ -1360,6 +1372,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; }; @@ -1412,6 +1432,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 27f44c5367..2f33c4d958 100644 --- a/src/Mod/Part/App/PropertyGeometryList.cpp +++ b/src/Mod/Part/App/PropertyGeometryList.cpp @@ -85,18 +85,14 @@ 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 &v : copy) { - auto range = std::equal_range(_lValueList.begin(), _lValueList.end(), v); + 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) - v = v->clone(); + geo = geo->clone(); else _lValueList.erase(range.first, range.second); } @@ -108,24 +104,16 @@ void PropertyGeometryList::setValues(const std::vector& lValue) void PropertyGeometryList::setValues(std::vector &&lValue) { -// aboutToSetValue(); -// std::set valueSet(_lValueList.begin(),_lValueList.end()); -// for(auto v : lValue) -// valueSet.erase(v); -// _lValueList = std::move(lValue); -// for(auto v : valueSet) -// delete v; -// hasSetValue(); // 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::sort(_lValueList.begin(), _lValueList.end()); - for (auto v : lValue) { - auto range = std::equal_range(_lValueList.begin(), _lValueList.end(), v); + for (auto geo : lValue) { + auto range = std::equal_range(_lValueList.begin(), _lValueList.end(), geo); _lValueList.erase(range.first, range.second); } - for (auto v : _lValueList) - delete v; + for (auto geo : _lValueList) + delete geo; _lValueList = std::move(lValue); hasSetValue(); } @@ -201,9 +189,9 @@ 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 &e : geom->getExtensions() ) { - auto ext = e.lock(); - auto gpe = freecad_dynamic_cast(ext.get()); + for( auto & ext : geom->getExtensions() ) { + auto extension = ext.lock(); + auto gpe = freecad_dynamic_cast(extension.get()); if (gpe) gpe->postSave(writer); } @@ -217,12 +205,12 @@ void PropertyGeometryList::tryRestoreGeometry(Geometry * geom, Base::XMLReader & { // Not all geometry classes implement Restore() and throw an exception instead try { - if (!reader.getAttributeAsInteger("migrated") && reader.hasAttribute("id")) { + 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"); + int index = reader.getAttributeAsInteger("refIndex", "1"); unsigned long flags = (unsigned long)reader.getAttributeAsUnsigned("flags"); ext->setReference(ref, index, flags); } @@ -241,7 +229,7 @@ void PropertyGeometryList::Save(Writer &writer) const writer.incInd(); for (int i = 0; i < getSize(); i++) { writer.Stream() << writer.ind() << "getTypeId().getName() << "\""; + << _lValueList[i]->getTypeId().getName() << "\"" << endl; for( auto &e : _lValueList[i]->getExtensions() ) { auto ext = e.lock(); auto gpe = freecad_dynamic_cast(ext.get()); 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 02b4d5cdd3..4473de6e00 100644 --- a/src/Mod/Spreadsheet/App/PropertySheet.cpp +++ b/src/Mod/Spreadsheet/App/PropertySheet.cpp @@ -2278,32 +2278,34 @@ void PropertySheet::getLinksTo(std::vector& identifiers, all ? Expression::DepOption::DepAll : Expression::DepOption::DepNormal; App::SubObjectT objT(obj, subname); - auto sobj = objT.getSubObject(); + auto subObject = objT.getSubObject(); auto subElement = objT.getOldElementName(); auto owner = Base::freecad_dynamic_cast(getContainer()); - for (const auto& v : data) { - if (auto expr = v.second->getExpression()) { + 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; } - for (auto& dep : it->second) { + const auto [docObj, depsList] = *it; + for (auto& [depName, paths] : depsList) { if (!subname) { - identifiers.emplace_back(owner, v.first.toString().c_str()); + identifiers.emplace_back(owner, cellName.toString().c_str()); break; } bool found = false; - for (const auto& path : dep.second) { + for (const auto& path : paths) { if (path.getSubObjectName() == subname) { - identifiers.emplace_back(owner, v.first.toString().c_str()); + identifiers.emplace_back(owner, cellName.toString().c_str()); found = true; break; } App::SubObjectT sobjT(obj, path.getSubObjectName().c_str()); - if (sobjT.getSubObject() == sobj && sobjT.getOldElementName() == subElement) { - identifiers.emplace_back(owner, v.first.toString().c_str()); + if (sobjT.getSubObject() == subObject + && sobjT.getOldElementName() == subElement) { + identifiers.emplace_back(owner, cellName.toString().c_str()); found = true; break; } diff --git a/src/Mod/Spreadsheet/App/PropertySheet.h b/src/Mod/Spreadsheet/App/PropertySheet.h index 9ff409dc41..2e0cd268bb 100644 --- a/src/Mod/Spreadsheet/App/PropertySheet.h +++ b/src/Mod/Spreadsheet/App/PropertySheet.h @@ -78,10 +78,10 @@ public: void Restore(Base::XMLReader& reader) override; - virtual void getLinksTo(std::vector& identifiers, - App::DocumentObject* obj, - const char* subname = nullptr, - bool all = false) const 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; From 97473a838f3ce1fd115db2bdebeb2061246aba88 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Fri, 19 Jul 2024 16:23:27 -0400 Subject: [PATCH 3/3] Refactor all element name pairs into clearer struct names - renames --- src/App/PropertyLinks.cpp | 7 ++++--- src/App/PropertyLinks.h | 1 - 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/App/PropertyLinks.cpp b/src/App/PropertyLinks.cpp index d7928b8481..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" @@ -516,7 +517,7 @@ bool PropertyLinkBase::_updateElementReference(DocumentObject *feature, (void)obj; (void)reverse; (void)notify; - shadow.second = sub; + shadow.oldName = sub; return false; #endif } @@ -2928,8 +2929,8 @@ void PropertyLinkSubList::getLinksTo(std::vector& identif if (i < (int)_ShadowSubList.size()) { const auto& shadow = _ShadowSubList[i]; App::SubObjectT sobjT(obj, - shadow.first.empty() ? shadow.second.c_str() - : shadow.first.c_str()); + shadow.newName.empty() ? shadow.oldName.c_str() + : shadow.newName.c_str()); if (sobjT.getSubObject() == subObject && sobjT.getOldElementName() == subElement) { identifiers.emplace_back(*this); continue; diff --git a/src/App/PropertyLinks.h b/src/App/PropertyLinks.h index ce81a5467f..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 {