From 579dee27ceddcf566436bbbc61793f147bc8bd70 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Wed, 28 Feb 2018 20:14:19 +0800 Subject: [PATCH] Support geometry element reference auto update, import and export Because element mapping auto generation uses the object ID as Tag, and possibly document depended string hash indexes, the mapping must be able to rebuilt when importing objects, i.e. during copy&paste. The mechanism is here is, * During exporting, strip out all element maps. * Property link sub now include shadow copy of both the new and old style element referece. It will store the old style reference as before, and store the any new reference in a new XML attribute. GeoFeature will signal any element mapping change, and allows all link sub to keep an up-to-date element reference * During importing, only import old style reference. * Touch all geo feature objects, and recompute the imported objects. * Regenerate mapped references, and geo feature will signal property link sub to update their reference as before Necessary Core API changes includes, * Move utility function resolveObject() from Gui.Selection to DocumentObject as resolve(). * Add getElementName() API to GeoFeature. * Add utility function resolveElement() to GeoFeature --- src/App/Document.cpp | 21 ++- src/App/Document.h | 3 +- src/App/DocumentObject.cpp | 65 ++++++++ src/App/DocumentObject.h | 14 ++ src/App/DocumentObjectPy.xml | 26 ++++ src/App/DocumentObjectPyImp.cpp | 46 ++++++ src/App/GeoFeature.cpp | 107 +++++++++++++ src/App/GeoFeature.h | 57 +++++++ src/App/PropertyLinks.cpp | 257 +++++++++++++++++++++++++++---- src/App/PropertyLinks.h | 16 ++ src/Gui/CommandLink.cpp | 2 +- src/Gui/PropertyView.cpp | 2 +- src/Gui/Selection.cpp | 264 ++++++++++---------------------- src/Gui/Selection.h | 34 ++-- 14 files changed, 670 insertions(+), 244 deletions(-) diff --git a/src/App/Document.cpp b/src/App/Document.cpp index b960cc8a9019..83d67f69abbb 100644 --- a/src/App/Document.cpp +++ b/src/App/Document.cpp @@ -159,6 +159,7 @@ struct DocumentP int iTransactionMode; bool rollback; bool undoing; ///< document in the middle of undo or redo + std::vector pendingRecomputes; std::bitset<32> StatusBits; int iUndoMode; unsigned int UndoMemSize; @@ -1792,12 +1793,18 @@ Document::readObjects(Base::XMLReader& reader) return objs; } +void Document::addRecomputeObject(DocumentObject *obj) { + if(testStatus(Status::Importing) && obj) + d->pendingRecomputes.push_back(obj); +} + std::vector Document::importObjects(Base::XMLReader& reader) { d->hashers.clear(); Base::ObjectStatusLocker restoreBit(Status::Restoring, this); Base::ObjectStatusLocker restoreBit2(Status::Importing, this); + d->pendingRecomputes.clear(); reader.readElement("Document"); long scheme = reader.getAttributeAsInteger("SchemaVersion"); reader.DocumentSchema = scheme; @@ -1822,9 +1829,21 @@ Document::importObjects(Base::XMLReader& reader) } reader.readEndElement("Document"); - signalImportObjects(objs, reader); + signalImportObjects(objs, reader); afterRestore(objs); + + if(d->pendingRecomputes.size()) { + for(auto obj : d->pendingRecomputes) { + if(obj) { + FC_LOG("recompute '" << obj->getNameInDocument() << "' after restore"); + obj->touch(); + } + } + recompute(objs); + } + d->pendingRecomputes.clear(); + signalFinishImportObjects(objs); d->hashers.clear(); return objs; diff --git a/src/App/Document.h b/src/App/Document.h index c6fc60b4d03a..aa35158ffec9 100644 --- a/src/App/Document.h +++ b/src/App/Document.h @@ -476,7 +476,8 @@ class AppExport Document : public App::PropertyContainer return !links.empty(); } - void addRemapProperty(Property *prop); + /// Called by objects during restore to ask for recompute + void addRecomputeObject(DocumentObject *obj); /// Function called to signal that an object identifier has been renamed void renameObjectIdentifiers(const std::map & paths, const std::function &selector = [](const App::DocumentObject *) { return true; }); diff --git a/src/App/DocumentObject.cpp b/src/App/DocumentObject.cpp index be2bece1e030..e3d2c4288c84 100644 --- a/src/App/DocumentObject.cpp +++ b/src/App/DocumentObject.cpp @@ -856,3 +856,68 @@ bool DocumentObject::hasChildElement() const { } return false; } + +DocumentObject *DocumentObject::resolve(const char *subname, + App::DocumentObject **parent, std::string *childName, const char **subElement) const +{ + auto self = const_cast(this); + if(parent) *parent = 0; + if(subElement) *subElement = 0; + if(!subname || *subname==0) + return self; + + auto obj = getSubObject(subname); + if(!obj) + return self; + + if(!parent && !subElement) + return obj; + + // NOTE, the convension of '.' separated SubName demands a mandatory ending + // '.' for each object name in SubName, even if there is no subelement + // following it. So finding the last dot will give us the end of the last + // object name. + const char *dot = strrchr(subname,'.'); + if(!dot || dot == subname) { + if(subElement) + *subElement = dot?dot+1:subname; + return obj; // this means no parent object reference in SubName + } + + if(parent) + *parent = self; + + bool elementMapChecked = false; + const char *lastDot = dot; + for(--dot;;--dot) { + // check for the second last dot, which is the end of the last parent object + if(*dot == '.' || dot == subname) { + // We can't get parent object by its name, because the object may be + // externally linked (i.e. in a different document). So go through + // getSubObject again. + if(!elementMapChecked) { + elementMapChecked = true; + const char *sub = dot==subname?dot:dot+1; + if(Data::ComplexGeoData::isMappedElement(sub)) + lastDot = dot; + } + if(dot==subname) + break; + auto sobj = getSubObject(std::string(subname,dot-subname+1).c_str()); + if(parent) *parent = sobj; + if(sobj!=this) + break; + } + } + if(childName && lastDot!=dot) { + if(*dot == '.') + ++dot; + const char *nextDot = strchr(dot,'.'); + assert(nextDot); + *childName = std::string(dot,nextDot-dot); + } + if(subElement) + *subElement = *lastDot=='.'?lastDot+1:lastDot; + return obj; +} + diff --git a/src/App/DocumentObject.h b/src/App/DocumentObject.h index acdca5cf82dc..8e23ccf1ec9c 100644 --- a/src/App/DocumentObject.h +++ b/src/App/DocumentObject.h @@ -351,6 +351,20 @@ class AppExport DocumentObject: public App::TransactionalObject return _pcViewProviderName.c_str(); } + /** Resolve the last document object referenced in the subname + * + * @param subname: dot separated subname + * @param parent: return the direct parent of the object + * @param childName: return child name to be passed to is/setElementVisible() + * @param subElement: return non-object sub-element name if found. The + * pointer is guaranteed to be within the buffer pointed to by 'subname' + * + * @return Returns the last referenced document object in the subname. If no + * such object in subname, return pObject. + */ + App::DocumentObject *resolve(const char *subname, App::DocumentObject **parent=0, + std::string *childName=0, const char **subElement=0) const; + protected: /// recompute only this object virtual App::DocumentObjectExecReturn *recompute(void); diff --git a/src/App/DocumentObjectPy.xml b/src/App/DocumentObjectPy.xml index 9be5ea40d148..9f1546f39fcc 100644 --- a/src/App/DocumentObjectPy.xml +++ b/src/App/DocumentObjectPy.xml @@ -147,6 +147,32 @@ referencing subobject. Get all paths from this object to another object following the OutList. + + + +resolve(subname) -- resolve the sub object + +Returns a tuple (subobj,parent,elementName,subElement), where 'subobj' is the +last object referenced in 'subname', and 'parent' is the direct parent of +'subobj', and 'elementName' is the child name of the subobj, which can be used +to call parent.isElementVisible/setElementVisible(). 'subElement' is the +non-object sub-element name if any. + + + + + + +resolveSubElement(subname,append,type) -- resolve both new and old style sub element + +subname: subname reference contianing object hierarchy +append: Whether to append object hierarchy prefix inside subname to returned element name +type: 0: normal, 1: for import, 2: for export + +Return tuple(obj,newElementName,oldElementName) + + + A list of all objects this object links to. diff --git a/src/App/DocumentObjectPyImp.cpp b/src/App/DocumentObjectPyImp.cpp index 6f92eca88fe1..a052be450955 100644 --- a/src/App/DocumentObjectPyImp.cpp +++ b/src/App/DocumentObjectPyImp.cpp @@ -26,6 +26,7 @@ #include "DocumentObject.h" #include "Document.h" #include "Expression.h" +#include "GeoFeature.h" #include "GroupExtension.h" #include "GeoFeatureGroupExtension.h" @@ -690,3 +691,48 @@ int DocumentObjectPy::setCustomAttributes(const char* attr, PyObject *obj) Py::Int DocumentObjectPy::getID() const { return Py::Int(getDocumentObjectPtr()->getID()); } + +PyObject *DocumentObjectPy::resolve(PyObject *args) +{ + const char *subname; + if (!PyArg_ParseTuple(args, "s",&subname)) + return NULL; // NULL triggers exception + + PY_TRY { + std::string elementName; + const char *subElement = 0; + App::DocumentObject *parent = 0; + auto obj = getDocumentObjectPtr()->resolve(subname,&parent,&elementName,&subElement); + + Py::Tuple ret(4); + ret.setItem(0,Py::Object(obj->getPyObject(),true)); + ret.setItem(1,parent?Py::Object(parent->getPyObject(),true):Py::None()); + ret.setItem(2,Py::String(elementName.c_str())); + ret.setItem(3,Py::String(subElement?subElement:"")); + return Py::new_reference_to(ret); + } PY_CATCH; + + Py_Return; +} + +PyObject *DocumentObjectPy::resolveSubElement(PyObject *args) +{ + const char *subname; + PyObject *append = Py_False; + int type = 0; + if (!PyArg_ParseTuple(args, "s|Oi",&subname,&append,&type)) + return NULL; // NULL triggers exception + + PY_TRY { + std::pair elementName; + auto obj = GeoFeature::resolveElement(getDocumentObjectPtr(), subname,elementName, + PyObject_IsTrue(append),(GeoFeature::ElementNameType)type); + Py::Tuple ret(3); + ret.setItem(0,obj?Py::Object(obj->getPyObject(),true):Py::None()); + ret.setItem(1,Py::String(elementName.first)); + ret.setItem(2,Py::String(elementName.second)); + return Py::new_reference_to(ret); + } PY_CATCH; + + Py_Return; +} diff --git a/src/App/GeoFeature.cpp b/src/App/GeoFeature.cpp index bff633ca7c3a..3e8551494c64 100644 --- a/src/App/GeoFeature.cpp +++ b/src/App/GeoFeature.cpp @@ -26,10 +26,14 @@ #ifndef _PreComp_ #endif +#include +#include "Document.h" #include "GeoFeature.h" #include "GeoFeatureGroupExtension.h" #include +FC_LOG_LEVEL_INIT("GeoFeature",true,true); + using namespace App; @@ -79,3 +83,106 @@ PyObject* GeoFeature::getPyObject(void) } return Py::new_reference_to(PythonObject); } + + +std::pair GeoFeature::getElementName( + const char *name, ElementNameType type) const +{ + (void)type; + + std::pair ret; + if(!name) return ret; + + auto prop = getPropertyOfGeometry(); + if(!prop) return ret; + + auto geo = prop->getComplexData(); + if(!geo) return ret; + + if(Data::ComplexGeoData::isMappedElement(name)) { + const char *oldName = geo->getElementName(name); + const char *dot = strrchr(name,'.'); + if(oldName != name) { + if(!dot) { + ret.first = name; + ret.first += '.'; + }else + ret.first.assign(name,dot-name+1); + ret.first += oldName; + ret.second = oldName; + }else{ + if(FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + FC_WARN("element mapped name not found " << name); + ret.first = name; + if(dot) + ret.second = dot+1; + } + return ret; + } + const char *newName = geo->getElementName(name,true); + if(newName != name) { + std::ostringstream ss; + ss << Data::ComplexGeoData::elementMapPrefix() << newName << '.' << name; + ret.first = ss.str(); + ret.second = name; + }else + ret.second = name; + return ret; +} + +DocumentObject *GeoFeature::resolveElement(DocumentObject *obj, const char *subname, + std::pair &elementName, bool append, + ElementNameType type, const DocumentObject *filter) +{ + const char *element = 0; + if(!obj || !obj->getNameInDocument()) + return 0; + obj = obj->resolve(subname,0,0,&element); + if(!obj) + return 0; + obj = obj->getLinkedObject(true); + if(!obj || (filter && obj!=filter)) + return 0; + if(!element || !element[0]) + return obj; + + auto geo = dynamic_cast(obj); + if(!geo) + return obj; + if(!append) + elementName = geo->getElementName(element,type); + else{ + const auto &names = geo->getElementName(element,type); + if(names.first.size() && names.second.size()) { + std::string prefix(subname,element-subname); + elementName.first = prefix + names.first; + elementName.second = prefix + names.second; + } + } + return obj; +} + +void GeoFeature::updateElementReference() { + auto prop = getPropertyOfGeometry(); + if(!prop) return; + auto geo = prop->getComplexData(); + if(!geo || !geo->getElementMapSize()) return; + auto elementMap = geo->getElementMap(); + if(_elementMapCache != elementMap) { + _elementMapCache.swap(elementMap); + for(auto obj : getInListEx(true)) + PropertyLinkSub::updateElementReferences(this,obj); + } +} + +void GeoFeature::onChanged(const Property *prop) { + if(!isRestoring() && prop==getPropertyOfGeometry()) + updateElementReference(); + DocumentObject::onChanged(prop); +} + +void GeoFeature::onDocumentRestored() { + if(!getDocument()->testStatus(Document::Status::Importing)) + updateElementReference(); + DocumentObject::onDocumentRestored(); +} diff --git a/src/App/GeoFeature.h b/src/App/GeoFeature.h index 673b1516c37a..ab11c0fed32a 100644 --- a/src/App/GeoFeature.h +++ b/src/App/GeoFeature.h @@ -66,7 +66,56 @@ class AppExport GeoFeature : public App::DocumentObject * @return the Python binding object */ virtual PyObject* getPyObject(void); + + /// Specify the type of element name to return when calling getElementName() + enum ElementNameType { + /// Normal usage + Normal=0, + /// For importing + Import=1, + /// For exporting + Export=2, + }; + /** Return the new and old style sub-element name + * + * @param name: input name + * @param type: desired element name type to return + * + * @return a pair(newName,oldName). New element name may be empty. + * + * NOTE: Unlike ComplexGeoData::getElementName(), this function + * relies on ComplexGeoData::elementMapPrefix() to decide whether + * it is a forward query, i.e. mapped -> original, or reverse query. + * The reason being that, unlike ComplexGeoData who deals with the + * actual element map data, GeoFeature here sits at a higher level. + * GeoFeature should be dealing with whatever various PropertyLinkSub(s) + * is assigned. + * + * This function is made virtual, so that inherited class can do something + * unusual, such as Sketcher::SketcherObject, which uses this to expose its + * private geometries without a correpsonding TopoShape, and yet being + * source code compatible. + */ + virtual std::pair getElementName( + const char *name, ElementNameType type=Normal) const; + /** Resolve both the new and old style element name + * + * @param obj: top parent object + * @param subname: subname reference + * @param elementName: output of a pair(newElementName,oldElementName) + * @param append: Whether to include subname prefix into the returned + * element name + * @param type: the type of element name to request + * @param filter: If none zero, then only perform lookup when the element + * owner object is the same as this filter + * + * @return Return the owner object of the element + */ + static DocumentObject *resolveElement(App::DocumentObject *obj, + const char *subname, std::pair &elementName, + bool append=false, ElementNameType type=Normal,const DocumentObject *filter=0); + /** * @brief Calculates the placement in the global reference coordinate system * @@ -80,6 +129,14 @@ class AppExport GeoFeature : public App::DocumentObject * @return Base::Placement The transformation from the global reference coordinate system */ Base::Placement globalPlacement() const; + +protected: + virtual void onChanged(const Property* prop); + virtual void onDocumentRestored(); + void updateElementReference(); + +private: + std::map _elementMapCache; }; } //namespace App diff --git a/src/App/PropertyLinks.cpp b/src/App/PropertyLinks.cpp index 95abc4ecf8c7..2ed84e630c9d 100644 --- a/src/App/PropertyLinks.cpp +++ b/src/App/PropertyLinks.cpp @@ -40,11 +40,13 @@ #include #include #include +#include #include "Application.h" #include "DocumentObject.h" #include "DocumentObjectPy.h" #include "Document.h" +#include "GeoFeature.h" #include "PropertyLinks.h" @@ -498,6 +500,7 @@ void PropertyLinkSub::setValue(App::DocumentObject * lValue, const std::vector props; + obj->getPropertyList(props); + for(auto prop : props) { + auto linksub = dynamic_cast(prop); + if(linksub) { + linksub->updateElementReference(feature); + continue; + } + auto linksubs = dynamic_cast(prop); + if(linksubs) { + linksubs->updateElementReference(feature); + continue; + } + auto xlink = dynamic_cast(prop); + if(xlink) { + linksubs->updateElementReference(feature); + continue; + } + } +} + +bool PropertyLinkSub::updateElementReference(DocumentObject *feature, + App::DocumentObject *obj, std::string &sub, + std::pair &shadow) +{ + if(!obj || !obj->getNameInDocument()) return false; + std::pair elementName; + if(!GeoFeature::resolveElement(obj,shadow.first.size()?shadow.first.c_str():sub.c_str(), + elementName,true,GeoFeature::ElementNameType::Export,feature)) + return false; + if(elementName.first.empty()) + return false; + FC_TRACE("element reference cache " << shadow.first << " -> " << elementName.first); + shadow.swap(elementName); + auto pos2 = shadow.first.rfind('.'); + if(pos2 == std::string::npos) + return false; + ++pos2; + auto pos = sub.rfind('.'); + if(pos == std::string::npos) + pos = 0; + else + ++pos; + if(sub.compare(pos,sub.size()-pos,&shadow.first[pos2])!=0) { + FC_LOG("element reference update " << sub << " -> " << shadow.first); + sub.replace(pos,sub.size()-pos,&shadow.first[pos2]); + return true; + } + return false; +} + +void PropertyLinkSub::updateElementReference(DocumentObject *feature) { + if(!feature) _ShadowSubList.clear(); + _ShadowSubList.resize(_cSubList.size()); + if(!_pcLinkSub || !_pcLinkSub->getNameInDocument()) + return; + int i=0; + bool touched = false; + for(auto &sub : _cSubList) { + if(updateElementReference(feature,_pcLinkSub,sub,_ShadowSubList[i++])) + touched = true; + } + for(int idx : _mapped) { + if(idx<(int)_cSubList.size() && _ShadowSubList[idx].first.size()) { + _cSubList[idx] = _ShadowSubList[idx].first; + touched = true; + } + } + _mapped.clear(); + if(touched) + touch(); +} + static std::string importSubName(Base::XMLReader &reader, const char *sub) { std::ostringstream str; for(const char *dot=strchr(sub,'.');dot;sub=dot+1,dot=strchr(sub,'.')) @@ -598,9 +675,6 @@ static std::string importSubName(Base::XMLReader &reader, const char *sub) { static std::string exportSubName(const App::DocumentObject *obj, const char *sub) { - if(!obj || !obj->getNameInDocument() || !obj->getDocument()->isExporting()) - return std::string(sub); - std::ostringstream str; for(const char *dot=strchr(sub,'.');dot;sub=dot+1,dot=strchr(sub,'.')) { // name with trailing '.' @@ -653,8 +727,13 @@ static std::string tryImportSubName(const std::map &nam return std::string(); } +#define ATTR_SHADOW "shadowed" +#define ATTR_MAPPED "mapped" + void PropertyLinkSub::Save (Base::Writer &writer) const { + assert(_cSubList.size() == _ShadowSubList.size()); + std::string internal_name; // it can happen that the object is still alive but is not part of the document anymore and thus // returns 0 @@ -662,9 +741,28 @@ void PropertyLinkSub::Save (Base::Writer &writer) const internal_name = _pcLinkSub->getExportName(); writer.Stream() << writer.ind() << "" << std::endl; writer.incInd(); - for(unsigned int i = 0;i<_cSubList.size(); i++) - writer.Stream() << writer.ind() << "" << endl; + bool exporting = _pcLinkSub&&_pcLinkSub->getNameInDocument()&&_pcLinkSub->getDocument()->isExporting(); + for(unsigned int i = 0;i<_cSubList.size(); i++) { + const auto &shadow = _ShadowSubList[i]; + // shadow.second stores the old style element name. For backward + // compatibility reason, we shall store the old name into attribute + // 'value' whenver possible. + const auto &sub = shadow.second.empty()?_cSubList[i]:shadow.second; + writer.Stream() << writer.ind() << "" << endl; + } writer.decInd(); writer.Stream() << writer.ind() << "" << endl ; } @@ -679,19 +777,23 @@ void PropertyLinkSub::Restore(Base::XMLReader &reader) // Property not in a DocumentObject! assert(getContainer()->getTypeId().isDerivedFrom(App::DocumentObject::getClassTypeId()) ); + App::Document* document = static_cast(getContainer())->getDocument(); + std::vector mapped; std::vector values(count); // Sub may store '.' separated object names, so be aware of the possible mapping when import for (int i = 0; i < count; i++) { reader.readElement("Sub"); - values[i] = importSubName(reader,reader.getAttribute("value")); + values[i] = importSubName(reader,reader.getAttribute( + reader.hasAttribute(ATTR_SHADOW)?ATTR_SHADOW:"value")); + if(reader.hasAttribute(ATTR_MAPPED)) + mapped.push_back(i); } reader.readEndElement("LinkSub"); DocumentObject *pcObject; if (!name.empty()) { - App::Document* document = static_cast(getContainer())->getDocument(); pcObject = document ? document->getObject(name.c_str()) : 0; if (!pcObject) { if (reader.isVerbose()) { @@ -700,6 +802,7 @@ void PropertyLinkSub::Restore(Base::XMLReader &reader) } } setValue(pcObject,values); + _mapped.swap(mapped); } else { setValue(0); @@ -782,6 +885,7 @@ void PropertyLinkSubList::setSize(int newSize) { _lValueList.resize(newSize); _lSubList .resize(newSize); + _ShadowSubList.resize(newSize); } int PropertyLinkSubList::getSize(void) const @@ -812,14 +916,14 @@ void PropertyLinkSubList::setValue(DocumentObject* lValue,const char* SubName) _lValueList[0]=lValue; _lSubList.resize(1); _lSubList[0]=SubName; - hasSetValue(); } else { aboutToSetValue(); _lValueList.clear(); _lSubList.clear(); - hasSetValue(); } + updateElementReference(0); + hasSetValue(); } void PropertyLinkSubList::setValues(const std::vector& lValue,const std::vector& lSubNames) @@ -853,6 +957,7 @@ void PropertyLinkSubList::setValues(const std::vector& lValue,c int i = 0; for (std::vector::const_iterator it = lSubNames.begin();it!=lSubNames.end();++it) _lSubList[i] = *it; + updateElementReference(0); hasSetValue(); } @@ -884,6 +989,7 @@ void PropertyLinkSubList::setValues(const std::vector& lValue,c aboutToSetValue(); _lValueList = lValue; _lSubList = lSubNames; + updateElementReference(0); hasSetValue(); } @@ -923,6 +1029,7 @@ void PropertyLinkSubList::setValue(DocumentObject* lValue, const std::vector_lSubList = SubList; this->_lValueList.insert(this->_lValueList.begin(), size, lValue); } + updateElementReference(0); hasSetValue(); } @@ -1143,15 +1250,63 @@ void PropertyLinkSubList::setPyObject(PyObject *value) } } +void PropertyLinkSubList::updateElementReference(DocumentObject *feature) { + if(!feature) _ShadowSubList.clear(); + _ShadowSubList.resize(_lSubList.size()); + int i=0; + bool touched = false; + for(auto &sub : _lSubList) { + auto obj = _lValueList[i]; + if(PropertyLinkSub::updateElementReference(feature,obj,sub,_ShadowSubList[i++])) + touched = true; + } + for(int idx : _mapped) { + if(idx<(int)_lSubList.size() && _ShadowSubList[idx].first.size()) { + _lSubList[idx] = _ShadowSubList[idx].first; + touched = true; + } + } + _mapped.clear(); + if(touched) + touch(); +} + void PropertyLinkSubList::Save (Base::Writer &writer) const { - writer.Stream() << writer.ind() << "" << endl; + assert(_lSubList.size() == _ShadowSubList.size()); + + int count = 0; + for(auto obj : _lValueList) { + if(obj && obj->getNameInDocument()) + ++count; + } + writer.Stream() << writer.ind() << "" << endl; writer.incInd(); for (int i = 0; i < getSize(); i++) { - writer.Stream() << writer.ind() << - "getExportName() << "\" " << - "sub=\"" << exportSubName(_lValueList[i],_lSubList[i].c_str()) << "\"/>" << endl; + auto obj = _lValueList[i]; + if(!obj || !obj->getNameInDocument()) + continue; + bool exporting = obj->getDocument()->isExporting(); + const auto &shadow = _ShadowSubList[i]; + // shadow.second stores the old style element name. For backward + // compatibility reason, we shall store the old name into attribute + // 'value' whenver possible. + const auto &sub = shadow.second.empty()?_lSubList[i]:shadow.second; + + writer.Stream() << writer.ind() << "getExportName() << "\" sub=\""; + if(exporting) { + writer.Stream() << exportSubName(obj,sub.c_str()); + if(shadow.second.size() && _lSubList[i]==shadow.first) + writer.Stream() << "\" " ATTR_MAPPED "=\"1"; + } else { + writer.Stream() << sub; + if(shadow.second.size()) { + // Stores the actual value that is shadowed. For new version FC, + // we will restore this shadowed value instead. + writer.Stream() << "\" " ATTR_SHADOW "=\"" << _lSubList[i]; + } + } + writer.Stream() << "\"/>" << endl; } writer.decInd(); @@ -1169,6 +1324,9 @@ void PropertyLinkSubList::Restore(Base::XMLReader &reader) values.reserve(count); std::vector SubNames; SubNames.reserve(count); + DocumentObject* father = dynamic_cast(getContainer()); + App::Document* document = father ? father->getDocument() : 0; + std::vector mapped; for (int i = 0; i < count; i++) { reader.readElement("Link"); std::string name = reader.getName(reader.getAttribute("obj")); @@ -1176,12 +1334,13 @@ void PropertyLinkSubList::Restore(Base::XMLReader &reader) // referenced objects in XML which do not exist anymore in the new // document. Thus, we should silently ignore this. // Property not in an object! - DocumentObject* father = dynamic_cast(getContainer()); - App::Document* document = father ? father->getDocument() : 0; DocumentObject* child = document ? document->getObject(name.c_str()) : 0; if (child) { values.push_back(child); - SubNames.push_back(importSubName(reader,reader.getAttribute("sub"))); + SubNames.push_back(importSubName(reader,reader.getAttribute( + reader.hasAttribute(ATTR_SHADOW)?ATTR_SHADOW:"sub"))); + if(reader.hasAttribute(ATTR_MAPPED)) + mapped.push_back(i); } else if (reader.isVerbose()) Base::Console().Warning("Lost link to '%s' while loading, maybe " "an object was not loaded correctly\n",name.c_str()); @@ -1191,6 +1350,7 @@ void PropertyLinkSubList::Restore(Base::XMLReader &reader) // assignment setValues(values,SubNames); + _mapped.swap(mapped); } Property *PropertyLinkSubList::CopyOnImportExternal( @@ -1617,6 +1777,7 @@ void PropertyXLink::detach() { if(docInfo) { aboutToSetValue(); resetLink(); + updateElementReference(0); hasSetValue(); } } @@ -1629,6 +1790,7 @@ void PropertyXLink::setSubName(const char *subname, bool transaction) { else subName.clear(); registerXLinkLabel(this); + updateElementReference(0); if(transaction) hasSetValue(); } @@ -1753,18 +1915,42 @@ bool PropertyXLink::isRestored() const { stamp==docInfo->pcDoc->LastModifiedDate.getValue(); } +void PropertyXLink::updateElementReference(DocumentObject *feature) { + if(!feature) { + shadowSub.first.clear(); + shadowSub.second.clear(); + } + bool touched = PropertyLinkSub::updateElementReference(feature,_pcLink,subName,shadowSub); + if(_mapped && shadowSub.first.size()) { + touched = true; + subName = shadowSub.first; + } + _mapped = false; + if(touched) + touch(); +} + void PropertyXLink::Save (Base::Writer &writer) const { auto owner = dynamic_cast(getContainer()); if(!owner || !owner->getDocument()) return; + + // shadowSub stores the old style element name. For backward + // compatibility reason, we shall store the old name into attribute + // 'value' whenver possible. + const auto &sub = shadowSub.second.empty()?subName:shadowSub.second; + auto exporting = owner->getDocument()->isExporting(); if(_pcLink && exporting==Document::Exporting) { // this means, we are exporting with all dependency included. So, store as // local object - writer.Stream() << writer.ind() << "getExportName() << "\" sub=\"" << - exportSubName(_pcLink,subName.c_str()) << - "\"/>" << std::endl; + + writer.Stream() << writer.ind() << "getExportName() << "\" sub=\"" + << exportSubName(_pcLink,sub.c_str()); + if(shadowSub.second.size() && shadowSub.first==subName) + writer.Stream() << "\"" ATTR_MAPPED "=\"1"; + writer.Stream() << "\"/>" << std::endl; return; } const char *path = filePath.c_str(); @@ -1790,8 +1976,14 @@ void PropertyXLink::Save (Base::Writer &writer) const { "pcDoc?docInfo->pcDoc->LastModifiedDate.getValue():"") << "\" name=\"" << objectName << - "\" sub=\"" << subName << - "\" relative=\"" << (relativePath?"true":"false") << "\"/>" << std::endl; + "\" sub=\"" << sub << + "\" relative=\"" << (relativePath?"true":"false"); + if(shadowSub.second.size()) { + // Stores the actual value that is shadowed. For new version FC, + // we will restore this shadowed value instead. + writer.Stream() << "\" " ATTR_SHADOW "=\"" << subName; + } + writer.Stream() << "\"/>" << std::endl; } void PropertyXLink::Restore(Base::XMLReader &reader) @@ -1811,13 +2003,18 @@ void PropertyXLink::Restore(Base::XMLReader &reader) name = reader.getName(reader.getAttribute("name")); else name = reader.getAttribute("name"); - std::string subname; - if(reader.getAttribute("sub")) - subname = importSubName(reader,reader.getAttribute("sub")); - // Property not in a DocumentObject! assert(getContainer()->getTypeId().isDerivedFrom(App::DocumentObject::getClassTypeId())); + DocumentObject* parent = static_cast(getContainer()); + Document *document = parent->getDocument(); + + std::string subname; + if(reader.getAttribute("sub")) { + subname = importSubName(reader,reader.getAttribute( + reader.hasAttribute(ATTR_SHADOW)?ATTR_SHADOW:"sub")); + } + bool mapped = reader.hasAttribute(ATTR_MAPPED); if (name.empty()) { setValue(0); @@ -1827,11 +2024,10 @@ void PropertyXLink::Restore(Base::XMLReader &reader) if(file.size()) { this->stamp = stamp; setValue(file.c_str(),name.c_str(),subname.c_str(),relativePath); + _mapped = mapped; return; } - DocumentObject* parent = static_cast(getContainer()); - Document *document = parent->getDocument(); DocumentObject* object = document ? document->getObject(name.c_str()) : 0; if(!object) { if(reader.isVerbose()) { @@ -1840,6 +2036,7 @@ void PropertyXLink::Restore(Base::XMLReader &reader) } } setValue(object,subname.c_str(),relativePath); + _mapped = mapped; } Property *PropertyXLink::CopyOnImportExternal( diff --git a/src/App/PropertyLinks.h b/src/App/PropertyLinks.h index 34550f34e0bc..b6dbcbb8dd11 100644 --- a/src/App/PropertyLinks.h +++ b/src/App/PropertyLinks.h @@ -303,9 +303,17 @@ class AppExport PropertyLinkSub: public Property, public ScopedLink return sizeof(App::DocumentObject *); } + static void updateElementReferences(DocumentObject *feature, DocumentObject *obj); + static bool updateElementReference(DocumentObject *feature, + App::DocumentObject *obj, std::string &sub, std::pair &shadow); + + void updateElementReference(DocumentObject *feature); + protected: App::DocumentObject* _pcLinkSub; std::vector _cSubList; + std::vector > _ShadowSubList; + std::vector _mapped; }; /** The general Link Property with Child scope @@ -401,10 +409,14 @@ class AppExport PropertyLinkSubList: public PropertyLists, public ScopedLink virtual unsigned int getMemSize (void) const; + void updateElementReference(DocumentObject *feature); + private: //FIXME: Do not make two independent lists because this will lead to some inconsistencies! std::vector _lValueList; std::vector _lSubList; + std::vector > _ShadowSubList; + std::vector _mapped; }; /** The general Link Property with Child scope @@ -471,6 +483,8 @@ class AppExport PropertyXLink : public PropertyLinkGlobal static std::map > getDocumentOutList(App::Document *doc=0); static std::map > getDocumentInList(App::Document *doc=0); + void updateElementReference(DocumentObject *feature); + protected: void unlink(); void detach(); @@ -479,8 +493,10 @@ class AppExport PropertyXLink : public PropertyLinkGlobal std::string filePath; std::string objectName; std::string subName; + std::pair shadowSub; std::string stamp; bool relativePath; + bool _mapped; DocInfoPtr docInfo; }; diff --git a/src/Gui/CommandLink.cpp b/src/Gui/CommandLink.cpp index d5cca5b7cfed..9116c4154bbc 100644 --- a/src/Gui/CommandLink.cpp +++ b/src/Gui/CommandLink.cpp @@ -601,7 +601,7 @@ static std::map > getLinkImpor std::map > objMap; for(auto &sel : Selection().getCompleteSelection(false)) { App::DocumentObject *parent = 0; - auto obj = Selection().resolveObject(sel.pObject,sel.SubName,&parent); + auto obj = sel.pObject->resolve(sel.SubName,&parent); if(!parent || parent->getDocument()==obj->getDocument()) { if(!checking) FC_WARN("skip invalid parent of " << diff --git a/src/Gui/PropertyView.cpp b/src/Gui/PropertyView.cpp index 7e4be92cc2e0..b1d063e4ac32 100644 --- a/src/Gui/PropertyView.cpp +++ b/src/Gui/PropertyView.cpp @@ -250,7 +250,7 @@ void PropertyView::onSelectionChanged(const SelectionChanges& msg) for(auto &sel : array) { if(!sel.pObject) continue; App::DocumentObject *parent = 0; - App::DocumentObject *ob = Gui::Selection().resolveObject(sel.pObject,sel.SubName,&parent); + App::DocumentObject *ob = sel.pObject->resolve(sel.SubName,&parent); if(!ob) continue; if(parent) { auto parentVp = Application::Instance->getViewProvider(parent); diff --git a/src/Gui/Selection.cpp b/src/Gui/Selection.cpp index c542f0742a93..e2e06345df5b 100644 --- a/src/Gui/Selection.cpp +++ b/src/Gui/Selection.cpp @@ -46,7 +46,7 @@ #include #include #include -#include +#include #include #include "MainWindow.h" #include "ViewProviderDocumentObject.h" @@ -332,12 +332,11 @@ std::vector SelectionSingleton::getSelection(const c std::map > objMap; - for(std::list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) { - if(!It->pDoc) continue; + for(auto &sel : _SelList) { + if(!sel.pDoc) continue; const char *subelement = 0; - auto obj = getObjectOfType(It->pObject,It->SubName.c_str(), - App::DocumentObject::getClassTypeId(),resolve,&subelement); - if(!obj || (pcDoc && It->pObject->getDocument()!=pcDoc)) + auto obj = getObjectOfType(sel,App::DocumentObject::getClassTypeId(),resolve,&subelement); + if(!obj || (pcDoc && sel.pObject->getDocument()!=pcDoc)) continue; // In case we are resolving objects, make sure no duplicates @@ -351,13 +350,13 @@ std::vector SelectionSingleton::getSelection(const c tempSelObj.DocName = obj->getDocument()->getName(); tempSelObj.FeatName = obj->getNameInDocument(); - tempSelObj.SubName = subelement; + tempSelObj.SubName = subelement; tempSelObj.TypeName = obj->getTypeId().getName(); tempSelObj.pObject = obj; tempSelObj.pDoc = obj->getDocument(); - tempSelObj.x = It->x; - tempSelObj.y = It->y; - tempSelObj.z = It->z; + tempSelObj.x = sel.x; + tempSelObj.y = sel.y; + tempSelObj.z = sel.z; temp.push_back(tempSelObj); } @@ -373,11 +372,10 @@ bool SelectionSingleton::hasSelection(const char* doc, bool resolve) const if (!pcDoc) return false; } - for(std::list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) { - if(!It->pDoc) continue; - auto obj = getObjectOfType(It->pObject, It->SubName.c_str(), - App::DocumentObject::getClassTypeId(),resolve); - if(obj && (!pcDoc || It->pObject->getDocument()==pcDoc)) { + for(auto &sel : _SelList) { + if(!sel.pDoc) continue; + auto obj = getObjectOfType(sel,App::DocumentObject::getClassTypeId(),resolve); + if(obj && (!pcDoc || sel.pObject->getDocument()==pcDoc)) { return true; } } @@ -425,7 +423,7 @@ std::vector SelectionSingleton::getPickedListEx(const char* pDo } std::vector SelectionSingleton::getObjectList(const char* pDocName, Base::Type typeId, - const std::list<_SelObj> &objList, int resolve, bool single) const + std::list<_SelObj> &objList, int resolve, bool single) const { std::vector temp; if(single) temp.reserve(1); @@ -442,10 +440,10 @@ std::vector SelectionSingleton::getObjectList(const char* pDocN return temp; } - for (const auto &sel : objList) { + for (auto &sel : objList) { if(!sel.pDoc) continue; const char *subelement = 0; - auto obj = getObjectOfType(sel.pObject,sel.SubName.c_str(),typeId,resolve,&subelement); + auto obj = getObjectOfType(sel,typeId,resolve,&subelement); if(!obj || (pcDoc && sel.pObject->getDocument()!=pcDoc)) continue; auto it = SortMap.find(obj); @@ -521,50 +519,33 @@ int SelectionSingleton::getAsPropertyLinkSubList(App::PropertyLinkSubList &prop) return objs.size(); } -App::DocumentObject *SelectionSingleton::getObjectOfType(App::DocumentObject *pObject, - const char *subname, Base::Type type, int resolve,const char **subelement) { - if(!pObject || !pObject->getNameInDocument()) +App::DocumentObject *SelectionSingleton::getObjectOfType(_SelObj &sel, + Base::Type typeId, int resolve, const char **subelement) +{ + auto obj = sel.pObject; + if(!obj || !obj->getNameInDocument()) return 0; - auto ret = pObject; - if(subelement) - *subelement = subname; + const char *subname = sel.SubName.c_str(); if(resolve) { - if(subname && *subname) { - const char *dot = strrchr(subname,'.'); - if(dot && dot!=subname && resolve>1) { - const char *prev = dot-1; - for(;prev!=subname;--prev) { - if(*prev == '.') { - ++prev; - break; - } - } - if(Data::ComplexGeoData::isMappedElement(prev)) { - if(prev!=subname) - dot = prev-1; - else - dot = 0; - } - } - if(subelement) { - if(!dot) - *subelement = subname; - else - *subelement = dot+1; - } - if(dot) { - ret = ret->getSubObject(subname); - if(!ret || !ret->getNameInDocument()) - return 0; - } + if(!sel.resolved) { + sel.resolved = true; + std::pair elementName; + sel.pResolvedObject = App::GeoFeature::resolveElement(obj,subname,elementName); + sel.newElementName = elementName.first; + sel.oldElementName = elementName.second; + if(sel.newElementName.empty()) + sel.newElementName = sel.oldElementName; } - auto linked = ret->getLinkedObject(true); - if(linked && linked->isDerivedFrom(type)) - return ret; + obj = sel.pResolvedObject; + if(resolve>1) + subname = sel.newElementName.c_str(); + else + subname = sel.oldElementName.c_str(); } - if(ret->isDerivedFrom(type)) - return ret; - return 0; + if(!obj || !obj->isDerivedFrom(typeId)) + return 0; + if(subelement) *subelement = subname; + return obj; } vector SelectionSingleton::getObjectsOfType(const Base::Type& typeId, const char* pDocName, int resolve) const @@ -579,9 +560,9 @@ vector SelectionSingleton::getObjectsOfType(const Base::Ty } std::set objs; - for (std::list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) { - if(pcDoc && pcDoc!=It->pDoc) continue; - App::DocumentObject *pObject = getObjectOfType(It->pObject,It->SubName.c_str(),typeId,resolve); + for(auto &sel : _SelList) { + if(pcDoc && pcDoc!=sel.pDoc) continue; + App::DocumentObject *pObject = getObjectOfType(sel,typeId,resolve); if (pObject) { auto ret = objs.insert(pObject); if(ret.second) @@ -610,8 +591,8 @@ unsigned int SelectionSingleton::countObjectsOfType(const Base::Type& typeId, co return 0; } - for (std::list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) { - if((!pcDoc||pcDoc==It->pDoc) && getObjectOfType(It->pObject,It->SubName.c_str(),typeId,resolve)) + for (auto &sel : _SelList) { + if((!pcDoc||pcDoc==sel.pDoc) && getObjectOfType(sel,typeId,resolve)) iNbr++; } @@ -639,23 +620,23 @@ void SelectionSingleton::slotSelectionChanged(const SelectionChanges& msg) { { App::Document* pDoc = getDocument(msg.pDocName); if(!pDoc) return; - const char *subname = 0; - App::DocumentObject* pObject = getObjectOfType(pDoc->getObject(msg.pObjectName), - msg.pSubName, App::DocumentObject::getClassTypeId(),2,&subname); + std::pair elementName; + auto &newElementName = elementName.first; + auto &oldElementName = elementName.second; + auto pObject = App::GeoFeature::resolveElement( + pDoc->getObject(msg.pObjectName),msg.pSubName,elementName); if (!pObject) return; std::string docName(pObject->getDocument()->getName()); std::string objName(pObject->getNameInDocument()); SelectionChanges msg2(msg); msg2.pDocName = docName.c_str(); msg2.pObjectName = objName.c_str(); - msg2.pSubName = subname; + msg2.pSubName = newElementName.size()?newElementName.c_str():oldElementName.c_str(); signalSelectionChanged3(msg2); - if(Data::ComplexGeoData::isMappedElement(subname)) { - const char *dot = strchr(subname,'.'); - if(dot) - msg2.pSubName = dot+1; - } + + msg2.pSubName = oldElementName.c_str(); signalSelectionChanged2(msg2); + }else { signalSelectionChanged3(msg); signalSelectionChanged2(msg); @@ -671,13 +652,20 @@ bool SelectionSingleton::setPreselect(const char* pDocName, const char* pObjectN App::Document* pDoc = getDocument(pDocName); if (!pDoc || !pObjectName) return false; - const char *SubName = pSubName; - App::DocumentObject* pObject = getObjectOfType(pDoc->getObject(pObjectName), - pSubName, App::DocumentObject::getClassTypeId(),gateResolve,&SubName); + std::pair elementName; + auto &newElementName = elementName.first; + auto &oldElementName = elementName.second; + auto pObject = App::GeoFeature::resolveElement( + pDoc->getObject(pObjectName),pSubName,elementName); if (!pObject) return false; - if (!ActiveGate->allow(pObject->getDocument(),pObject,SubName)) { + const char *subelement = pSubName; + if(gateResolve > 1) + subelement = newElementName.size()?newElementName.c_str():oldElementName.c_str(); + else if(gateResolve) + subelement = oldElementName.c_str(); + if (!ActiveGate->allow(pObject->getDocument(),pObject,subelement)) { QString msg; if (ActiveGate->notAllowedReason.length() > 0){ msg = QObject::tr(ActiveGate->notAllowedReason.c_str()); @@ -863,12 +851,21 @@ bool SelectionSingleton::addSelection(const char* pDocName, const char* pObjectN else temp.pObject = 0; + temp.DocName = pDocName; + temp.FeatName = pObjectName ? pObjectName : ""; + temp.SubName = pSubName ? pSubName : ""; + temp.x = x; + temp.y = y; + temp.z = z; + + if (temp.pObject) + temp.TypeName = temp.pObject->getTypeId().getName(); + // check for a Selection Gate if (ActiveGate) { - const char *SubName = pSubName; - auto pObject = getObjectOfType(temp.pObject, pSubName, - App::DocumentObject::getClassTypeId(),gateResolve,&SubName); - if (!ActiveGate->allow(pObject?pObject->getDocument():temp.pDoc,pObject,SubName)) { + const char *subelement = 0; + auto pObject = getObjectOfType(temp,App::DocumentObject::getClassTypeId(),gateResolve,&subelement); + if (!ActiveGate->allow(pObject?pObject->getDocument():temp.pDoc,pObject,subelement)) { if (getMainWindow()) { QString msg; if (ActiveGate->notAllowedReason.length() > 0) { @@ -886,16 +883,6 @@ bool SelectionSingleton::addSelection(const char* pDocName, const char* pObjectN } } - temp.DocName = pDocName; - temp.FeatName = pObjectName ? pObjectName : ""; - temp.SubName = pSubName ? pSubName : ""; - temp.x = x; - temp.y = y; - temp.z = z; - - if (temp.pObject) - temp.TypeName = temp.pObject->getTypeId().getName(); - _SelList.push_back(temp); SelectionChanges Chng; @@ -909,7 +896,6 @@ bool SelectionSingleton::addSelection(const char* pDocName, const char* pObjectN Chng.z = z; Chng.Type = SelectionChanges::AddSelection; - FC_LOG("Add Selection "<< (pDocName?pDocName:"(null)")<<'.'<< (pObjectName?pObjectName:"(null)")<<'.' << @@ -1104,68 +1090,6 @@ void SelectionSingleton::rmvSelection(const char* pDocName, const char* pObjectN } } -App::DocumentObject *SelectionSingleton::resolveObject( - App::DocumentObject *pObject, const char *subname, - App::DocumentObject **parent, std::string *elementName, - const char **subElement) -{ - if(parent) *parent = 0; - if(subElement) *subElement = 0; - if(!pObject || !subname || *subname==0) - return pObject; - - auto obj = pObject->getSubObject(subname); - if(!obj) - return pObject; - - if(!parent && !subElement) - return obj; - - // NOTE, the convension of '.' separated SubName demands a mandatory ending - // '.' for each object name in SubName, even if there is no subelement - // following it. So finding the last dot will give us the end of the last - // object name. - const char *dot = strrchr(subname,'.'); - if(!dot || dot == subname) { - if(subElement && dot) - *subElement = dot+1; - return obj; // this means no parent object reference in SubName - } - - if(parent) - *parent = pObject; - - bool elementMapChecked = false; - const char *lastDot = dot; - for(--dot;dot!=subname;--dot) { - // check for the second last dot, which is the end of the last parent object - if(*dot == '.') { - // We can't get parent object by its name, because the object may be - // externally linked (i.e. in a different document). So go through - // getSubObject again. - auto sobj = pObject->getSubObject(std::string(subname,dot-subname+1).c_str()); - if(parent) *parent = sobj; - if(sobj!=pObject) - break; - if(!elementMapChecked) { - elementMapChecked = true; - if(Data::ComplexGeoData::isMappedElement(dot+1)) - lastDot = dot; - } - } - } - if(elementName && lastDot!=dot) { - if(*dot == '.') - ++dot; - const char *nextDot = strchr(dot,'.'); - assert(nextDot); - *elementName = std::string(dot,nextDot-dot); - } - if(subElement) - *subElement = lastDot+1; - return obj; -} - void SelectionSingleton::setVisible(int visible) { std::set > filter; if(visible<0) @@ -1178,7 +1102,7 @@ void SelectionSingleton::setVisible(int visible) { // get parent object App::DocumentObject *parent = 0; std::string elementName; - auto obj = Selection().resolveObject(sel.pObject,sel.SubName.c_str(),&parent,&elementName); + auto obj = sel.pObject->resolve(sel.SubName.c_str(),&parent,&elementName); if(!obj || !obj->getNameInDocument() || (parent && !parent->getNameInDocument())) continue; // try call parent object's setElementVisibility @@ -1583,13 +1507,6 @@ PyMethodDef SelectionSingleton::Methods[] = { "Gui.Selection.addSelectionGate(Gate())"}, {"removeSelectionGate", (PyCFunction) SelectionSingleton::sRemoveSelectionGate, METH_VARARGS, "removeSelectionGate() -- remove the active selection gate\n"}, - {"resolveObject", (PyCFunction) SelectionSingleton::sResolveObject, 1, - "resolveObject(object, subname) -- resolve the sub object\n" - "Returns a tuple (subobj,parent,elementName,subElement), where 'subobj' is the last\n" - "object referenced in 'subname', and 'parent' is the direct parent of 'subobj', and\n" - "'elementName' is the child name of the subobj, which can be used to call\n" - "parent.isElementVisible/setElementVisible(). 'subElement' is the non-object\n" - "sub-element name if any."}, {"setVisible", (PyCFunction) SelectionSingleton::sSetVisible, 1, "setVisible(visible=None) -- set visibility of all selection items\n" "If 'visible' is None, then toggle visibility"}, @@ -2014,28 +1931,3 @@ PyObject *SelectionSingleton::sSetVisible(PyObject * /*self*/, PyObject *args, P Py_Return; } -PyObject *SelectionSingleton::sResolveObject(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) -{ - PyObject *pyobj; - const char *subname; - if (!PyArg_ParseTuple(args, "O!s",&App::DocumentObjectPy::Type,&pyobj,&subname)) - return NULL; // NULL triggers exception - - PY_TRY { - std::string elementName; - const char *subElement = 0; - App::DocumentObject *parent = 0; - auto obj = Selection().resolveObject( - static_cast(pyobj)->getDocumentObjectPtr(), - subname,&parent,&elementName,&subElement); - - Py::Tuple ret(4); - ret.setItem(0,Py::Object(obj->getPyObject(),true)); - ret.setItem(1,parent?Py::Object(parent->getPyObject(),true):Py::None()); - ret.setItem(2,Py::String(elementName.c_str())); - ret.setItem(3,Py::String(subElement?subElement:"")); - return Py::new_reference_to(ret); - } PY_CATCH; - - Py_Return; -} diff --git a/src/Gui/Selection.h b/src/Gui/Selection.h index cf349d4d16dd..035b2a44c6e6 100644 --- a/src/Gui/Selection.h +++ b/src/Gui/Selection.h @@ -310,24 +310,6 @@ class GuiExport SelectionSingleton : public Base::Subject inline std::vector getObjectsOfType( const char* pDocName=0, int resolve=1) const; - /** Resolve the last document object referenced in the subname - * - * @param pObject: the top parent object - * @param subname: dot separated subname - * @param parent: return the direct parent of the object - * @param elementName: return element name to be passed to - * DocumentObject::is//setElementVisible() - * @param subElement: return non-object sub-element name if found. The - * pointer is guaranteed to be within the buffer pointed to by 'subname' - * - * @return Returns the last referenced document object in the subname. If no - * such object in subname, return pObject. - * @ - */ - static App::DocumentObject *resolveObject(App::DocumentObject *pObject, - const char *subname, App::DocumentObject **parent=0, - std::string *elementName=0, const char **subElement=0); - /** Set selection object visibility * * @param visible: 1: make visible, 0: make invisible, -1: toggle visibility @@ -420,7 +402,6 @@ class GuiExport SelectionSingleton : public Base::Subject _SelList; + mutable std::list<_SelObj> _SelList; - std::list<_SelObj> _PickedList; + mutable std::list<_SelObj> _PickedList; bool _needPickedList; - std::vector getObjectList(const char* pDocName,Base::Type typeId, const std::list<_SelObj> &objs, int resolve, bool single=false) const; + std::vector getObjectList(const char* pDocName,Base::Type typeId, std::list<_SelObj> &objs, int resolve, bool single=false) const; - static App::DocumentObject *getObjectOfType(App::DocumentObject *pObject, - const char *subname, Base::Type type, int resolve, const char **subelement=0); + static App::DocumentObject *getObjectOfType(_SelObj &sel, Base::Type type, + int resolve, const char **subelement=0); static SelectionSingleton* _pcSingleton;