App: add PropertyXLinkList

PropertyXLinkList is derived from PropertyXLinkSubList, which is
modified to provide APIs compatible to PropertyLinkList.

The only difference between the two class is in their getPyObject() to
make PropertyXLinkList output similar to PropertyLinkList in Python. In
C++, both class can accept mix of objects link with or without subname,
depending on which set of APIs are used.
This commit is contained in:
Zheng, Lei
2020-01-22 17:06:46 +08:00
committed by WandererFan
parent 6922b83674
commit 8fd53f4f75
4 changed files with 178 additions and 51 deletions

View File

@@ -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();

View File

@@ -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;
};

View File

@@ -3306,9 +3306,9 @@ Property *PropertyXLink::CopyOnImportExternal(
if(subs.empty() && linked==_pcLink)
return 0;
PropertyXLink *p= createInstance();
std::unique_ptr<PropertyXLink> 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<PropertyXLink> 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<PropertyXLink> 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<PropertyXLink> 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<string> &SubList)
@@ -3776,6 +3768,41 @@ void PropertyXLinkSubList::setValue(DocumentObject* lValue, const std::vector<st
setValues(std::move(values));
}
void PropertyXLinkSubList::setValues(const std::vector<DocumentObject*> &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<std::string> &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<count;++i) {
_Links.emplace_back(false,this);
_Links.back().Restore(reader);
}
reader.readEndElement("XLinkSubList");
guard.tryInvoke();
}
Property *PropertyXLinkSubList::CopyOnImportExternal(
@@ -4108,6 +4141,16 @@ void PropertyXLinkSubList::getLinks(std::vector<App::DocumentObject *> &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<App::DocumentObject*> &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);
}
//**************************************************************************

View File

@@ -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<std::string> *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<PropertyXLinkSubList>
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();
typedef typename AtomicPropertyChangeInterface<PropertyXLinkSubList>::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<DocumentObject*>&);
void set1Value(int idx, DocumentObject *value, const std::vector<std::string> &SubList={});
void setValues(const std::vector<DocumentObject*>&,const std::vector<const char*>&);
void setValues(const std::vector<DocumentObject*>&,const std::vector<std::string>&);
void setValues(std::map<App::DocumentObject*,std::vector<std::string> > &&);
@@ -1209,7 +1213,7 @@ public:
* @brief setValue: PropertyLinkSub-compatible overload
* @param SubList
*/
void setValue(App::DocumentObject *lValue, const std::vector<std::string> &SubList=std::vector<std::string>());
void setValue(App::DocumentObject *lValue, const std::vector<std::string> &SubList={});
std::vector<DocumentObject*> getValues(void);
@@ -1279,6 +1283,25 @@ protected:
std::list<PropertyXLinkSub> _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