diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 53a0bd44f4..200640d8b7 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -1702,6 +1702,7 @@ void Application::initTypes(void) App ::PropertyXLink ::init(); App ::PropertyXLinkSub ::init(); App ::PropertyXLinkSubList ::init(); + App ::PropertyXLinkList ::init(); App ::PropertyXLinkContainer ::init(); App ::PropertyMatrix ::init(); App ::PropertyVector ::init(); diff --git a/src/App/Property.h b/src/App/Property.h index 95c263a775..8e4ff24061 100644 --- a/src/App/Property.h +++ b/src/App/Property.h @@ -376,8 +376,7 @@ public: P & mProp; /**< Referenced to property we work on */ }; -private: - +protected: int signalCounter; /**< Counter for invoking transaction start/stop */ bool hasChanged; }; diff --git a/src/App/PropertyLinks.cpp b/src/App/PropertyLinks.cpp index 2cd85c329e..3f7ba7a80d 100644 --- a/src/App/PropertyLinks.cpp +++ b/src/App/PropertyLinks.cpp @@ -3306,9 +3306,9 @@ Property *PropertyXLink::CopyOnImportExternal( if(subs.empty() && linked==_pcLink) return 0; - PropertyXLink *p= createInstance(); + std::unique_ptr p(new PropertyXLink); copyTo(*p,linked,&subs); - return p; + return p.release(); } Property *PropertyXLink::CopyOnLinkReplace(const App::DocumentObject *parent, @@ -3317,13 +3317,9 @@ Property *PropertyXLink::CopyOnLinkReplace(const App::DocumentObject *parent, auto res = tryReplaceLinkSubs(getContainer(),_pcLink,parent,oldObj,newObj,_SubList); if(!res.first) return 0; - PropertyXLink *p= createInstance(); + std::unique_ptr p(new PropertyXLink); copyTo(*p,res.first,&res.second); - return p; -} - -PropertyXLink *PropertyXLink::createInstance() const { - return new PropertyXLink(); + return p.release(); } Property *PropertyXLink::CopyOnLabelChange(App::DocumentObject *obj, @@ -3335,9 +3331,9 @@ Property *PropertyXLink::CopyOnLabelChange(App::DocumentObject *obj, auto subs = updateLinkSubs(_pcLink,_SubList,&updateLabelReference,obj,ref,newLabel); if(subs.empty()) return 0; - PropertyXLink *p= createInstance(); + std::unique_ptr p(new PropertyXLink); copyTo(*p,_pcLink,&subs); - return p; + return p.release(); } void PropertyXLink::copyTo(PropertyXLink &other, @@ -3365,9 +3361,9 @@ void PropertyXLink::copyTo(PropertyXLink &other, Property *PropertyXLink::Copy(void) const { - PropertyXLink *p= createInstance(); + std::unique_ptr p(new PropertyXLink); copyTo(*p); - return p; + return p.release(); } void PropertyXLink::Paste(const Property &from) @@ -3593,10 +3589,6 @@ PropertyXLinkSub::PropertyXLinkSub(bool allowPartial, PropertyLinkBase *parent) PropertyXLinkSub::~PropertyXLinkSub() { } -PropertyXLink *PropertyXLinkSub::createInstance() const{ - return new PropertyXLinkSub(); -} - bool PropertyXLinkSub::upgrade(Base::XMLReader &reader, const char *typeName) { if(strcmp(typeName, PropertyLinkSubGlobal::getClassTypeId().getName())==0 || strcmp(typeName, PropertyLinkSub::getClassTypeId().getName())==0 || @@ -3717,7 +3709,7 @@ void PropertyXLinkSubList::setValues( FC_THROWM(Base::ValueError,"invalid document object"); } - aboutToSetValue(); + atomic_change guard(*this); for(auto it=_Links.begin(),itNext=it;it!=_Links.end();it=itNext) { ++itNext; @@ -3734,7 +3726,7 @@ void PropertyXLinkSubList::setValues( _Links.emplace_back(testFlag(LinkAllowPartial),this); _Links.back().setValue(v.first,std::move(v.second)); } - hasSetValue(); + guard.tryInvoke(); } void PropertyXLinkSubList::addValue(App::DocumentObject *obj, @@ -3762,10 +3754,10 @@ void PropertyXLinkSubList::addValue(App::DocumentObject *obj, return; } } - aboutToSetValue(); + atomic_change guard(*this); _Links.emplace_back(testFlag(LinkAllowPartial),this); _Links.back().setValue(obj,std::move(subs)); - hasSetValue(); + guard.tryInvoke(); } void PropertyXLinkSubList::setValue(DocumentObject* lValue, const std::vector &SubList) @@ -3776,6 +3768,41 @@ void PropertyXLinkSubList::setValue(DocumentObject* lValue, const std::vector &values) { + atomic_change guard(*this); + _Links.clear(); + for(auto obj : values) { + _Links.emplace_back(testFlag(LinkAllowPartial),this); + _Links.back().setValue(obj); + } + guard.tryInvoke(); +} + +void PropertyXLinkSubList::set1Value(int idx, + DocumentObject *value, + const std::vector &SubList) +{ + if(idx < -1 || idx > getSize()) + throw Base::RuntimeError("index out of bound"); + + if(idx < 0 || idx+1 == getSize()) { + if(SubList.empty()) { + addValue(value,SubList); + return; + } + atomic_change guard(*this); + _Links.emplace_back(testFlag(LinkAllowPartial),this); + _Links.back().setValue(value); + guard.tryInvoke(); + return; + } + + auto it = _Links.begin(); + for(;idx;--idx) + ++it; + it->setValue(value,SubList); +} + const string PropertyXLinkSubList::getPyReprString() const { if (_Links.empty()) @@ -3810,15 +3837,18 @@ DocumentObject *PropertyXLinkSubList::getValue() const int PropertyXLinkSubList::removeValue(App::DocumentObject *lValue) { + atomic_change guard(*this,false); int ret = 0; - auto it = std::find_if(_Links.begin(),_Links.end(), - [=](const PropertyXLinkSub &l){return l.getValue()==lValue;}); - if(it != _Links.end()) { - ret = (int)it->getSubValues().size(); - if(!ret) - ret = 1; - _Links.erase(it); + for(auto it=_Links.begin();it!=_Links.end();) { + if(it->getValue() != lValue) + ++it; + else { + guard.aboutToChange(); + it = _Links.erase(it); + ++ret; + } } + guard.tryInvoke(); return ret; } @@ -3829,6 +3859,7 @@ PyObject *PropertyXLinkSubList::getPyObject(void) auto obj = link.getValue(); if(!obj || !obj->getNameInDocument()) continue; + Py::Tuple tup(2); tup[0] = Py::asObject(obj->getPyObject()); @@ -3918,12 +3949,14 @@ void PropertyXLinkSubList::Restore(Base::XMLReader &reader) reader.hasAttribute("partial") && reader.getAttributeAsInteger("partial")); int count = reader.getAttributeAsInteger("count"); + atomic_change guard(*this,false); _Links.clear(); for(int i=0;i &objs, if(obj && obj->getNameInDocument()) count += l.getSubValues().size(); } + if(!count) { + objs.reserve(objs.size()+_Links.size()); + for(auto &l : _Links) { + auto obj = l.getValue(); + if(obj && obj->getNameInDocument()) + objs.push_back(obj); + } + return; + } + objs.reserve(objs.size()+count); subs->reserve(subs->size()+count); for(auto &l : _Links) { @@ -4127,18 +4170,14 @@ void PropertyXLinkSubList::breakLink(App::DocumentObject *obj, bool clear) { setValue(0); return; } - bool touched = false; + atomic_change guard(*this,false); for(auto &l : _Links) { if(l.getValue() == obj) { - if(!touched) { - touched = true; - aboutToSetValue(); - } + guard.aboutToChange(); l.setValue(0); } } - if(touched) - hasSetValue(); + guard.tryInvoke(); } bool PropertyXLinkSubList::adjustLink(const std::set &inList) { @@ -4183,9 +4222,18 @@ int PropertyXLinkSubList::checkRestore(std::string *msg) const { } bool PropertyXLinkSubList::upgrade(Base::XMLReader &reader, const char *typeName) { - if(strcmp(typeName, PropertyLinkSubListGlobal::getClassTypeId().getName())==0 || - strcmp(typeName, PropertyLinkSubList::getClassTypeId().getName())==0 || - strcmp(typeName, PropertyLinkSubListChild::getClassTypeId().getName())==0) + if(strcmp(typeName, PropertyLinkListGlobal::getClassTypeId().getName())==0 || + strcmp(typeName, PropertyLinkList::getClassTypeId().getName())==0 || + strcmp(typeName, PropertyLinkListChild::getClassTypeId().getName())==0) + { + PropertyLinkList linkProp; + linkProp.setContainer(getContainer()); + linkProp.Restore(reader); + setValues(linkProp.getValues()); + return true; + } else if (strcmp(typeName, PropertyLinkSubListGlobal::getClassTypeId().getName())==0 || + strcmp(typeName, PropertyLinkSubList::getClassTypeId().getName())==0 || + strcmp(typeName, PropertyLinkSubListChild::getClassTypeId().getName())==0) { PropertyLinkSubList linkProp; linkProp.setContainer(getContainer()); @@ -4215,11 +4263,67 @@ void PropertyXLinkSubList::setAllowPartial(bool enable) { } void PropertyXLinkSubList::hasSetChildValue(Property &) { - hasSetValue(); + if(!signalCounter) + hasSetValue(); } void PropertyXLinkSubList::aboutToSetChildValue(Property &) { - aboutToSetValue(); + if(!signalCounter || !hasChanged) { + aboutToSetValue(); + if(signalCounter) + hasChanged = true; + } +} + +//************************************************************************** +// PropertyXLinkList +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +TYPESYSTEM_SOURCE(App::PropertyXLinkList , App::PropertyXLinkSubList) + +//************************************************************************** +// Construction/Destruction + +PropertyXLinkList::PropertyXLinkList() +{ +} + +PropertyXLinkList::~PropertyXLinkList() +{ +} + +PyObject *PropertyXLinkList::getPyObject(void) +{ + for(auto &link : _Links) { + auto obj = link.getValue(); + if(!obj || !obj->getNameInDocument()) + continue; + if(link.hasSubName()) + return PropertyXLinkSubList::getPyObject(); + } + + Py::List list; + for(auto &link : _Links) { + auto obj = link.getValue(); + if(!obj || !obj->getNameInDocument()) + continue; + list.append(Py::asObject(obj->getPyObject())); + } + return Py::new_reference_to(list); +} + +void PropertyXLinkList::setPyObject(PyObject *value) +{ + try { //try PropertyLinkList syntax + PropertyLinkList dummy; + dummy.setAllowExternal(true); + dummy.setPyObject(value); + this->setValues(dummy.getValues()); + return; + } + catch (Base::Exception&) {} + + PropertyXLinkSubList::setPyObject(value); } //************************************************************************** diff --git a/src/App/PropertyLinks.h b/src/App/PropertyLinks.h index 3c214acf70..c37aca223c 100644 --- a/src/App/PropertyLinks.h +++ b/src/App/PropertyLinks.h @@ -762,8 +762,6 @@ public: PropertyLinkListHidden() {_pcScope = LinkScope::Hidden;}; }; -class PropertyXLinkSub; - /** the Link Property with sub elements * This property links an object and a defined sequence of * sub elements. These subelements (like Edges of a Shape) @@ -1129,16 +1127,14 @@ public: return filePath.c_str(); } + virtual bool upgrade(Base::XMLReader &reader, const char *typeName); + protected: void unlink(); void detach(); void restoreLink(App::DocumentObject *); - virtual PropertyXLink *createInstance() const; - - virtual bool upgrade(Base::XMLReader &reader, const char *typeName); - void copyTo(PropertyXLink &other, App::DocumentObject *linked=0, std::vector *subs=0) const; virtual void aboutToSetValue() override; @@ -1174,16 +1170,21 @@ public: virtual PyObject *getPyObject(void) override; -protected: - virtual PropertyXLink *createInstance() const override; + virtual const char* getEditorName(void) const override + { return "Gui::PropertyEditor::PropertyLinkItem"; } }; /** Link to one or more (sub)object(s) of one or more object(s) from the same or different document */ -class AppExport PropertyXLinkSubList: public PropertyLinkBase { +class AppExport PropertyXLinkSubList: public PropertyLinkBase + , public AtomicPropertyChangeInterface +{ TYPESYSTEM_HEADER_WITH_OVERRIDE(); + typedef typename AtomicPropertyChangeInterface::AtomicPropertyChange atomic_change; + friend atomic_change; + public: PropertyXLinkSubList(); virtual ~PropertyXLinkSubList(); @@ -1197,6 +1198,9 @@ public: * setValue(0, whatever) clears the property */ void setValue(DocumentObject*,const char*); + void setValues(const std::vector&); + void set1Value(int idx, DocumentObject *value, const std::vector &SubList={}); + void setValues(const std::vector&,const std::vector&); void setValues(const std::vector&,const std::vector&); void setValues(std::map > &&); @@ -1209,7 +1213,7 @@ public: * @brief setValue: PropertyLinkSub-compatible overload * @param SubList */ - void setValue(App::DocumentObject *lValue, const std::vector &SubList=std::vector()); + void setValue(App::DocumentObject *lValue, const std::vector &SubList={}); std::vector getValues(void); @@ -1279,6 +1283,25 @@ protected: std::list _Links; }; + +/** Link to one or more (sub)object(s) of one or more object(s) from the same or different document + * + * The only difference for PropertyXLinkList and PropertyXLinkSubList is in + * their getPyObject(). PropertyXLinkList will return a list of object is + * there is no sub-object/sub-elements in the property. + */ +class AppExport PropertyXLinkList: public PropertyXLinkSubList { + TYPESYSTEM_HEADER_WITH_OVERRIDE(); + +public: + PropertyXLinkList(); + virtual ~PropertyXLinkList(); + + virtual PyObject *getPyObject(void) override; + virtual void setPyObject(PyObject *) override; +}; + + /** Abstract property that can link to multiple external objects * * @sa See PropertyExpressionEngine for example usage