App: Property related API changes

Property:

* Extended property status bitset. Mirror most of PropertyType and
  allow dynamic change property type.

* Cache property name and type to improve performance

* Centralize property status change signalling

* Change aboutToSetValue()/hasSetValue() to virtual

* Add new API getFullName() to obtain full quanlified name of the property

AtomicPropertyChangeInterface:

* Allow calling aboutToSetValue()/hasSetValue() when actually changed

PropertyLists:

* Refactor implementation by an abstract class PropertyListBase and a
  template class PropertyListsT, to allow better code reuse.
  PropertyListT is derived from AtomicPropertyChangeInterface to allow
  more efficient change on individual elements.

* All list type property now accept setting python value as a dictionary
  with index as key to set individual element of a list.

* Add touch list for more efficient handling of value changes. The list
  contains the index of changed value. And empty touch list should be
  treated as the entire list is changed. PropertyContainerPy expose this
  functionality with getPropertyTouchList().

PropertyPersistentObject:

* New property to allow dynamic creation of any FreeCAD object derived
  from Base::Persistence, and use it as a property.

DynamicProperty:

* Use boost multi_index_container for efficient property lookup while
  keeping order.

* Modify to be allowed to use in PropertyContainer directly

PropertyContainer:

* Use boost multi_index_container for efficient property lookup while
  keeping order.

* Allow adding/removing dynamic property on all property container

* Modify Save/Restore() to persist property status, and better handle
  transient property which can now be dynamically enabled/disabled per
  object.

* Add new API getFullName() to obtain full quanlified name of the property.
  Implemented by Document, DocumentObject, and also
  ViewProviderDocumentObject if future patch

DocumentObject and FeaturePython are modified to accommondate the
dynamic property changes.

Removed get/setCustomAttribute() implementation from DocumentObjectPy,
and rely on PropertyContainerPy for the implementation, because of the
additional dynamic property support in property container.

Gui::ViewProviderDocumentObject, which is derived from
PropertyContainer, is also modified accordingly
This commit is contained in:
Zheng, Lei
2019-06-28 10:16:42 +08:00
committed by wmayer
parent ff1d1cd341
commit f4205130ae
25 changed files with 1511 additions and 1579 deletions

View File

@@ -423,7 +423,7 @@ Document* Application::newDocument(const char * Name, const char * UserName)
}
// create the FreeCAD document
std::unique_ptr<Document> newDoc(new Document());
std::unique_ptr<Document> newDoc(new Document(name.c_str()));
// add the document to the internal list
DocMap[name] = newDoc.release(); // now owned by the Application
@@ -448,6 +448,8 @@ Document* Application::newDocument(const char * Name, const char * UserName)
_pActiveDoc->signalAbortTransaction.connect(boost::bind(&App::Application::slotAbortTransaction, this, _1));
_pActiveDoc->signalStartSave.connect(boost::bind(&App::Application::slotStartSaveDocument, this, _1, _2));
_pActiveDoc->signalFinishSave.connect(boost::bind(&App::Application::slotFinishSaveDocument, this, _1, _2));
_pActiveDoc->signalChangePropertyEditor.connect(
boost::bind(&App::Application::slotChangePropertyEditor, this, _1, _2));
// make sure that the active document is set in case no GUI is up
{
@@ -1131,6 +1133,11 @@ void Application::slotFinishSaveDocument(const App::Document& doc, const std::st
this->signalFinishSaveDocument(doc, filename);
}
void Application::slotChangePropertyEditor(const App::Document &doc, const App::Property &prop)
{
this->signalChangePropertyEditor(doc,prop);
}
//**************************************************************************
// Init, Destruct and singleton
@@ -1410,6 +1417,7 @@ void Application::initTypes(void)
App ::PropertyIntegerSet ::init();
App ::PropertyMap ::init();
App ::PropertyString ::init();
App ::PropertyPersistentObject ::init();
App ::PropertyUUID ::init();
App ::PropertyFont ::init();
App ::PropertyStringList ::init();

View File

@@ -184,7 +184,7 @@ public:
/// signal on about removing a dynamic property
boost::signals2::signal<void (const App::Property&)> signalRemoveDynamicProperty;
/// signal on about changing the editor mode of a property
boost::signals2::signal<void (const App::Property&)> signalChangePropertyEditor;
boost::signals2::signal<void (const App::Document&, const App::Property&)> signalChangePropertyEditor;
//@}
@@ -342,6 +342,7 @@ protected:
void slotAbortTransaction(const App::Document&);
void slotStartSaveDocument(const App::Document&, const std::string&);
void slotFinishSaveDocument(const App::Document&, const std::string&);
void slotChangePropertyEditor(const App::Document&, const App::Property &);
//@}
private:

View File

@@ -940,15 +940,20 @@ bool Document::redo(void)
return false;
}
void Document::removePropertyOfObject(TransactionalObject* obj, const char* name)
void Document::addOrRemovePropertyOfObject(TransactionalObject* obj, Property *prop, bool add)
{
Property* prop = obj->getDynamicPropertyByName(name);
if (prop) {
if (d->activeUndoTransaction)
d->activeUndoTransaction->removeProperty(obj, prop);
for (auto it : mUndoTransactions)
it->removeProperty(obj, prop);
if (!prop || !obj)
return;
if(d->iUndoMode && !isPerformingTransaction() && !d->activeUndoTransaction) {
if(!testStatus(Restoring) || testStatus(Importing)) {
int tid=0;
const char *name = GetApplication().getActiveTransaction(&tid);
if(name && tid>0)
_openTransaction(name,tid);
}
}
if (d->activeUndoTransaction)
d->activeUndoTransaction->addOrRemoveProperty(obj, prop, add);
}
bool Document::isPerformingTransaction() const
@@ -1202,7 +1207,8 @@ void Document::setTransactionMode(int iMode)
//--------------------------------------------------------------------------
// constructor
//--------------------------------------------------------------------------
Document::Document(void)
Document::Document(const char *name)
: myName(name)
{
// Remark: In a constructor we should never increment a Python object as we cannot be sure
// if the Python interpreter gets a reference of it. E.g. if we increment but Python don't
@@ -1957,7 +1963,12 @@ bool Document::isSaved() const
*/
const char* Document::getName() const
{
return GetApplication().getDocumentName(this);
// return GetApplication().getDocumentName(this);
return myName.c_str();
}
std::string Document::getFullName() const {
return myName;
}
/// Remove all modifications. After this call The document becomes valid again.

View File

@@ -169,6 +169,7 @@ public:
boost::signals2::signal<void (const App::Document&)> signalCommitTransaction;
// signal an aborted transaction
boost::signals2::signal<void (const App::Document&)> signalAbortTransaction;
boost::signals2::signal<void (const App::Document&,const App::Property&)> signalChangePropertyEditor;
//@}
/** @name File handling of the document */
@@ -340,8 +341,8 @@ public:
bool redo() ;
/// returns true if the document is in an Transaction phase, e.g. currently performing a redo/undo or rollback
bool isPerformingTransaction() const;
/// \internal remove property from a transactional object with name \a name
void removePropertyOfObject(TransactionalObject*, const char*);
/// \internal add or remove property from a transactional object
void addOrRemovePropertyOfObject(TransactionalObject*, Property *prop, bool add);
//@}
/** @name dependency stuff */
@@ -389,6 +390,8 @@ public:
virtual PyObject *getPyObject(void);
virtual std::string getFullName() const override;
friend class Application;
/// because of transaction handling
friend class TransactionalObject;
@@ -401,7 +404,7 @@ public:
protected:
/// Construction
Document(void);
Document(const char *name = "");
void _removeObject(DocumentObject* pcObject);
void _addObject(DocumentObject* pcObject, const char* pObjectName);
@@ -438,6 +441,8 @@ private:
// pointer to the python class
Py::Object DocumentPythonObject;
struct DocumentP* d;
std::string myName;
};
template<typename T>

View File

@@ -529,27 +529,44 @@ void DocumentObject::setDocument(App::Document* doc)
onSettingDocument();
}
void DocumentObject::onAboutToRemoveProperty(const char* name)
bool DocumentObject::removeDynamicProperty(const char* name)
{
if (_pDoc) {
_pDoc->removePropertyOfObject(this, name);
if (!_pDoc)
return false;
Property* prop = getDynamicPropertyByName(name);
if (prop) {
auto expressions = ExpressionEngine.getExpressions();
std::vector<App::ObjectIdentifier> removeExpr;
Property* prop = getDynamicPropertyByName(name);
if(!prop || prop->testStatus(App::Property::LockDynamic))
return false;
for (auto it : expressions) {
if (it.first.getProperty() == prop) {
removeExpr.push_back(it.first);
}
}
if(prop->isDerivedFrom(PropertyLinkBase::getClassTypeId()))
clearOutListCache();
for (auto it : removeExpr) {
ExpressionEngine.setValue(it, boost::shared_ptr<Expression>());
}
_pDoc->addOrRemovePropertyOfObject(this, prop, false);
auto expressions = ExpressionEngine.getExpressions();
std::vector<App::ObjectIdentifier> removeExpr;
for (auto it : expressions) {
if (it.first.getProperty() == prop) {
removeExpr.push_back(it.first);
}
}
for (auto it : removeExpr) {
ExpressionEngine.setValue(it, boost::shared_ptr<Expression>());
}
return TransactionalObject::removeDynamicProperty(name);
}
App::Property* DocumentObject::addDynamicProperty(
const char* type, const char* name, const char* group, const char* doc,
short attr, bool ro, bool hidden)
{
auto prop = TransactionalObject::addDynamicProperty(type,name,group,doc,attr,ro,hidden);
if(prop && _pDoc)
_pDoc->addOrRemovePropertyOfObject(this, prop, true);
return prop;
}
void DocumentObject::onBeforeChange(const Property* prop)
@@ -580,8 +597,14 @@ void DocumentObject::onChanged(const Property* prop)
_pDoc->signalRelabelObject(*this);
// set object touched if it is an input property
if (!(prop->getType() & Prop_Output)) {
StatusBits.set(ObjectStatus::Touch);
if (!testStatus(ObjectStatus::NoTouch)
&& !(prop->getType() & Prop_Output)
&& !prop->testStatus(Property::Output))
{
if(!StatusBits.test(ObjectStatus::Touch)) {
FC_TRACE("touch '" << getFullName() << "' on change of '" << prop->getName() << "'");
StatusBits.set(ObjectStatus::Touch);
}
// must execute on document recompute
if (!(prop->getType() & Prop_NoRecompute))
StatusBits.set(ObjectStatus::Enforce);
@@ -1008,3 +1031,9 @@ bool DocumentObject::redirectSubName(std::ostringstream &, DocumentObject *, Doc
return false;
}
void DocumentObject::onPropertyStatusChanged(const Property &prop, unsigned long oldStatus) {
(void)oldStatus;
if(!Document::isAnyRestoring() && getNameInDocument() && getDocument())
getDocument()->signalChangePropertyEditor(*getDocument(),prop);
}

View File

@@ -115,7 +115,7 @@ public:
/// Return the object ID that is unique within its owner document
long getID() const {return _Id;}
/// Return the object full name of the form DocName#ObjName
std::string getFullName() const;
virtual std::string getFullName() const override;
virtual bool isAttachedToDocument() const;
virtual const char* detachFromDocument();
/// gets the document in which this Object is handled
@@ -365,6 +365,13 @@ public:
return _pcViewProviderName.c_str();
}
virtual bool removeDynamicProperty(const char* prop) override;
virtual App::Property* addDynamicProperty(
const char* type, const char* name=0,
const char* group=0, const char* doc=0,
short attr=0, bool ro=false, bool hidden=false) override;
/** Resolve the last document object referenced in the subname
*
* @param subname: dot separated subname
@@ -438,8 +445,6 @@ protected:
void resetError(void){StatusBits.reset(ObjectStatus::Error);}
void setDocument(App::Document* doc);
/// \internal get called when removing a property of name \a prop
void onAboutToRemoveProperty(const char* prop);
/// get called before the value is changed
virtual void onBeforeChange(const Property* prop);
/// get called by the container when a property was changed
@@ -453,6 +458,9 @@ protected:
/// get called when object is going to be removed from the document
virtual void unsetupObject();
/// get called when a property status has changed
virtual void onPropertyStatusChanged(const Property &prop, unsigned long oldStatus) override;
/// python object of this class and all descendent
protected: // attributes
Py::Object PythonObject;

View File

@@ -672,20 +672,39 @@ PyObject* DocumentObjectPy::getPathsByOutList(PyObject *args)
PyObject *DocumentObjectPy::getCustomAttributes(const char* attr) const
{
// Dynamic proeprty is now directly supported in PropertyContainer. So we
// can comment out here and let PropertyContainerPy handle it.
#if 1
(void)attr;
#else
// search for dynamic property
Property* prop = getDocumentObjectPtr()->getDynamicPropertyByName(attr);
if (prop)
return prop->getPyObject();
else
#endif
return 0;
}
int DocumentObjectPy::setCustomAttributes(const char* attr, PyObject *obj)
{
// The following code is practically the same as in PropertyContainerPy,
// especially since now dynamic proeprty is directly supported in
// PropertyContainer. So we can comment out here and let PropertyContainerPy
// handle it.
#if 1
(void)attr;
(void)obj;
#else
// explicitly search for dynamic property
try {
Property* prop = getDocumentObjectPtr()->getDynamicPropertyByName(attr);
if (prop) {
if(prop->testStatus(Property::Immutable)) {
std::stringstream s;
s << "'DocumentObject' attribute '" << attr << "' is read-only";
throw Py::AttributeError(s.str());
}
prop->setPyObject(obj);
return 1;
}
@@ -700,7 +719,9 @@ int DocumentObjectPy::setCustomAttributes(const char* attr, PyObject *obj)
s << "Attribute (Name: " << attr << ") error: '" << exc.what() << "' ";
throw Py::AttributeError(s.str());
}
catch (...) {
catch (Py::AttributeError &) {
throw;
}catch (...) {
std::stringstream s;
s << "Unknown error in attribute " << attr;
throw Py::AttributeError(s.str());
@@ -710,8 +731,9 @@ int DocumentObjectPy::setCustomAttributes(const char* attr, PyObject *obj)
Property *prop = getDocumentObjectPtr()->getPropertyByName(attr);
if (prop) {
// Read-only attributes must not be set over its Python interface
short Type = getDocumentObjectPtr()->getPropertyType(prop);
if (Type & Prop_ReadOnly) {
if(prop->testStatus(Property::Immutable) ||
(getDocumentObjectPtr()->getPropertyType(prop) & Prop_ReadOnly))
{
std::stringstream s;
s << "'DocumentObject' attribute '" << attr << "' is read-only";
throw Py::AttributeError(s.str());
@@ -727,6 +749,7 @@ int DocumentObjectPy::setCustomAttributes(const char* attr, PyObject *obj)
}
return 1;
}
#endif
return 0;
}

View File

@@ -37,202 +37,117 @@
#include <Base/Exception.h>
#include <Base/Tools.h>
FC_LOG_LEVEL_INIT("DynamicProperty",true,true)
using namespace App;
DynamicProperty::DynamicProperty(PropertyContainer* p) : pc(p)
DynamicProperty::DynamicProperty()
{
}
DynamicProperty::~DynamicProperty()
{
clear();
}
void DynamicProperty::clear() {
auto &index = props.get<0>();
for(auto &v : index)
delete v.property;
index.clear();
}
void DynamicProperty::getPropertyList(std::vector<Property*> &List) const
{
// get the properties of the base class first and insert the dynamic properties afterwards
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
static_cast<App::ExtensionContainer*>(this->pc)->ExtensionContainer::getPropertyList(List);
else
this->pc->PropertyContainer::getPropertyList(List);
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it)
List.push_back(it->second.property);
for (auto &v : props.get<0>())
List.push_back(v.property);
}
void DynamicProperty::getPropertyMap(std::map<std::string,Property*> &Map) const
{
// get the properties of the base class first and insert the dynamic properties afterwards
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
static_cast<App::ExtensionContainer*>(this->pc)->ExtensionContainer::getPropertyMap(Map);
else
this->pc->PropertyContainer::getPropertyMap(Map);
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it)
Map[it->first] = it->second.property;
}
Property *DynamicProperty::getPropertyByName(const char* name) const
{
std::map<std::string,PropData>::const_iterator it = props.find(name);
if (it != props.end())
return it->second.property;
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
return static_cast<App::ExtensionContainer*>(this->pc)->ExtensionContainer::getPropertyByName(name);
return this->pc->PropertyContainer::getPropertyByName(name);
for (auto &v : props.get<0>())
Map[v.name] = v.property;
}
Property *DynamicProperty::getDynamicPropertyByName(const char* name) const
{
std::map<std::string,PropData>::const_iterator it = props.find(name);
if (it != props.end())
return it->second.property;
auto &index = props.get<0>();
auto it = index.find(name);
if (it != index.end())
return it->property;
return 0;
}
std::vector<std::string> DynamicProperty::getDynamicPropertyNames() const
{
std::vector<std::string> names;
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it) {
names.push_back(it->first);
}
auto &index = props.get<0>();
names.reserve(index.size());
for(auto &v : index)
names.push_back(v.name);
return names;
}
void DynamicProperty::addDynamicProperties(const PropertyContainer* cont)
{
std::vector<std::string> names = cont->getDynamicPropertyNames();
for (std::vector<std::string>::iterator it = names.begin(); it != names.end(); ++it) {
App::Property* prop = cont->getDynamicPropertyByName(it->c_str());
if (prop) {
addDynamicProperty(
prop->getTypeId().getName(),
prop->getName(),
prop->getGroup(),
prop->getDocumentation(),
prop->getType(),
cont->isReadOnly(prop),
cont->isHidden(prop));
}
}
}
const char* DynamicProperty::getPropertyName(const Property* prop) const
{
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it) {
if (it->second.property == prop)
return it->first.c_str();
}
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
return static_cast<App::ExtensionContainer*>(this->pc)->ExtensionContainer::getPropertyName(prop);
return this->pc->PropertyContainer::getPropertyName(prop);
}
unsigned int DynamicProperty::getMemSize (void) const
{
std::map<std::string,Property*> Map;
getPropertyMap(Map);
std::map<std::string,Property*>::const_iterator It;
unsigned int size = 0;
for (It = Map.begin(); It != Map.end();++It)
size += It->second->getMemSize();
return size;
}
short DynamicProperty::getPropertyType(const Property* prop) const
{
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it) {
if (it->second.property == prop) {
short attr = it->second.attr;
if (it->second.hidden)
attr |= Prop_Hidden;
if (it->second.readonly)
attr |= Prop_ReadOnly;
return attr;
}
}
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
return static_cast<App::ExtensionContainer*>(this->pc)->ExtensionContainer::getPropertyType(prop);
return this->pc->PropertyContainer::getPropertyType(prop);
return prop?prop->getType():0;
}
short DynamicProperty::getPropertyType(const char *name) const
{
std::map<std::string,PropData>::const_iterator it = props.find(name);
if (it != props.end()) {
short attr = it->second.attr;
if (it->second.hidden)
auto &index = props.get<0>();
auto it = index.find(name);
if (it != index.end()) {
short attr = it->attr;
if (it->hidden)
attr |= Prop_Hidden;
if (it->second.readonly)
if (it->readonly)
attr |= Prop_ReadOnly;
return attr;
}
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
return static_cast<App::ExtensionContainer*>(this->pc)->ExtensionContainer::getPropertyType(name);
return this->pc->PropertyContainer::getPropertyType(name);
return 0;
}
const char* DynamicProperty::getPropertyGroup(const Property* prop) const
{
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it) {
if (it->second.property == prop)
return it->second.group.c_str();
}
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
return static_cast<App::ExtensionContainer*>(this->pc)->ExtensionContainer::getPropertyGroup(prop);
return this->pc->PropertyContainer::getPropertyGroup(prop);
auto &index = props.get<1>();
auto it = index.find(const_cast<Property*>(prop));
if(it!=index.end())
return it->group.c_str();
return 0;
}
const char* DynamicProperty::getPropertyGroup(const char *name) const
{
std::map<std::string,PropData>::const_iterator it = props.find(name);
if (it != props.end())
return it->second.group.c_str();
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
return static_cast<App::ExtensionContainer*>(this->pc)->ExtensionContainer::getPropertyGroup(name);
return this->pc->PropertyContainer::getPropertyGroup(name);
auto &index = props.get<0>();
auto it = index.find(name);
if (it != index.end())
return it->group.c_str();
return 0;
}
const char* DynamicProperty::getPropertyDocumentation(const Property* prop) const
{
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it) {
if (it->second.property == prop)
return it->second.doc.c_str();
}
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
return static_cast<App::ExtensionContainer*>(this->pc)->ExtensionContainer::getPropertyDocumentation(prop);
return this->pc->PropertyContainer::getPropertyDocumentation(prop);
auto &index = props.get<1>();
auto it = index.find(const_cast<Property*>(prop));
if(it!=index.end())
return it->doc.c_str();
return 0;
}
const char* DynamicProperty::getPropertyDocumentation(const char *name) const
{
std::map<std::string,PropData>::const_iterator it = props.find(name);
if (it != props.end())
return it->second.doc.c_str();
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
return static_cast<App::ExtensionContainer*>(this->pc)->ExtensionContainer::getPropertyDocumentation(name);
return this->pc->PropertyContainer::getPropertyDocumentation(name);
auto &index = props.get<0>();
auto it = index.find(name);
if (it != index.end())
return it->doc.c_str();
return 0;
}
Property* DynamicProperty::addDynamicProperty(const char* type, const char* name, const char* group,
const char* doc, short attr, bool ro, bool hidden)
Property* DynamicProperty::addDynamicProperty(PropertyContainer &pc, const char* type,
const char* name, const char* group, const char* doc, short attr, bool ro, bool hidden)
{
Base::BaseClass* base = static_cast<Base::BaseClass*>(Base::Type::createInstanceByName(type,true));
if (!base)
@@ -246,49 +161,76 @@ Property* DynamicProperty::addDynamicProperty(const char* type, const char* name
// get unique name
Property* pcProperty = static_cast<Property*>(base);
std::string ObjectName;
if (name && *name != '\0')
ObjectName = getUniquePropertyName(name);
else
ObjectName = getUniquePropertyName(type);
if (!name || !name[0])
name = type;
pcProperty->setContainer(this->pc);
PropData data;
data.property = pcProperty;
data.group = (group ? group : "");
data.doc = (doc ? doc : "");
data.attr = attr;
data.readonly = ro;
data.hidden = hidden;
props[ObjectName] = data;
auto res = props.get<0>().emplace(pcProperty,
getUniquePropertyName(pc,name), nullptr, group, doc, attr, ro, hidden);
pcProperty->setContainer(&pc);
pcProperty->myName = res.first->name.c_str();
if(ro) attr |= Prop_ReadOnly;
if(hidden) attr |= Prop_Hidden;
pcProperty->syncType(attr);
pcProperty->StatusBits.set((size_t)Property::PropDynamic);
GetApplication().signalAppendDynamicProperty(*pcProperty);
return pcProperty;
}
bool DynamicProperty::addProperty(Property *prop)
{
if(!prop || !prop->getName())
return false;
auto &index = props.get<0>();
if(index.count(prop->getName()))
return false;
index.emplace(prop,std::string(),prop->getName(),
prop->getGroup(),prop->getDocumentation(),prop->getType(),false,false);
return true;
}
bool DynamicProperty::removeProperty(const Property *prop)
{
auto &index = props.get<1>();
auto it = index.find(const_cast<Property*>(prop));
if (it != index.end()) {
index.erase(it);
return true;
}
return false;
}
bool DynamicProperty::removeDynamicProperty(const char* name)
{
std::map<std::string,PropData>::iterator it = props.find(name);
if (it != props.end()) {
GetApplication().signalRemoveDynamicProperty(*it->second.property);
delete it->second.property;
props.erase(it);
auto &index = props.get<0>();
auto it = index.find(name);
if (it != index.end()) {
if(it->property->testStatus(Property::LockDynamic))
throw Base::RuntimeError("property is locked");
else if(!it->property->testStatus(Property::PropDynamic))
throw Base::RuntimeError("property is not dynamic");
Property *prop = it->property;
GetApplication().signalRemoveDynamicProperty(*prop);
delete prop;
index.erase(it);
return true;
}
return false;
}
std::string DynamicProperty::getUniquePropertyName(const char *Name) const
std::string DynamicProperty::getUniquePropertyName(PropertyContainer &pc, const char *Name) const
{
std::string CleanName = Base::Tools::getIdentifier(Name);
// name in use?
std::map<std::string,Property*> objectProps;
getPropertyMap(objectProps);
std::map<std::string,Property*>::const_iterator pos;
pos = objectProps.find(CleanName);
pc.getPropertyMap(objectProps);
auto pos = objectProps.find(CleanName);
if (pos == objectProps.end()) {
// if not, name is OK
@@ -304,151 +246,60 @@ std::string DynamicProperty::getUniquePropertyName(const char *Name) const
}
}
void DynamicProperty::Save (Base::Writer &writer) const
void DynamicProperty::save(const Property *prop, Base::Writer &writer) const
{
//extensions must be saved first, as they need to be read and initialised before properties (as
//they have their own properties which they need to handle on restore)
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
static_cast<App::ExtensionContainer*>(this->pc)->saveExtensions(writer);
std::map<std::string,Property*> Map;
getPropertyMap(Map);
writer.incInd(); // indentation for 'Properties Count'
writer.Stream() << writer.ind() << "<Properties Count=\"" << Map.size() << "\">" << std::endl;
std::map<std::string,Property*>::iterator it;
for (it = Map.begin(); it != Map.end(); ++it)
{
writer.incInd(); // indentation for 'Property name'
// check whether a static or dynamic property
std::map<std::string,PropData>::const_iterator pt = props.find(it->first);
if (pt == props.end()) {
writer.Stream() << writer.ind() << "<Property name=\"" << it->first << "\" type=\""
<< it->second->getTypeId().getName() << "\">" << std::endl;
}
else {
writer.Stream() << writer.ind() << "<Property name=\"" << it->first
<< "\" type=\"" << it->second->getTypeId().getName()
<< "\" group=\"" << encodeAttribute(pt->second.group)
<< "\" doc=\"" << encodeAttribute(pt->second.doc)
<< "\" attr=\"" << pt->second.attr << "\" ro=\"" << pt->second.readonly
<< "\" hide=\"" << pt->second.hidden << "\">" << std::endl;
}
writer.incInd(); // indentation for the actual property
try {
// We must make sure to handle all exceptions accordingly so that
// the project file doesn't get invalidated. In the error case this
// means to proceed instead of aborting the write operation.
// Don't write transient properties
if (!(getPropertyType(it->second) & Prop_Transient))
it->second->Save(writer);
}
catch (const Base::Exception &e) {
Base::Console().Error("%s\n", e.what());
}
catch (const std::exception &e) {
Base::Console().Error("%s\n", e.what());
}
catch (const char* e) {
Base::Console().Error("%s\n", e);
}
#ifndef FC_DEBUG
catch (...) {
Base::Console().Error("DynamicProperty::Save: Unknown C++ exception thrown. Try to continue...\n");
}
#endif
writer.decInd(); // indentation for the actual property
writer.Stream() << writer.ind() << "</Property>" << std::endl;
writer.decInd(); // indentation for 'Property name'
auto &index = props.get<1>();
auto it = index.find(const_cast<Property*>(prop));
if(it != index.end()) {
auto &data = *it;
writer.Stream() << "\" group=\"" << Base::Persistence::encodeAttribute(data.group)
<< "\" doc=\"" << Base::Persistence::encodeAttribute(data.doc)
<< "\" attr=\"" << data.attr << "\" ro=\"" << data.readonly
<< "\" hide=\"" << data.hidden;
}
writer.Stream() << writer.ind() << "</Properties>" << std::endl;
writer.decInd(); // indentation for 'Properties Count'
}
void DynamicProperty::Restore(Base::XMLReader &reader)
Property *DynamicProperty::restore(PropertyContainer &pc,
const char *PropName, const char *TypeName, Base::XMLReader &reader)
{
//first all extensions must be initialised so that they can handle their properties
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
static_cast<App::ExtensionContainer*>(this->pc)->restoreExtensions(reader);
if (!reader.hasAttribute("group"))
return 0;
reader.readElement("Properties");
int Cnt = reader.getAttributeAsInteger("Count");
for (int i=0 ;i<Cnt ;i++) {
reader.readElement("Property");
const char* PropName = reader.getAttribute("name");
const char* TypeName = reader.getAttribute("type");
Property* prop = getPropertyByName(PropName);
try {
if (!prop) {
short attribute = 0;
bool readonly = false, hidden = false;
const char *group=0, *doc=0, *attr=0, *ro=0, *hide=0;
if (reader.hasAttribute("group"))
group = reader.getAttribute("group");
if (reader.hasAttribute("doc"))
doc = reader.getAttribute("doc");
if (reader.hasAttribute("attr")) {
attr = reader.getAttribute("attr");
if (attr) attribute = attr[0]-48;
}
if (reader.hasAttribute("ro")) {
ro = reader.getAttribute("ro");
if (ro) readonly = (ro[0]-48) != 0;
}
if (reader.hasAttribute("hide")) {
hide = reader.getAttribute("hide");
if (hide) hidden = (hide[0]-48) != 0;
}
prop = addDynamicProperty(TypeName, PropName, group, doc, attribute, readonly, hidden);
}
}
catch(const Base::Exception& e) {
// only handle this exception type
Base::Console().Warning(e.what());
}
//NOTE: We must also check the type of the current property because a subclass of
//PropertyContainer might change the type of a property but not its name. In this
//case we would force to read-in a wrong property type and the behaviour would be
//undefined.
// Don't read transient properties
if (!(getPropertyType(prop) & Prop_Transient)) {
if (prop && strcmp(prop->getTypeId().getName(), TypeName) == 0) {
try {
prop->Restore(reader);
}
catch (const Base::XMLParseException&) {
throw; // re-throw
}
catch (const Base::Exception &e) {
Base::Console().Error("%s\n", e.what());
}
catch (const std::exception &e) {
Base::Console().Error("%s\n", e.what());
}
#ifndef FC_DEBUG
catch (...) {
Base::Console().Error("DynamicProperty::Restore: Unknown C++ exception thrown\n");
}
#endif
}
else if (prop) {
//Base::Console().Warning("%s: Overread data for property %s of type %s, expected type is %s\n",
// pc->getTypeId().getName(), prop->getName(), prop->getTypeId().getName(), TypeName);
pc->handleChangedPropertyType(reader, TypeName, prop);
}
else {
//Base::Console().Warning("%s: No property found with name %s and type %s\n",
// pc->getTypeId().getName(), PropName, TypeName);
pc->handleChangedPropertyName(reader, TypeName, PropName);
}
}
reader.readEndElement("Property");
short attribute = 0;
bool readonly = false, hidden = false;
const char *group=0, *doc=0, *attr=0, *ro=0, *hide=0;
group = reader.getAttribute("group");
if (reader.hasAttribute("doc"))
doc = reader.getAttribute("doc");
if (reader.hasAttribute("attr")) {
attr = reader.getAttribute("attr");
if (attr) attribute = attr[0]-48;
}
reader.readEndElement("Properties");
if (reader.hasAttribute("ro")) {
ro = reader.getAttribute("ro");
if (ro) readonly = (ro[0]-48) != 0;
}
if (reader.hasAttribute("hide")) {
hide = reader.getAttribute("hide");
if (hide) hidden = (hide[0]-48) != 0;
}
return addDynamicProperty(pc,TypeName, PropName, group, doc, attribute, readonly, hidden);
}
DynamicProperty::PropData DynamicProperty::getDynamicPropertyData(const Property *prop) const
{
auto &index = props.get<1>();
auto it = index.find(const_cast<Property*>(prop));
if(it != index.end())
return *it;
return PropData();
}
const char *DynamicProperty::getPropertyName(const Property *prop) const
{
auto &index = props.get<1>();
auto it = index.find(const_cast<Property*>(prop));
if(it != index.end())
return it->getName();
return 0;
}

View File

@@ -25,10 +25,18 @@
#define APP_DYNAMICPROPERTY_H
#include <Base/Persistence.h>
#include <unordered_map>
#include <map>
#include <vector>
#include <string>
#include <boost/functional/hash.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/mem_fun.hpp>
namespace Base {
class Writer;
class XMLWriter;
@@ -39,14 +47,28 @@ namespace App
class Property;
class PropertyContainer;
namespace bmi = boost::multi_index;
struct CStringHasher {
inline std::size_t operator()(const char *s) const {
if(!s) return 0;
return boost::hash_range(s,s+std::strlen(s));
}
inline bool operator()(const char *a, const char *b) const {
if(!a) return !b;
if(!b) return false;
return std::strcmp(a,b)==0;
}
};
/** This class implements an interface to add properties at run-time to an object
* derived from PropertyContainer. The additional properties are made persistent.
* @author Werner Mayer
*/
class AppExport DynamicProperty : public Base::Persistence
class AppExport DynamicProperty
{
public:
DynamicProperty(PropertyContainer* pc);
DynamicProperty();
virtual ~DynamicProperty();
/** @name Access properties */
@@ -55,8 +77,6 @@ public:
void getPropertyList(std::vector<Property*> &List) const;
/// Get all properties of the class (including parent)
void getPropertyMap(std::map<std::string,Property*> &Map) const;
/// Find a property by its name
Property *getPropertyByName(const char* name) const;
/// Find a dynamic property by its name
Property *getDynamicPropertyByName(const char* name) const;
/*!
@@ -76,20 +96,24 @@ public:
addDynamicProperty(..., ..., "Base","blah", Prop_None, true, true);
@endcode
*/
Property* addDynamicProperty(const char* type, const char* name=0, const char* group=0,
Property* addDynamicProperty(PropertyContainer &pc, const char* type, const char* name=0, const char* group=0,
const char* doc=0, short attr=0, bool ro=false, bool hidden=false);
/** Add a pre-existing property
*
* The property is not treated as dynamic, and will not trigger signal.
*
* @return Return false if there is a property exist with the same name.
*/
bool addProperty(Property *prop);
/*!
Removes a dynamic property by name. Returns true if the property is part of the container, otherwise
false is returned.
*/
bool removeDynamicProperty(const char* name);
/// Remove pre-existing property, which will not be deleted.
bool removeProperty(const Property *prop);
/// Get a list of all dynamic properties.
std::vector<std::string> getDynamicPropertyNames() const;
/*!
Get all dynamic properties of the given container and add these property types to this
instance of DynamicProperty.
*/
void addDynamicProperties(const PropertyContainer*);
/// Get the name of a property
const char* getPropertyName(const Property* prop) const;
//@}
@@ -110,28 +134,57 @@ public:
const char* getPropertyDocumentation(const char *name) const;
//@}
/** @name Property serialization */
//@{
void Save (Base::Writer &writer) const;
void Restore(Base::XMLReader &reader);
unsigned int getMemSize (void) const;
//@}
/// Remove all properties
void clear();
private:
std::string getUniquePropertyName(const char *Name) const;
/// Get property count
size_t size() const { return props.size(); }
void save(const Property *prop, Base::Writer &writer) const;
Property *restore(PropertyContainer &pc,
const char *PropName, const char *TypeName, Base::XMLReader &reader);
private:
struct PropData {
Property* property;
std::string name;
const char *pName;
std::string group;
std::string doc;
short attr;
bool readonly;
bool hidden;
PropData(Property *prop=0, std::string &&n=std::string(), const char *pn=0,
const char *g=0, const char *d=0, short a=0, bool ro=false, bool h=false)
:property(prop),name(std::move(n)),pName(pn)
,group(g?g:""),doc(d?d:""),attr(a),readonly(ro),hidden(h)
{}
const char *getName() const {
return pName?pName:name.c_str();
}
};
PropertyContainer* pc;
std::map<std::string,PropData> props;
PropData getDynamicPropertyData(const Property* prop) const;
private:
std::string getUniquePropertyName(PropertyContainer &pc, const char *Name) const;
private:
bmi::multi_index_container<
PropData,
bmi::indexed_by<
bmi::hashed_unique<
bmi::const_mem_fun<PropData, const char*, &PropData::getName>,
CStringHasher,
CStringHasher
>,
bmi::hashed_unique<
bmi::member<PropData, Property*, &PropData::property>
>
>
> props;
};
} // namespace App

View File

@@ -28,7 +28,6 @@
#include <Base/Writer.h>
#include <App/DocumentObject.h>
#include <App/DynamicProperty.h>
namespace App
{
@@ -50,10 +49,9 @@ class FeatureCustomT : public FeatureT
public:
FeatureCustomT() {
props = new DynamicProperty(this);
}
virtual ~FeatureCustomT() {
delete props;
}
/** @name methods override DocumentObject */
@@ -70,84 +68,6 @@ public:
return FeatureT::getViewProviderName();
}
/** @name Access properties */
//@{
Property* addDynamicProperty(
const char* type, const char* name=0,
const char* group=0, const char* doc=0,
short attr=0, bool ro=false, bool hidden=false) {
return props->addDynamicProperty(type, name, group, doc, attr, ro, hidden);
}
virtual bool removeDynamicProperty(const char* name) {
FeatureT::onAboutToRemoveProperty(name);
return props->removeDynamicProperty(name);
}
std::vector<std::string> getDynamicPropertyNames() const {
return props->getDynamicPropertyNames();
}
Property *getDynamicPropertyByName(const char* name) const {
return props->getDynamicPropertyByName(name);
}
virtual void addDynamicProperties(const PropertyContainer* cont) {
return props->addDynamicProperties(cont);
}
/// get all properties of the class (including properties of the parent)
virtual void getPropertyList(std::vector<Property*> &List) const {
props->getPropertyList(List);
}
/// get all properties of the class (including parent)
void getPropertyMap(std::map<std::string,Property*> &Map) const {
props->getPropertyMap(Map);
}
/// find a property by its name
virtual Property *getPropertyByName(const char* name) const {
return props->getPropertyByName(name);
}
/// get the name of a property
virtual const char* getPropertyName(const Property* prop) const {
return props->getPropertyName(prop);
}
//@}
/** @name Property attributes */
//@{
/// get the Type of a Property
short getPropertyType(const Property* prop) const {
return props->getPropertyType(prop);
}
/// get the Type of a named Property
short getPropertyType(const char *name) const {
return props->getPropertyType(name);
}
/// get the Group of a Property
const char* getPropertyGroup(const Property* prop) const {
return props->getPropertyGroup(prop);
}
/// get the Group of a named Property
const char* getPropertyGroup(const char *name) const {
return props->getPropertyGroup(name);
}
/// get the Documentation of a Property
const char* getPropertyDocumentation(const Property* prop) const {
return props->getPropertyDocumentation(prop);
}
/// get the Group of a named Property
const char* getPropertyDocumentation(const char *name) const {
return props->getPropertyDocumentation(name);
}
//@}
/** @name Property serialization */
//@{
void Save (Base::Writer &writer) const {
writer.ObjectName = this->getNameInDocument();
props->Save(writer);
}
void Restore(Base::XMLReader &reader) {
props->Restore(reader);
}
//@}
PyObject *getPyObject(void) {
return FeatureT::getPyObject();
}
@@ -168,9 +88,6 @@ protected:
virtual void onSettingDocument() {
FeatureT::onSettingDocument();
}
private:
DynamicProperty* props;
};
} //namespace App

View File

@@ -70,11 +70,9 @@ public:
ADD_PROPERTY(Proxy,(Py::Object()));
// cannot move this to the initializer list to avoid warning
imp = new FeaturePythonImp(this);
props = new DynamicProperty(this);
}
virtual ~FeaturePythonT() {
delete imp;
delete props;
}
/** @name methods override DocumentObject */
@@ -102,88 +100,6 @@ public:
//return "Gui::ViewProviderPythonFeature";
}
/** @name Access properties */
//@{
Property* addDynamicProperty(
const char* type, const char* name=0,
const char* group=0, const char* doc=0,
short attr=0, bool ro=false, bool hidden=false) {
return props->addDynamicProperty(type, name, group, doc, attr, ro, hidden);
}
virtual bool removeDynamicProperty(const char* name) {
FeatureT::onAboutToRemoveProperty(name);
return props->removeDynamicProperty(name);
}
std::vector<std::string> getDynamicPropertyNames() const {
return props->getDynamicPropertyNames();
}
Property *getDynamicPropertyByName(const char* name) const {
return props->getDynamicPropertyByName(name);
}
virtual void addDynamicProperties(const PropertyContainer* cont) {
return props->addDynamicProperties(cont);
}
/// get all properties of the class (including properties of the parent)
virtual void getPropertyList(std::vector<Property*> &List) const {
props->getPropertyList(List);
}
/// get all properties of the class (including parent)
void getPropertyMap(std::map<std::string,Property*> &Map) const {
props->getPropertyMap(Map);
}
/// find a property by its name
virtual Property *getPropertyByName(const char* name) const {
return props->getPropertyByName(name);
}
/// get the name of a property
virtual const char* getPropertyName(const Property* prop) const {
return props->getPropertyName(prop);
}
//@}
/** @name Property attributes */
//@{
/// get the Type of a Property
short getPropertyType(const Property* prop) const {
return props->getPropertyType(prop);
}
/// get the Type of a named Property
short getPropertyType(const char *name) const {
return props->getPropertyType(name);
}
/// get the Group of a Property
const char* getPropertyGroup(const Property* prop) const {
return props->getPropertyGroup(prop);
}
/// get the Group of a named Property
const char* getPropertyGroup(const char *name) const {
return props->getPropertyGroup(name);
}
/// get the Documentation of a Property
const char* getPropertyDocumentation(const Property* prop) const {
return props->getPropertyDocumentation(prop);
}
/// get the Group of a named Property
const char* getPropertyDocumentation(const char *name) const {
return props->getPropertyDocumentation(name);
}
//@}
/** @name Property serialization */
//@{
void Save (Base::Writer &writer) const {
const char* objname = this->getNameInDocument();
// if null then it's not part of the document
if (objname) {
writer.ObjectName = objname;
props->Save(writer);
}
}
void Restore(Base::XMLReader &reader) {
props->Restore(reader);
}
//@}
PyObject *getPyObject(void) {
if (FeatureT::PythonObject.is(Py::_None())) {
// ref counter is set to 1

View File

@@ -73,6 +73,8 @@
#include <bitset>
#include <exception>
#include <random>
#include <unordered_set>
#include <unordered_map>
// Boost
#include <boost/signals2.hpp>

View File

@@ -33,6 +33,7 @@
#include "PropertyContainer.h"
#include <Base/Exception.h>
#include "Application.h"
#include "DocumentObject.h"
using namespace App;
@@ -49,7 +50,7 @@ TYPESYSTEM_SOURCE_ABSTRACT(App::Property , Base::Persistence);
// Here is the implementation! Description should take place in the header file!
Property::Property()
:father(0)
:father(0), myName(0)
{
}
@@ -61,12 +62,45 @@ Property::~Property()
const char* Property::getName(void) const
{
return father->getPropertyName(this);
return myName;
}
std::string Property::getFullName() const {
std::string name;
if(myName) {
if(father)
name = father->getFullName() + ".";
name += myName;
}else
return "?";
return name;
}
short Property::getType(void) const
{
return father->getPropertyType(this);
short type = 0;
#define GET_PTYPE(_name) do {\
if(testStatus(App::Property::Prop##_name)) type|=Prop_##_name;\
}while(0)
GET_PTYPE(ReadOnly);
GET_PTYPE(Hidden);
GET_PTYPE(Output);
GET_PTYPE(Transient);
GET_PTYPE(NoRecompute);
GET_PTYPE(NoPersist);
return type;
}
void Property::syncType(unsigned type) {
#define SYNC_PTYPE(_name) do{\
if(type & Prop_##_name) StatusBits.set((size_t)Prop##_name);\
}while(0)
SYNC_PTYPE(ReadOnly);
SYNC_PTYPE(Transient);
SYNC_PTYPE(Hidden);
SYNC_PTYPE(Output);
SYNC_PTYPE(NoRecompute);
SYNC_PTYPE(NoPersist);
}
const char* Property::getGroup(void) const
@@ -99,7 +133,7 @@ void Property::getPaths(std::vector<ObjectIdentifier> &paths) const
paths.push_back(App::ObjectIdentifier(getContainer(), getName()));
}
const ObjectIdentifier Property::canonicalPath(const ObjectIdentifier &p) const
ObjectIdentifier Property::canonicalPath(const ObjectIdentifier &p) const
{
return p;
}
@@ -113,10 +147,7 @@ void Property::touch()
void Property::setReadOnly(bool readOnly)
{
unsigned long status = this->getStatus();
this->setStatus(App::Property::ReadOnly, readOnly);
if (status != this->getStatus())
App::GetApplication().signalChangePropertyEditor(*this);
}
void Property::hasSetValue(void)
@@ -134,12 +165,7 @@ void Property::aboutToSetValue(void)
void Property::verifyPath(const ObjectIdentifier &p) const
{
if (p.numSubComponents() != 1)
throw Base::ValueError("Invalid property path: single component expected");
if (!p.getPropertyComponent(0).isSimple())
throw Base::ValueError("Invalid property path: simple component expected");
if (p.getPropertyComponent(0).getName() != getName())
throw Base::ValueError("Invalid property path: name mismatch");
p.verify(*this);
}
Property *Property::Copy(void) const
@@ -155,6 +181,76 @@ void Property::Paste(const Property& /*from*/)
assert(0);
}
void Property::setStatusValue(unsigned long status) {
static const unsigned long mask =
(1<<PropDynamic)
|(1<<PropNoRecompute)
|(1<<PropReadOnly)
|(1<<PropTransient)
|(1<<PropOutput)
|(1<<PropHidden);
status &= ~mask;
status |= StatusBits.to_ulong() & mask;
unsigned long oldStatus = StatusBits.to_ulong();
StatusBits = decltype(StatusBits)(status);
if(father) {
static unsigned long _signalMask = (1<<ReadOnly) | (1<<Hidden);
if((status & _signalMask) != (oldStatus & _signalMask))
father->onPropertyStatusChanged(*this,oldStatus);
}
}
void Property::setStatus(Status pos, bool on) {
auto bits = StatusBits;
bits.set(pos,on);
setStatusValue(bits.to_ulong());
}
//**************************************************************************
//**************************************************************************
// PropertyListsBase
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void PropertyListsBase::_setPyObject(PyObject *value) {
std::vector<PyObject *> vals;
std::vector<int> indices;
if (PyDict_Check(value)) {
PyObject* keyList = PyDict_Keys(value);
PyObject* itemList = PyDict_Values(value);
Py_ssize_t nSize = PyList_Size(keyList);
vals.reserve(nSize);
indices.reserve(nSize);
int listSize = getSize();
for (Py_ssize_t i=0; i<nSize;++i) {
std::string keyStr;
PyObject* key = PyList_GetItem(keyList, i);
#if PY_MAJOR_VERSION < 3
if(!PyInt_Check(key))
#else
if(!PyLong_Check(key))
#endif
throw Base::TypeError("expect key type to be interger");
auto idx = PyLong_AsLong(key);
if(idx<-1 || idx>listSize)
throw Base::RuntimeError("index out of bound");
if(idx==-1 || idx==listSize) {
idx = listSize;
++listSize;
}
indices.push_back(idx);
vals.push_back(PyList_GetItem(itemList,i));
}
}else if (PySequence_Check(value)) {
Py_ssize_t nSize = PySequence_Size(value);
vals.reserve(nSize);
for (Py_ssize_t i=0; i<nSize;++i)
vals.push_back(PySequence_GetItem(value, i));
}else
vals.push_back(value);
setPyValues(vals,indices);
}
//**************************************************************************
//**************************************************************************
// PropertyLists

View File

@@ -62,8 +62,35 @@ public:
Immutable = 1, // can't modify property
ReadOnly = 2, // for property editor
Hidden = 3, // for property editor
Single = 4, // for save/load of floating point numbers
Ordered = 5, // for PropertyLists whether the order of the elements is relevant for the container using it
Transient = 4, // for property container save
MaterialEdit = 5, // to turn ON PropertyMaterial edit
NoMaterialListEdit = 6, // to turn OFF PropertyMaterialList edit
Output = 7, // same effect as Prop_Output
LockDynamic = 8, // prevent being removed from dynamic property
NoModify = 9, // prevent causing Gui::Document::setModified()
PartialTrigger = 10, // allow change in partial doc
NoRecompute = 11, // touch owner for recompute on property change
Single = 12, // for save/load of floating point numbers
Ordered = 13, // for PropertyLists whether the order of the elements is
// relevant for the container using it
EvalOnRestore = 14, // In case of expression binding, evaluate the
// expression on restore and touch the object on value change.
// The following bits are corresponding to PropertyType set when the
// property added. These types are meant to be static, and cannot be
// changed in runtime. It is mirrored here to save the linear search
// required in PropertyContainer::getPropertyType()
//
PropStaticBegin = 21,
PropDynamic = 21, // indicating the property is dynamically added
PropNoPersist = 22, // corresponding to Prop_NoPersist
PropNoRecompute = 23, // corresponding to Prop_NoRecompute
PropReadOnly = 24, // corresponding to Prop_ReadOnly
PropTransient= 25, // corresponding to Prop_Transient
PropHidden = 26, // corresponding to Prop_Hidden
PropOutput = 27, // corresponding to Prop_Output
PropStaticEnd = 28,
User1 = 28, // user-defined status
User2 = 29, // user-defined status
User3 = 30, // user-defined status
@@ -87,6 +114,8 @@ public:
/// get the name of this property in the belonging container
const char* getName(void) const;
std::string getFullName() const;
/// Get the class name of the associated property editor item
virtual const char* getEditorName(void) const { return ""; }
@@ -112,7 +141,7 @@ public:
virtual const boost::any getPathValue(const App::ObjectIdentifier & path) const;
/// Convert p to a canonical representation of it
virtual const App::ObjectIdentifier canonicalPath(const App::ObjectIdentifier & p) const;
virtual App::ObjectIdentifier canonicalPath(const App::ObjectIdentifier & p) const;
/// Get valid paths for this property; used by auto completer
virtual void getPaths(std::vector<App::ObjectIdentifier> & paths) const;
@@ -137,9 +166,8 @@ public:
inline bool testStatus(Status pos) const {
return StatusBits.test(static_cast<size_t>(pos));
}
inline void setStatus(Status pos, bool on) {
StatusBits.set(static_cast<size_t>(pos), on);
}
void setStatus(Status pos, bool on);
void setStatusValue(unsigned long status);
///Sets property editable/grayed out in property editor
void setReadOnly(bool readOnly);
inline bool isReadOnly() const {
@@ -161,8 +189,9 @@ public:
/// Paste the value from the property (mainly for Undo/Redo and transactions)
virtual void Paste(const Property &from) = 0;
friend class PropertyContainer;
friend struct PropertyData;
friend class DynamicProperty;
protected:
/** Status bits of the property
@@ -178,9 +207,9 @@ protected:
protected:
/// Gets called by all setValue() methods after the value has changed
void hasSetValue(void);
virtual void hasSetValue(void);
/// Gets called by all setValue() methods before the value has changed
void aboutToSetValue(void);
virtual void aboutToSetValue(void);
/// Verify a path for the current property
virtual void verifyPath(const App::ObjectIdentifier & p) const;
@@ -190,37 +219,19 @@ private:
Property(const Property&);
Property& operator = (const Property&);
// Sync status with Property_Type
void syncType(unsigned type);
private:
PropertyContainer *father;
const char *myName;
};
/** Base class of all property lists.
* The PropertyLists class is the base class for properties which can contain
* multiple values, not only a single value.
* All property types which may contain more than one value inherits this class.
*/
class AppExport PropertyLists : public Property
{
TYPESYSTEM_HEADER();
public:
PropertyLists() {};
virtual void setSize(int newSize)=0;
virtual int getSize(void) const =0;
// if the order of the elements in the list relevant?
// if yes, certain operations, like restoring must make sure that the
// order is kept despite errors.
inline void setOrderRelevant(bool on) { this->setStatus(Status::Ordered,on); };
inline bool isOrderRelevant() const { return this->testStatus(Status::Ordered);}
};
/** A template class that is used to inhibit multiple nested calls to aboutToSetValue/hasSetValue for properties.
*
* A template class that is used to inhibit multiple nested calls to
* aboutToSetValue/hasSetValue for properties, and only invoke it the first and
* aboutToSetValue/hasSetValue for properties, and only invoke it on change and
* last time it is needed. This is useful in cases where you want to change multiple
* values in a property "atomically", using possibly multiple primitive functions
* that normally would trigger aboutToSetValue/hasSetValue calls on their own.
@@ -228,48 +239,257 @@ public:
* To use, inherit privately from the AtomicPropertyChangeInterface class, using
* your class name as the template argument. In all cases where you normally would
* call aboutToSetValue/hasSetValue before and after a change, create an
* AtomicPropertyChange object before you do the change. Depending on a counter
* in the main property, the constructor might invoke aboutToSetValue. When the
* AtomicPropertyChange object is destructed, it might call hasSetValue if it is
* found necessary to do (i.e last item on the AtomicPropertyChange stack).
* This makes it easy to match the calls, and it is also exception safe in the
* sense that the destructors are guaranteed to be called during unwinding and
* exception handling, making the calls to boutToSetValue and hasSetValue balanced.
* AtomicPropertyChange object. The default constructor assume you are about to
* change the property and will call property's aboutToSetValue() if the
* property has not been marked as changed before by any other
* AtomicPropertyChange instances in current call stack. You can pass 'false'
* as the a second argument to the constructor, and manually call
* AtomicPropertyChange::aboutToChange() before actual change, this enables you
* to prevent unnecessary property copy for undo/redo where there is actual
* changes. AtomicPropertyChange will guaranetee calling hasSetValue() when the
* last instance in the current call stack is destroied.
*
* One thing to take note is that, because C++ does not allow throwing
* exception in destructor, any exception thrown when calling property's
* hasSetValue() will be caught and swallowed. To allow exception propagation,
* you can manually call AtomicPropertyChange::tryInvoke(). If the condition is
* satisfied, it will call hasSetValue() that allows exception propagation.
*/
template<class P> class AtomicPropertyChangeInterface {
protected:
AtomicPropertyChangeInterface() : signalCounter(0) { }
AtomicPropertyChangeInterface() : signalCounter(0), hasChanged(false) { }
public:
class AtomicPropertyChange {
public:
AtomicPropertyChange(P & prop) : mProp(prop) {
// Signal counter == 0? Then we need to invoke the aboutToSetValue in the property.
if (mProp.signalCounter == 0)
mProp.aboutToSetValue();
/** Constructor
*
* @param prop: the property
* @param markChange: If true, marks the property as changed if it
* hasn't been marked before, and calls its
* aboutToSetValue().
*/
AtomicPropertyChange(P & prop, bool markChange=true) : mProp(prop) {
mProp.signalCounter++;
if (markChange)
aboutToChange();
}
~AtomicPropertyChange() {
mProp.signalCounter--;
/** Mark the property as changed
*
* It will mark the property as changed only if it has been marked
* before, and only then will it call the property's aboutToSetValue().
*/
void aboutToChange() {
if(!mProp.hasChanged) {
mProp.hasChanged = true;
mProp.aboutToSetValue();
}
}
// Signal counter == 0? Then we need to invoke the hasSetValue in the property.
if (mProp.signalCounter == 0)
mProp.hasSetValue();
/** Destructor
*
* If the property is marked as changed, and this is the last instance
* of the class in current call stack, it will call property's
* hasSetValue()
*/
~AtomicPropertyChange() {
// Signal counter == 1? meaning we are the last one. Invoke
// hasSetValue() before decrease counter to prevent recursive call
// triggered by another AtomicPropertyChange created inside
// hasSetValue(), as it has now been changed to a virtual function.
if (mProp.signalCounter == 1 && mProp.hasChanged) {
// Must make sure to not throw in a destructor
try {
mProp.hasSetValue();
}catch(Base::Exception &e) {
e.ReportException();
}catch(...) {}
mProp.hasChanged = false;
}
if(mProp.signalCounter>0)
mProp.signalCounter--;
}
/** Check and invoke property's hasSetValue()
*
* Check if this is the last instance and the proeprty has been marked
* as changed. If so, invoke property's hasSetValue().
*/
// Destructor cannot throw. So we provide this function to allow error
// propagation.
void tryInvoke() {
if(mProp.signalCounter==1 && mProp.hasChanged) {
mProp.hasSetValue();
if(mProp.signalCounter>0)
--mProp.signalCounter;
mProp.hasChanged = false;
}
}
private:
P & mProp; /**< Referenced to property we work on */
};
static AtomicPropertyChange * getAtomicPropertyChange(P & prop) { return new AtomicPropertyChange(prop); }
private:
int signalCounter; /**< Counter for invoking transaction start/stop */
bool hasChanged;
};
/** Helper class to construct list like properties
*
* This class is not derived from Property so that we can have more that one
* base class for list like properties, e.g. see PropertyList, and
* PropertyLinkListBase
*/
class AppExport PropertyListsBase
{
public:
virtual void setSize(int newSize)=0;
virtual int getSize(void) const =0;
const std::set<int> &getTouchList() const {
return _touchList;
}
void clearTouchList() {
_touchList.clear();
}
protected:
virtual void setPyValues(const std::vector<PyObject*> &vals, const std::vector<int> &indices) {
(void)vals;
(void)indices;
throw Base::NotImplementedError("not implemented");
}
void _setPyObject(PyObject *);
protected:
std::set<int> _touchList;
};
/** Base class of all property lists.
* The PropertyLists class is the base class for properties which can contain
* multiple values, not only a single value.
* All property types which may contain more than one value inherits this class.
*/
class AppExport PropertyLists : public Property, public PropertyListsBase
{
TYPESYSTEM_HEADER();
public:
virtual void setPyObject(PyObject *obj) override {
_setPyObject(obj);
}
// if the order of the elements in the list relevant?
// if yes, certain operations, like restoring must make sure that the
// order is kept despite errors.
inline void setOrderRelevant(bool on) { this->setStatus(Status::Ordered,on); };
inline bool isOrderRelevant() const { return this->testStatus(Status::Ordered);}
};
/** Helper class to implement PropertyLists */
template<class T, class ListT = std::vector<T>, class ParentT = PropertyLists >
class PropertyListsT: public ParentT
, public AtomicPropertyChangeInterface<PropertyListsT<T,ListT,ParentT> >
{
public:
typedef typename ListT::const_reference const_reference;
typedef ListT list_type;
typedef ParentT parent_type;
typedef typename AtomicPropertyChangeInterface<
PropertyListsT<T,ListT,ParentT> >::AtomicPropertyChange atomic_change;
friend atomic_change;
virtual void setSize(int newSize, const_reference def) {
_lValueList.resize(newSize,def);
}
virtual void setSize(int newSize) override {
_lValueList.resize(newSize);
}
virtual int getSize(void) const override {
return static_cast<int>(_lValueList.size());
}
void setValue(const_reference value) {
ListT vals;
vals.resize(1,value);
setValues(vals);
}
virtual void setValues(const ListT &newValues = ListT()) {
atomic_change guard(*this);
this->_touchList.clear();
this->_lValueList = newValues;
guard.tryInvoke();
}
void setValue(const ListT &newValues = ListT()) {
setValues(newValues);
}
const ListT &getValues(void) const{return _lValueList;}
// alias to getValues
const ListT &getValue(void) const{return getValues();}
const_reference operator[] (int idx) const {return _lValueList[idx];}
virtual void setPyObject(PyObject *value) override {
try {
setValue(getPyValue(value));
return;
}catch(...){}
parent_type::setPyObject(value);
}
virtual void set1Value(int index, const_reference value) {
int size = getSize();
if(index<-1 || index>size)
throw Base::RuntimeError("index out of bound");
atomic_change guard(*this);
if(index==-1 || index == size) {
index = size;
setSize(index+1,value);
}else
_lValueList[index] = value;
this->_touchList.insert(index);
guard.tryInvoke();
}
protected:
void setPyValues(const std::vector<PyObject*> &vals, const std::vector<int> &indices) override
{
ListT values;
// old version boost::dynamic_bitset don't have reserve(). What a shame!
// values.reserve(vals.size());
for(auto item : vals)
values.push_back(getPyValue(item));
if(indices.empty())
setValues(values);
else {
atomic_change guard(*this);
assert(values.size()==indices.size());
for(int i=0,count=values.size();i<count;++i)
set1Value(indices[i],values[i]);
guard.tryInvoke();
}
}
virtual T getPyValue(PyObject *item) const = 0;
protected:
ListT _lValueList;
};
} // namespace App

View File

@@ -35,10 +35,13 @@
#include <Base/Console.h>
#include <Base/Exception.h>
#include "Application.h"
#include "Property.h"
#include "PropertyContainer.h"
#include "PropertyLinks.h"
FC_LOG_LEVEL_INIT("App",true,true)
using namespace App;
using namespace Base;
using namespace std;
@@ -71,18 +74,30 @@ unsigned int PropertyContainer::getMemSize (void) const
return size;
}
App::Property* PropertyContainer::addDynamicProperty(
const char* type, const char* name, const char* group, const char* doc,
short attr, bool ro, bool hidden)
{
return dynamicProps.addDynamicProperty(*this,type,name,group,doc,attr,ro,hidden);
}
Property *PropertyContainer::getPropertyByName(const char* name) const
{
auto prop = dynamicProps.getDynamicPropertyByName(name);
if(prop) return prop;
return getPropertyData().getPropertyByName(this,name);
}
void PropertyContainer::getPropertyMap(std::map<std::string,Property*> &Map) const
{
dynamicProps.getPropertyMap(Map);
getPropertyData().getPropertyMap(this,Map);
}
void PropertyContainer::getPropertyList(std::vector<Property*> &List) const
{
dynamicProps.getPropertyList(List);
getPropertyData().getPropertyList(this,List);
}
@@ -96,31 +111,39 @@ void PropertyContainer::setPropertyStatus(unsigned char bit,bool value)
short PropertyContainer::getPropertyType(const Property* prop) const
{
return getPropertyData().getType(this,prop);
return prop?prop->getType():0;
}
short PropertyContainer::getPropertyType(const char *name) const
{
return getPropertyData().getType(this,name);
return getPropertyType(getPropertyByName(name));
}
const char* PropertyContainer::getPropertyGroup(const Property* prop) const
{
auto group = dynamicProps.getPropertyGroup(prop);
if(group) return group;
return getPropertyData().getGroup(this,prop);
}
const char* PropertyContainer::getPropertyGroup(const char *name) const
{
auto group = dynamicProps.getPropertyGroup(name);
if(group) return group;
return getPropertyData().getGroup(this,name);
}
const char* PropertyContainer::getPropertyDocumentation(const Property* prop) const
{
auto doc = dynamicProps.getPropertyDocumentation(prop);
if(doc) return doc;
return getPropertyData().getDocumentation(this,prop);
}
const char* PropertyContainer::getPropertyDocumentation(const char *name) const
{
auto doc = dynamicProps.getPropertyDocumentation(name);
if(doc) return doc;
return getPropertyData().getDocumentation(this,name);
}
@@ -146,10 +169,12 @@ bool PropertyContainer::isHidden(const char *name) const
const char* PropertyContainer::getPropertyName(const Property* prop)const
{
return getPropertyData().getName(this,prop);
auto res = dynamicProps.getPropertyName(prop);
if(!res)
res = getPropertyData().getName(this,prop);
return res;
}
const PropertyData * PropertyContainer::getPropertyDataPtr(void){return &propertyData;}
const PropertyData & PropertyContainer::getPropertyData(void) const{return propertyData;}
@@ -193,66 +218,82 @@ void PropertyContainer::handleChangedPropertyType(XMLReader &reader, const char
PropertyData PropertyContainer::propertyData;
/**
* Binary function to query the flags for use with generic STL functions.
*/
template <class TCLASS>
class PropertyAttribute : public std::binary_function<TCLASS, typename App::PropertyType, bool>
{
public:
PropertyAttribute(const PropertyContainer* c) : cont(c) {}
bool operator () (const TCLASS& prop, typename App::PropertyType attr) const
{ return (cont->getPropertyType(prop.second) & attr) == attr; }
private:
const PropertyContainer* cont;
};
void PropertyContainer::Save (Base::Writer &writer) const
void PropertyContainer::Save (Base::Writer &writer) const
{
std::map<std::string,Property*> Map;
getPropertyMap(Map);
// ignore the properties we won't store
size_t ct = std::count_if(Map.begin(), Map.end(), std::bind2nd(PropertyAttribute
<std::pair<std::string,Property*> >(this), Prop_Transient));
size_t size = Map.size() - ct;
std::vector<Property*> transients;
for(auto it=Map.begin();it!=Map.end();) {
auto prop = it->second;
if(prop->testStatus(Property::PropNoPersist)) {
it = Map.erase(it);
continue;
}
if(!prop->testStatus(Property::PropDynamic)
&& (prop->testStatus(Property::Transient) ||
getPropertyType(prop) & Prop_Transient))
{
transients.push_back(prop);
it = Map.erase(it);
}else
++it;
}
writer.incInd(); // indentation for 'Properties Count'
writer.Stream() << writer.ind() << "<Properties Count=\"" << size << "\">" << endl;
std::map<std::string,Property*>::iterator it;
for (it = Map.begin(); it != Map.end(); ++it)
writer.Stream() << writer.ind() << "<Properties Count=\"" << Map.size()
<< "\" TransientCount=\"" << transients.size() << "\">" << endl;
// First store transient properties to persisit their status value. We use
// a new element named "_Property" so that the save file can be opened by
// older version FC.
writer.incInd();
for(auto prop : transients) {
writer.Stream() << writer.ind() << "<_Property name=\"" << prop->getName()
<< "\" type=\"" << prop->getTypeId().getName()
<< "\" status=\"" << prop->getStatus() << "\"/>" << std::endl;
}
writer.decInd();
// Now store normal properties
for (auto it = Map.begin(); it != Map.end(); ++it)
{
// Don't write transient properties
if (!(getPropertyType(it->second) & Prop_Transient))
{
writer.incInd(); // indentation for 'Property name'
writer.Stream() << writer.ind() << "<Property name=\"" << it->first << "\" type=\""
<< it->second->getTypeId().getName() << "\">" << endl;;
writer.incInd(); // indentation for the actual property
try {
// We must make sure to handle all exceptions accordingly so that
// the project file doesn't get invalidated. In the error case this
// means to proceed instead of aborting the write operation.
it->second->Save(writer);
}
catch (const Base::Exception &e) {
Base::Console().Error("%s\n", e.what());
}
catch (const std::exception &e) {
Base::Console().Error("%s\n", e.what());
}
catch (const char* e) {
Base::Console().Error("%s\n", e);
}
#ifndef FC_DEBUG
catch (...) {
Base::Console().Error("PropertyContainer::Save: Unknown C++ exception thrown. Try to continue...\n");
}
#endif
writer.decInd(); // indentation for the actual property
writer.Stream() << writer.ind() << "</Property>" << endl;
writer.decInd(); // indentation for 'Property name'
writer.incInd(); // indentation for 'Property name'
writer.Stream() << writer.ind() << "<Property name=\"" << it->first << "\" type=\""
<< it->second->getTypeId().getName();
dynamicProps.save(it->second,writer);
auto status = it->second->getStatus();
if(status)
writer.Stream() << "\" status=\"" << status;
writer.Stream() << "\">" << std::endl;
writer.incInd(); // indentation for the actual property
try {
// We must make sure to handle all exceptions accordingly so that
// the project file doesn't get invalidated. In the error case this
// means to proceed instead of aborting the write operation.
it->second->Save(writer);
}
catch (const Base::Exception &e) {
Base::Console().Error("%s\n", e.what());
}
catch (const std::exception &e) {
Base::Console().Error("%s\n", e.what());
}
catch (const char* e) {
Base::Console().Error("%s\n", e);
}
#ifndef FC_DEBUG
catch (...) {
Base::Console().Error("PropertyContainer::Save: Unknown C++ exception thrown. Try to continue...\n");
}
#endif
writer.decInd(); // indentation for the actual property
writer.Stream() << writer.ind() << "</Property>" << endl;
writer.decInd(); // indentation for 'Property name'
}
writer.Stream() << writer.ind() << "</Properties>" << endl;
writer.decInd(); // indentation for 'Properties Count'
@@ -264,11 +305,33 @@ void PropertyContainer::Restore(Base::XMLReader &reader)
reader.readElement("Properties");
int Cnt = reader.getAttributeAsInteger("Count");
int transientCount = 0;
if(reader.hasAttribute("TransientCount"))
transientCount = reader.getAttributeAsUnsigned("TransientCount");
for (int i=0;i<transientCount; ++i) {
reader.readElement("_Property");
Property* prop = getPropertyByName(reader.getAttribute("name"));
if(prop)
FC_TRACE("restore transient '" << prop->getName() << "'");
if(prop && reader.hasAttribute("status"))
prop->setStatusValue(reader.getAttributeAsUnsigned("status"));
}
for (int i=0 ;i<Cnt ;i++) {
reader.readElement("Property");
std::string PropName = reader.getAttribute("name");
std::string TypeName = reader.getAttribute("type");
Property* prop = getPropertyByName(PropName.c_str());
auto prop = dynamicProps.restore(*this,PropName.c_str(),TypeName.c_str(),reader);
if(!prop)
prop = getPropertyByName(PropName.c_str());
decltype(Property::StatusBits) status;
if(reader.hasAttribute("status")) {
status = decltype(status)(reader.getAttributeAsUnsigned("status"));
if(prop)
prop->setStatusValue(status.to_ulong());
}
// NOTE: We must also check the type of the current property because a
// subclass of PropertyContainer might change the type of a property but
// not its name. In this case we would force to read-in a wrong property
@@ -276,7 +339,15 @@ void PropertyContainer::Restore(Base::XMLReader &reader)
try {
// name and type match
if (prop && strcmp(prop->getTypeId().getName(), TypeName.c_str()) == 0) {
prop->Restore(reader);
if (!prop->testStatus(Property::Transient)
&& !status.test(Property::Transient)
&& !status.test(Property::PropTransient)
&& !(getPropertyType(prop) & Prop_Transient))
{
FC_TRACE("restore proeprty '" << prop->getName() << "'");
prop->Restore(reader);
}else
FC_TRACE("skip transient '" << prop->getName() << "'");
}
// name matches but not the type
else if (prop) {
@@ -315,58 +386,98 @@ void PropertyContainer::Restore(Base::XMLReader &reader)
Base::Console().Error("PropertyContainer::Restore: Unknown C++ exception thrown\n");
}
#endif
reader.readEndElement("Property");
}
reader.readEndElement("Properties");
}
void PropertyContainer::onPropertyStatusChanged(const Property &prop, unsigned long oldStatus)
{
(void)prop;
(void)oldStatus;
}
void PropertyData::addProperty(OffsetBase offsetBase,const char* PropName, Property *Prop, const char* PropertyGroup , PropertyType Type, const char* PropertyDocu)
{
bool IsIn = false;
for (vector<PropertySpec>::const_iterator It = propertyData.begin(); It != propertyData.end(); ++It)
if(strcmp(It->Name,PropName)==0)
IsIn = true;
#ifdef FC_DEBUG
if(!parentMerged)
#endif
{
short offset = offsetBase.getOffsetTo(Prop);
if(offset < 0)
throw Base::RuntimeError("Invalid static property");
auto &index = propertyData.get<1>();
auto it = index.find(PropName);
if(it == index.end()) {
if(parentMerged)
throw Base::RuntimeError("Cannot add static property");
index.emplace(PropName, PropertyGroup, PropertyDocu, offset, Type);
} else{
#ifdef FC_DEBUG
if(it->Offset != offset) {
FC_ERR("Duplicate property '" << PropName << "'");
}
#endif
}
}
if( !IsIn )
{
PropertySpec temp;
temp.Name = PropName;
temp.Offset = offsetBase.getOffsetTo(Prop);
assert(temp.Offset>=0);
temp.Group = PropertyGroup;
temp.Type = Type;
temp.Docu = PropertyDocu;
propertyData.push_back(temp);
}
Prop->syncType(Type);
Prop->myName = PropName;
}
void PropertyData::merge(PropertyData *other) const {
if(!other)
other = const_cast<PropertyData*>(parentPropertyData);
if(other == parentPropertyData) {
if(parentMerged)
return;
parentMerged = true;
}
if(other) {
other->merge();
auto &index = propertyData.get<0>();
for(const auto &spec : other->propertyData.get<0>())
index.push_back(spec);
}
}
void PropertyData::split(PropertyData *other) {
if(other == parentPropertyData) {
if(!parentMerged)
return;
parentMerged = false;
}
if(other) {
auto &index = propertyData.get<2>();
for(const auto &spec : other->propertyData.get<0>())
index.erase(spec.Offset);
}
}
const PropertyData::PropertySpec *PropertyData::findProperty(OffsetBase offsetBase,const char* PropName) const
{
for (vector<PropertyData::PropertySpec>::const_iterator It = propertyData.begin(); It != propertyData.end(); ++It)
if(strcmp(It->Name,PropName)==0)
return &(*It);
if(parentPropertyData)
return parentPropertyData->findProperty(offsetBase,PropName);
return 0;
(void)offsetBase;
merge();
auto &index = propertyData.get<1>();
auto it = index.find(PropName);
if(it != index.end())
return &(*it);
return 0;
}
const PropertyData::PropertySpec *PropertyData::findProperty(OffsetBase offsetBase,const Property* prop) const
{
const int diff = offsetBase.getOffsetTo(prop);
if(diff<0)
return 0;
merge();
int diff = offsetBase.getOffsetTo(prop);
if(diff<0)
return 0;
for (vector<PropertyData::PropertySpec>::const_iterator It = propertyData.begin(); It != propertyData.end(); ++It)
if(diff == It->Offset)
return &(*It);
if(parentPropertyData)
return parentPropertyData->findProperty(offsetBase,prop);
return 0;
auto &index = propertyData.get<2>();
auto it = index.find(diff);
if(it!=index.end())
return &(*it);
return 0;
}
const char* PropertyData::getName(OffsetBase offsetBase,const Property* prop) const
@@ -377,17 +488,6 @@ const char* PropertyData::getName(OffsetBase offsetBase,const Property* prop) co
return Spec->Name;
else
return 0;
/*
for(std::map<std::string,PropertySpec>::const_iterator pos = propertyData.begin();pos != propertyData.end();++pos)
if(pos->second.Offset == diff)
return pos->first.c_str();
if(parentPropertyData)
return parentPropertyData->getName(container,prop);
return 0;
*/
}
short PropertyData::getType(OffsetBase offsetBase,const Property* prop) const
@@ -398,19 +498,6 @@ short PropertyData::getType(OffsetBase offsetBase,const Property* prop) const
return Spec->Type;
else
return 0;
/*
const int diff = (int) ((char*)prop - (char*)container);
for(std::map<std::string,PropertySpec>::const_iterator pos = propertyData.begin();pos != propertyData.end();++pos)
if(pos->second.Offset == diff)
return pos->second.Type;
if(parentPropertyData)
return parentPropertyData->getType(container,prop);
return 0;
*/
}
short PropertyData::getType(OffsetBase offsetBase,const char* name) const
@@ -431,19 +518,6 @@ const char* PropertyData::getGroup(OffsetBase offsetBase,const Property* prop) c
return Spec->Group;
else
return 0;
/*
const int diff = (int) ((char*)prop - (char*)container);
for(std::map<std::string,PropertySpec>::const_iterator pos = propertyData.begin();pos != propertyData.end();++pos)
if(pos->second.Offset == diff)
return pos->second.Group;
if(parentPropertyData)
return parentPropertyData->getGroup(container,prop);
return 0;
*/
}
const char* PropertyData::getGroup(OffsetBase offsetBase,const char* name) const
@@ -476,8 +550,6 @@ const char* PropertyData::getDocumentation(OffsetBase offsetBase,const char* nam
return 0;
}
Property *PropertyData::getPropertyByName(OffsetBase offsetBase,const char* name) const
{
const PropertyData::PropertySpec* Spec = findProperty(offsetBase,name);
@@ -486,53 +558,22 @@ Property *PropertyData::getPropertyByName(OffsetBase offsetBase,const char* name
return (Property *) (Spec->Offset + offsetBase.getOffset());
else
return 0;
/*
std::map<std::string,PropertySpec>::const_iterator pos = propertyData.find(name);
if(pos != propertyData.end())
{
// calculate propterty by offset
return (Property *) (pos->second.Offset + (char *)container);
}else{
if(parentPropertyData)
return parentPropertyData->getPropertyByName(container,name);
else
return 0;
}*/
}
void PropertyData::getPropertyMap(OffsetBase offsetBase,std::map<std::string,Property*> &Map) const
{
for (vector<PropertyData::PropertySpec>::const_iterator It = propertyData.begin(); It != propertyData.end(); ++It)
Map[It->Name] = (Property *) (It->Offset + offsetBase.getOffset());
/*
std::map<std::string,PropertySpec>::const_iterator pos;
for(pos = propertyData.begin();pos != propertyData.end();++pos)
{
Map[pos->first] = (Property *) (pos->second.Offset + (char *)container);
}
*/
if(parentPropertyData)
parentPropertyData->getPropertyMap(offsetBase,Map);
merge();
for(auto &spec : propertyData.get<0>())
Map[spec.Name] = (Property *) (spec.Offset + offsetBase.getOffset());
}
void PropertyData::getPropertyList(OffsetBase offsetBase,std::vector<Property*> &List) const
{
for (vector<PropertyData::PropertySpec>::const_iterator It = propertyData.begin(); It != propertyData.end(); ++It)
List.push_back((Property *) (It->Offset + offsetBase.getOffset()) );
/* std::map<std::string,PropertySpec>::const_iterator pos;
for(pos = propertyData.begin();pos != propertyData.end();++pos)
{
List.push_back((Property *) (pos->second.Offset + (char *)container) );
}*/
if(parentPropertyData)
parentPropertyData->getPropertyList(offsetBase,List);
merge();
size_t base = List.size();
List.reserve(base+propertyData.size());
for (auto &spec : propertyData.get<0>())
List.push_back((Property *) (spec.Offset + offsetBase.getOffset()));
}

View File

@@ -26,8 +26,11 @@
#include <map>
#include <climits>
#include <cstring>
#include <Base/Persistence.h>
#include "DynamicProperty.h"
namespace Base {
class Writer;
}
@@ -37,7 +40,6 @@ namespace App
{
class Property;
class PropertyContainer;
class DynamicProperty;
class DocumentObject;
class Extension;
@@ -45,20 +47,25 @@ enum PropertyType
{
Prop_None = 0, /*!< No special property type */
Prop_ReadOnly = 1, /*!< Property is read-only in the editor */
Prop_Transient = 2, /*!< Property won't be saved to file */
Prop_Transient = 2, /*!< Property content won't be saved to file, but still saves name, type and status */
Prop_Hidden = 4, /*!< Property won't appear in the editor */
Prop_Output = 8, /*!< Modified property doesn't touch its parent container */
Prop_NoRecompute = 16 /*!< Modified property doesn't touch its container for recompute */
Prop_NoRecompute = 16,/*!< Modified property doesn't touch its container for recompute */
Prop_NoPersist = 32,/*!< Property won't be saved to file at all */
};
struct AppExport PropertyData
{
struct PropertySpec
{
const char* Name;
const char * Name;
const char * Group;
const char * Docu;
short Offset,Type;
short Offset, Type;
inline PropertySpec(const char *name, const char *group, const char *doc, short offset, short type)
:Name(name),Group(group),Docu(doc),Offset(offset),Type(type)
{}
};
//purpose of this struct is to be constructible from all acceptable container types and to
@@ -82,10 +89,30 @@ struct AppExport PropertyData
private:
const void* m_container;
};
// vector of all properties
std::vector<PropertySpec> propertyData;
const PropertyData* parentPropertyData;
// A multi index container for holding the property spec, with the following
// index,
// * a sequence, to preserve creation order
// * hash index on property name
// * hash index on property pointer offset
mutable bmi::multi_index_container<
PropertySpec,
bmi::indexed_by<
bmi::sequenced<>,
bmi::hashed_unique<
bmi::member<PropertySpec, const char*, &PropertySpec::Name>,
CStringHasher,
CStringHasher
>,
bmi::hashed_unique<
bmi::member<PropertySpec, short, &PropertySpec::Offset>
>
>
> propertyData;
mutable bool parentMerged = false;
const PropertyData* parentPropertyData;
void addProperty(OffsetBase offsetBase,const char* PropName, Property *Prop, const char* PropertyGroup= 0, PropertyType = Prop_None, const char* PropertyDocu= 0 );
@@ -103,6 +130,9 @@ struct AppExport PropertyData
Property *getPropertyByName(OffsetBase offsetBase,const char* name) const;
void getPropertyMap(OffsetBase offsetBase,std::map<std::string,Property*> &Map) const;
void getPropertyList(OffsetBase offsetBase,std::vector<Property*> &List) const;
void merge(PropertyData *other=0) const;
void split(PropertyData *other);
};
@@ -128,6 +158,8 @@ public:
virtual unsigned int getMemSize (void) const;
virtual std::string getFullName() const {return std::string();}
/// find a property by its name
virtual Property *getPropertyByName(const char* name) const;
/// get the name of a property
@@ -162,33 +194,34 @@ public:
virtual App::Property* addDynamicProperty(
const char* type, const char* name=0,
const char* group=0, const char* doc=0,
short attr=0, bool ro=false, bool hidden=false){
(void)type;
(void)name;
(void)group;
(void)doc;
(void)attr;
(void)ro;
(void)hidden;
return 0;
short attr=0, bool ro=false, bool hidden=false);
DynamicProperty::PropData getDynamicPropertyData(const Property* prop) const {
return dynamicProps.getDynamicPropertyData(prop);
}
virtual bool removeDynamicProperty(const char* name) {
(void)name;
return false;
return dynamicProps.removeDynamicProperty(name);
}
virtual std::vector<std::string> getDynamicPropertyNames() const {
return std::vector<std::string>();
return dynamicProps.getDynamicPropertyNames();
}
virtual App::Property *getDynamicPropertyByName(const char* name) const {
(void)name;
return 0;
}
virtual void addDynamicProperties(const PropertyContainer*) {
return dynamicProps.getDynamicPropertyByName(name);
}
virtual void onPropertyStatusChanged(const Property &prop, unsigned long oldStatus);
virtual void Save (Base::Writer &writer) const;
virtual void Restore(Base::XMLReader &reader);
const char *getPropertyPrefix() const {
return _propertyPrefix.c_str();
}
void setPropertyPrefix(const char *prefix) {
_propertyPrefix = prefix;
}
friend class Property;
friend class DynamicProperty;
@@ -212,25 +245,34 @@ private:
PropertyContainer(const PropertyContainer&);
PropertyContainer& operator = (const PropertyContainer&);
protected:
DynamicProperty dynamicProps;
private:
std::string _propertyPrefix;
static PropertyData propertyData;
};
/// Property define
#define ADD_PROPERTY(_prop_, _defaultval_) \
#define _ADD_PROPERTY(_name,_prop_, _defaultval_) \
do { \
this->_prop_.setValue _defaultval_;\
this->_prop_.setContainer(this); \
propertyData.addProperty(static_cast<App::PropertyContainer*>(this), #_prop_, &this->_prop_); \
propertyData.addProperty(static_cast<App::PropertyContainer*>(this), _name, &this->_prop_); \
} while (0)
#define ADD_PROPERTY(_prop_, _defaultval_) \
_ADD_PROPERTY(#_prop_, _prop_, _defaultval_)
#define _ADD_PROPERTY_TYPE(_name,_prop_, _defaultval_, _group_,_type_,_Docu_) \
do { \
this->_prop_.setValue _defaultval_;\
this->_prop_.setContainer(this); \
propertyData.addProperty(static_cast<App::PropertyContainer*>(this), _name, &this->_prop_, (_group_),(_type_),(_Docu_)); \
} while (0)
#define ADD_PROPERTY_TYPE(_prop_, _defaultval_, _group_,_type_,_Docu_) \
do { \
this->_prop_.setValue _defaultval_;\
this->_prop_.setContainer(this); \
propertyData.addProperty(static_cast<App::PropertyContainer*>(this), #_prop_, &this->_prop_, (_group_),(_type_),(_Docu_)); \
} while (0)
_ADD_PROPERTY_TYPE(#_prop_,_prop_,_defaultval_,_group_,_type_,_Docu_)
#define PROPERTY_HEADER(_class_) \

View File

@@ -19,6 +19,11 @@
<UserDocu>Return the value of a named property.</UserDocu>
</Documentation>
</Methode>
<Methode Name="getPropertyTouchList">
<Documentation>
<UserDocu>Return a list of index of touched values for list type properties.</UserDocu>
</Documentation>
</Methode>
<Methode Name="getTypeOfProperty">
<Documentation>
<UserDocu>Return the type of a named property. This can be (Hidden,ReadOnly,Output) or any combination. </UserDocu>
@@ -52,6 +57,30 @@ If the list contains 'Hidden' then the item even doesn't appear in the property
<UserDocu>Return the name of the group which the property belongs to in this class. The properties sorted in different named groups for convenience.</UserDocu>
</Documentation>
</Methode>
<Methode Name="setPropertyStatus">
<Documentation>
<UserDocu>
setPropertyStatus(name,val): Set property status
name(String): property name
val(String|Int|[String|Int...]): text or integer value, or list/tuple of
values. Call getPropertyStatus() to get a list of supported text value.
If the text start with '-' or the integer value is negative, then the
status is cleared.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="getPropertyStatus">
<Documentation>
<UserDocu>
getPropertyStatus(name=''): Get property status.
name(String): property name. If name is empty, return a list of supported
text names of the status.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="getDocumentationOfProperty">
<Documentation>
<UserDocu>Return the documentation string of the property of this class.</UserDocu>

View File

@@ -30,7 +30,9 @@
#include "PropertyContainer.h"
#include "Property.h"
#include "PropertyLinks.h"
#include "Application.h"
#include "DocumentObject.h"
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>
@@ -39,6 +41,8 @@
#include "PropertyContainerPy.h"
#include "PropertyContainerPy.cpp"
FC_LOG_LEVEL_INIT("Property", true, 2);
using namespace App;
// returns a string which represent the object e.g. when printed in python
@@ -62,6 +66,27 @@ PyObject* PropertyContainerPy::getPropertyByName(PyObject *args)
}
}
PyObject* PropertyContainerPy::getPropertyTouchList(PyObject *args)
{
char *pstr;
if (!PyArg_ParseTuple(args, "s", &pstr)) // convert args: Python->C
return NULL; // NULL triggers exception
App::Property* prop = getPropertyContainerPtr()->getPropertyByName(pstr);
if (prop && prop->isDerivedFrom(PropertyLists::getClassTypeId())) {
const auto &touched = static_cast<PropertyLists*>(prop)->getTouchList();
Py::Tuple ret(touched.size());
int i=0;
for(int idx : touched)
ret.setItem(i++,Py::Long(idx));
return Py::new_reference_to(ret);
}
else if(!prop)
PyErr_Format(PyExc_AttributeError, "Property container has no property '%s'", pstr);
else
PyErr_Format(PyExc_AttributeError, "Property '%s' is not of list type", pstr);
return NULL;
}
PyObject* PropertyContainerPy::getTypeOfProperty(PyObject *args)
{
Py::List ret;
@@ -117,12 +142,10 @@ PyObject* PropertyContainerPy::setEditorMode(PyObject *args)
return 0;
}
unsigned long status = prop->getStatus();
prop->setStatus(Property::ReadOnly,(type & 1) > 0);
prop->setStatus(Property::Hidden,(type & 2) > 0);
if (status != prop->getStatus())
GetApplication().signalChangePropertyEditor(*prop);
std::bitset<32> status(prop->getStatus());
status.set(Property::ReadOnly, (type & 1) > 0);
status.set(Property::Hidden, (type & 2) > 0);
prop->setStatusValue(status.to_ulong());
Py_Return;
}
@@ -139,20 +162,17 @@ PyObject* PropertyContainerPy::setEditorMode(PyObject *args)
}
// reset all bits first
unsigned long status = prop->getStatus();
prop->setStatus(Property::ReadOnly, false);
prop->setStatus(Property::Hidden, false);
std::bitset<32> status(prop->getStatus());
status.reset(Property::ReadOnly);
status.reset(Property::Hidden);
for (Py::Sequence::iterator it = seq.begin();it!=seq.end();++it) {
std::string str = (std::string)Py::String(*it);
if (str == "ReadOnly")
prop->setStatus(Property::ReadOnly, true);
status.set(Property::ReadOnly);
else if (str == "Hidden")
prop->setStatus(Property::Hidden, true);
status.set(Property::Hidden);
}
if (status != prop->getStatus())
GetApplication().signalChangePropertyEditor(*prop);
prop->setStatusValue(status.to_ulong());
Py_Return;
}
@@ -162,6 +182,124 @@ PyObject* PropertyContainerPy::setEditorMode(PyObject *args)
return 0;
}
static const std::map<std::string, int> &getStatusMap() {
static std::map<std::string,int> statusMap;
if(statusMap.empty()) {
statusMap["Immutable"] = Property::Immutable;
statusMap["ReadOnly"] = Property::ReadOnly;
statusMap["Hidden"] = Property::Hidden;
statusMap["Transient"] = Property::Transient;
statusMap["MaterialEdit"] = Property::MaterialEdit;
statusMap["NoMaterialListEdit"] = Property::NoMaterialListEdit;
statusMap["Output"] = Property::Output;
statusMap["LockDynamic"] = Property::LockDynamic;
statusMap["NoModify"] = Property::NoModify;
statusMap["PartialTrigger"] = Property::PartialTrigger;
}
return statusMap;
}
PyObject* PropertyContainerPy::setPropertyStatus(PyObject *args)
{
char* name;
PyObject *pyValue;
if (!PyArg_ParseTuple(args, "sO", &name, &pyValue))
return 0;
App::Property* prop = getPropertyContainerPtr()->getPropertyByName(name);
if (!prop) {
PyErr_Format(PyExc_AttributeError, "Property container has no property '%s'", name);
return 0;
}
auto linkProp = Base::freecad_dynamic_cast<App::PropertyLinkBase>(prop);
std::bitset<32> status(prop->getStatus());
size_t count = 1;
bool isSeq = false;
if(PyList_Check(pyValue) || PyTuple_Check(pyValue)) {
isSeq = true;
count = PySequence_Size(pyValue);
}
for(size_t i=0;i<count;++i) {
Py::Object item;
if(isSeq)
item = Py::Object(PySequence_GetItem(pyValue,i));
else
item = Py::Object(pyValue);
bool value = true;
if(item.isString()) {
const auto &statusMap = getStatusMap();
auto v = (std::string)Py::String(item);
if(v.size()>1 && v[0] == '-') {
value = false;
v = v.substr(1);
}
auto it = statusMap.find(v);
if(it == statusMap.end()) {
if(linkProp && v == "AllowPartial") {
linkProp->setAllowPartial(value);
continue;
}
PyErr_Format(PyExc_ValueError, "Unknown property status '%s'", v.c_str());
return 0;
}
status.set(it->second,value);
}else if(item.isNumeric()) {
int v = Py::Int(item);
if(v<0) {
value = false;
v = -v;
}
if(v==0 || v>31)
PyErr_Format(PyExc_ValueError, "Status value out of range '%d'", v);
status.set(v,value);
} else {
PyErr_SetString(PyExc_TypeError, "Expects status type to be Int or String");
return 0;
}
}
prop->setStatusValue(status.to_ulong());
Py_Return;
}
PyObject* PropertyContainerPy::getPropertyStatus(PyObject *args)
{
char* name = "";
if (!PyArg_ParseTuple(args, "|s", &name)) // convert args: Python->C
return NULL; // NULL triggers exception
Py::List ret;
const auto &statusMap = getStatusMap();
if(!name[0]) {
for(auto &v : statusMap)
ret.append(Py::String(v.first.c_str()));
}else{
App::Property* prop = getPropertyContainerPtr()->getPropertyByName(name);
if (prop) {
auto linkProp = Base::freecad_dynamic_cast<App::PropertyLinkBase>(prop);
if(linkProp && linkProp->testFlag(App::PropertyLinkBase::LinkAllowPartial))
ret.append(Py::String("AllowPartial"));
std::bitset<32> bits(prop->getStatus());
for(size_t i=1;i<bits.size();++i) {
if(!bits[i]) continue;
bool found = false;
for(auto &v : statusMap) {
if(v.second == (int)i) {
ret.append(Py::String(v.first.c_str()));
found = true;
break;
}
}
if(!found)
ret.append(Py::Int((long)i));
}
}
}
return Py::new_reference_to(ret);
}
PyObject* PropertyContainerPy::getEditorMode(PyObject *args)
{
char* name;
@@ -336,14 +474,19 @@ PyObject* PropertyContainerPy::restorePropertyContent(PyObject *args)
PyObject *PropertyContainerPy::getCustomAttributes(const char* attr) const
{
// search in PropertyList
if(FC_LOG_INSTANCE.level()>FC_LOGLEVEL_TRACE) {
FC_TRACE("Get property " << attr);
}
Property *prop = getPropertyContainerPtr()->getPropertyByName(attr);
if (prop) {
PyObject* pyobj = prop->getPyObject();
if (!pyobj && PyErr_Occurred()) {
// the Python exception is already set
throw Py::Exception();
}
return pyobj;
PY_TRY {
PyObject* pyobj = prop->getPyObject();
if (!pyobj && PyErr_Occurred()) {
// the Python exception is already set
throw Py::Exception();
}
return pyobj;
}PY_CATCH
}
else if (Base::streq(attr, "__dict__")) {
// get the properties to the C++ PropertyContainer class
@@ -374,14 +517,17 @@ int PropertyContainerPy::setCustomAttributes(const char* attr, PyObject *obj)
Property *prop = getPropertyContainerPtr()->getPropertyByName(attr);
if (prop) {
// Read-only attributes must not be set over its Python interface
short Type = getPropertyContainerPtr()->getPropertyType(prop);
if (Type & Prop_ReadOnly) {
if(prop->testStatus(Property::Immutable)) {
std::stringstream s;
s << "Object attribute '" << attr << "' is read-only";
throw Py::AttributeError(s.str());
}
prop->setPyObject(obj);
PY_TRY {
FC_TRACE("Set property " << prop->getFullName());
prop->setPyObject(obj);
}_PY_CATCH(return(-1))
return 1;
}

View File

@@ -319,37 +319,9 @@ PropertyVectorList::~PropertyVectorList()
//**************************************************************************
// Base class implementer
void PropertyVectorList::setSize(int newSize)
{
_lValueList.resize(newSize);
}
int PropertyVectorList::getSize(void) const
{
return static_cast<int>(_lValueList.size());
}
void PropertyVectorList::setValue(const Base::Vector3d& lValue)
{
aboutToSetValue();
_lValueList.resize(1);
_lValueList[0]=lValue;
hasSetValue();
}
void PropertyVectorList::setValue(double x, double y, double z)
{
aboutToSetValue();
_lValueList.resize(1);
_lValueList[0].Set(x,y,z);
hasSetValue();
}
void PropertyVectorList::setValues(const std::vector<Base::Vector3d>& values)
{
aboutToSetValue();
_lValueList = values;
hasSetValue();
setValue(Base::Vector3d(x,y,z));
}
PyObject *PropertyVectorList::getPyObject(void)
@@ -362,37 +334,10 @@ PyObject *PropertyVectorList::getPyObject(void)
return list;
}
void PropertyVectorList::setPyObject(PyObject *value)
{
if (PySequence_Check(value)) {
Py_ssize_t nSize = PySequence_Size(value);
std::vector<Base::Vector3d> values;
values.resize(nSize);
for (Py_ssize_t i=0; i<nSize;++i) {
PyObject* item = PySequence_GetItem(value, i);
PropertyVector val;
val.setPyObject( item );
values[i] = val.getValue();
}
setValues(values);
}
else if (PyObject_TypeCheck(value, &(VectorPy::Type))) {
Base::VectorPy *pcObject = static_cast<Base::VectorPy*>(value);
Base::Vector3d* val = pcObject->getVectorPtr();
setValue(*val);
}
else if (PyTuple_Check(value) && PyTuple_Size(value) == 3) {
PropertyVector val;
val.setPyObject( value );
setValue( val.getValue() );
}
else {
std::string error = std::string("type must be 'Vector' or list of 'Vector', not ");
error += value->ob_type->tp_name;
throw Base::TypeError(error);
}
Base::Vector3d PropertyVectorList::getPyValue(PyObject *item) const {
PropertyVector val;
val.setPyObject( item );
return val.getValue();
}
void PropertyVectorList::Save (Base::Writer &writer) const
@@ -463,9 +408,7 @@ Property *PropertyVectorList::Copy(void) const
void PropertyVectorList::Paste(const Property &from)
{
aboutToSetValue();
_lValueList = dynamic_cast<const PropertyVectorList&>(from)._lValueList;
hasSetValue();
setValues(dynamic_cast<const PropertyVectorList&>(from)._lValueList);
}
unsigned int PropertyVectorList::getMemSize (void) const
@@ -639,6 +582,18 @@ void PropertyPlacement::setValue(const Base::Placement &pos)
hasSetValue();
}
bool PropertyPlacement::setValueIfChanged(const Base::Placement &pos,double tol,double atol)
{
if(_cPos.getPosition().IsEqual(pos.getPosition(),tol)
&& _cPos.getRotation().isSame(pos.getRotation(),atol))
{
return false;
}
setValue(pos);
return true;
}
const Base::Placement & PropertyPlacement::getValue(void)const
{
return _cPos;
@@ -826,31 +781,6 @@ PropertyPlacementList::~PropertyPlacementList()
//**************************************************************************
// Base class implementer
void PropertyPlacementList::setSize(int newSize)
{
_lValueList.resize(newSize);
}
int PropertyPlacementList::getSize(void) const
{
return static_cast<int>(_lValueList.size());
}
void PropertyPlacementList::setValue(const Base::Placement& lValue)
{
aboutToSetValue();
_lValueList.resize(1);
_lValueList[0]=lValue;
hasSetValue();
}
void PropertyPlacementList::setValues(const std::vector<Base::Placement>& values)
{
aboutToSetValue();
_lValueList = values;
hasSetValue();
}
PyObject *PropertyPlacementList::getPyObject(void)
{
PyObject* list = PyList_New( getSize() );
@@ -861,37 +791,10 @@ PyObject *PropertyPlacementList::getPyObject(void)
return list;
}
void PropertyPlacementList::setPyObject(PyObject *value)
{
if (PySequence_Check(value)) {
Py_ssize_t nSize = PySequence_Size(value);
std::vector<Base::Placement> values;
values.resize(nSize);
for (Py_ssize_t i=0; i<nSize;++i) {
PyObject* item = PySequence_GetItem(value, i);
PropertyPlacement val;
val.setPyObject( item );
values[i] = val.getValue();
}
setValues(values);
}
else if (PyObject_TypeCheck(value, &(PlacementPy::Type))) {
Base::PlacementPy *pcObject = static_cast<Base::PlacementPy*>(value);
Base::Placement* val = pcObject->getPlacementPtr();
setValue(*val);
}
else if (PyTuple_Check(value) && PyTuple_Size(value) == 3) {
PropertyPlacement val;
val.setPyObject( value );
setValue( val.getValue() );
}
else {
std::string error = std::string("type must be 'Placement' or list of 'Placement', not ");
error += value->ob_type->tp_name;
throw Base::TypeError(error);
}
Base::Placement PropertyPlacementList::getPyValue(PyObject *item) const {
PropertyPlacement val;
val.setPyObject( item );
return val.getValue();
}
void PropertyPlacementList::Save (Base::Writer &writer) const
@@ -975,9 +878,7 @@ Property *PropertyPlacementList::Copy(void) const
void PropertyPlacementList::Paste(const Property &from)
{
aboutToSetValue();
_lValueList = dynamic_cast<const PropertyPlacementList&>(from)._lValueList;
hasSetValue();
setValues(dynamic_cast<const PropertyPlacementList&>(from)._lValueList);
}
unsigned int PropertyPlacementList::getMemSize (void) const

View File

@@ -175,10 +175,12 @@ public:
}
};
class AppExport PropertyVectorList: public PropertyLists
class AppExport PropertyVectorList: public PropertyListsT<Base::Vector3d>
{
TYPESYSTEM_HEADER();
typedef PropertyListsT<Base::Vector3d> inherited;
public:
/**
* A constructor.
@@ -192,33 +194,10 @@ public:
*/
virtual ~PropertyVectorList();
virtual void setSize(int newSize);
virtual int getSize(void) const;
/** Sets the property
*/
void setValue(const Base::Vector3d&);
void setValue(double x, double y, double z);
/// index operator
const Base::Vector3d& operator[] (const int idx) const {
return _lValueList.operator[] (idx);
}
void set1Value (const int idx, const Base::Vector3d& value) {
_lValueList.operator[] (idx) = value;
}
void setValues (const std::vector<Base::Vector3d>& values);
void setValue (void){}
const std::vector<Base::Vector3d> &getValues(void) const {
return _lValueList;
}
using inherited::setValue;
virtual PyObject *getPyObject(void);
virtual void setPyObject(PyObject *);
virtual void Save (Base::Writer &writer) const;
virtual void Restore(Base::XMLReader &reader);
@@ -231,8 +210,8 @@ public:
virtual unsigned int getMemSize (void) const;
private:
std::vector<Base::Vector3d> _lValueList;
protected:
Base::Vector3d getPyValue(PyObject *) const override;
};
/// Property representing a 4x4 matrix
@@ -308,6 +287,14 @@ public:
*/
void setValue(const Base::Placement &pos);
/** Sets property only if changed
* @param pos: input placement
* @param tol: position tolerance
* @param atol: angular tolerance
*/
bool setValueIfChanged(const Base::Placement &pos,
double tol=1e-7, double atol=1e-12);
/** This method returns a string representation of the property
*/
const Base::Placement &getValue(void) const;
@@ -371,7 +358,7 @@ public:
};
class AppExport PropertyPlacementList: public PropertyLists
class AppExport PropertyPlacementList: public PropertyListsT<Base::Placement>
{
TYPESYSTEM_HEADER();
@@ -383,32 +370,7 @@ public:
virtual ~PropertyPlacementList();
virtual void setSize(int newSize);
virtual int getSize(void) const;
/** Sets the property
*/
void setValue(const Base::Placement&);
/// index operator
const Base::Placement& operator[] (const int idx) const {
return _lValueList.operator[] (idx);
}
void set1Value (const int idx, const Base::Placement& value) {
_lValueList.operator[] (idx) = value;
}
void setValues (const std::vector<Base::Placement>& values);
void setValue (void){}
const std::vector<Base::Placement> &getValues(void) const {
return _lValueList;
}
virtual PyObject *getPyObject(void);
virtual void setPyObject(PyObject *);
virtual void Save (Base::Writer &writer) const;
virtual void Restore(Base::XMLReader &reader);
@@ -421,8 +383,8 @@ public:
virtual unsigned int getMemSize (void) const;
private:
std::vector<Base::Placement> _lValueList;
protected:
Base::Placement getPyValue(PyObject *) const override;
};

View File

@@ -701,34 +701,9 @@ PropertyIntegerList::~PropertyIntegerList()
}
void PropertyIntegerList::setSize(int newSize)
{
_lValueList.resize(newSize);
}
int PropertyIntegerList::getSize(void) const
{
return static_cast<int>(_lValueList.size());
}
//**************************************************************************
// Base class implementer
void PropertyIntegerList::setValue(long lValue)
{
aboutToSetValue();
_lValueList.resize(1);
_lValueList[0]=lValue;
hasSetValue();
}
void PropertyIntegerList::setValues(const std::vector<long>& values)
{
aboutToSetValue();
_lValueList = values;
hasSetValue();
}
PyObject *PropertyIntegerList::getPyObject(void)
{
PyObject* list = PyList_New(getSize());
@@ -741,47 +716,17 @@ PyObject *PropertyIntegerList::getPyObject(void)
return list;
}
void PropertyIntegerList::setPyObject(PyObject *value)
{
if (PySequence_Check(value)) {
Py_ssize_t nSize = PySequence_Size(value);
std::vector<long> values;
values.resize(nSize);
for (Py_ssize_t i=0; i<nSize;++i) {
PyObject* item = PySequence_GetItem(value, i);
long PropertyIntegerList::getPyValue(PyObject *item) const {
#if PY_MAJOR_VERSION < 3
if (!PyInt_Check(item)) {
std::string error = std::string("type in list must be int, not ");
error += item->ob_type->tp_name;
throw Base::TypeError(error);
}
values[i] = PyInt_AsLong(item);
if (PyInt_Check(item))
return PyInt_AsLong(item);
#else
if (!PyLong_Check(item)) {
std::string error = std::string("type in list must be int, not ");
error += item->ob_type->tp_name;
throw Base::TypeError(error);
}
values[i] = PyLong_AsLong(item);
if (PyLong_Check(item))
return PyLong_AsLong(item);
#endif
}
setValues(values);
}
#if PY_MAJOR_VERSION < 3
else if (PyInt_Check(value)) {
setValue(PyInt_AsLong(value));
#else
else if (PyLong_Check(value)) {
setValue(PyLong_AsLong(value));
#endif
}
else {
std::string error = std::string("type must be int or a sequence of int, not ");
error += value->ob_type->tp_name;
throw Base::TypeError(error);
}
std::string error = std::string("type in list must be int, not ");
error += item->ob_type->tp_name;
throw Base::TypeError(error);
}
void PropertyIntegerList::Save (Base::Writer &writer) const
@@ -822,8 +767,6 @@ Property *PropertyIntegerList::Copy(void) const
void PropertyIntegerList::Paste(const Property &from)
{
aboutToSetValue();
_lValueList = dynamic_cast<const PropertyIntegerList&>(from)._lValueList;
hasSetValue();
}
@@ -1251,31 +1194,6 @@ PropertyFloatList::~PropertyFloatList()
//**************************************************************************
// Base class implementer
void PropertyFloatList::setSize(int newSize)
{
_lValueList.resize(newSize);
}
int PropertyFloatList::getSize(void) const
{
return static_cast<int>(_lValueList.size());
}
void PropertyFloatList::setValue(double lValue)
{
aboutToSetValue();
_lValueList.resize(1);
_lValueList[0]=lValue;
hasSetValue();
}
void PropertyFloatList::setValues(const std::vector<double>& values)
{
aboutToSetValue();
_lValueList = values;
hasSetValue();
}
PyObject *PropertyFloatList::getPyObject(void)
{
PyObject* list = PyList_New(getSize());
@@ -1284,39 +1202,19 @@ PyObject *PropertyFloatList::getPyObject(void)
return list;
}
void PropertyFloatList::setPyObject(PyObject *value)
{
if (PyList_Check(value)) {
Py_ssize_t nSize = PyList_Size(value);
std::vector<double> values;
values.resize(nSize);
for (Py_ssize_t i=0; i<nSize;++i) {
PyObject* item = PyList_GetItem(value, i);
if (PyFloat_Check(item)) {
values[i] = PyFloat_AsDouble(item);
double PropertyFloatList::getPyValue(PyObject *item) const {
if (PyFloat_Check(item)) {
return PyFloat_AsDouble(item);
#if PY_MAJOR_VERSION >= 3
} else if (PyLong_Check(item)) {
values[i] = static_cast<double>(PyLong_AsLong(item));
} else if (PyLong_Check(item)) {
return static_cast<double>(PyLong_AsLong(item));
#else
} else if (PyInt_Check(item)) {
values[i] = static_cast<double>(PyInt_AsLong(item));
} else if (PyInt_Check(item)) {
return static_cast<double>(PyInt_AsLong(item));
#endif
} else {
std::string error = std::string("type in list must be float, not ");
error += item->ob_type->tp_name;
throw Base::TypeError(error);
}
}
setValues(values);
}
else if (PyFloat_Check(value)) {
setValue(PyFloat_AsDouble(value));
}
else {
std::string error = std::string("type must be float or list of float, not ");
error += value->ob_type->tp_name;
} else {
std::string error = std::string("type in list must be float, not ");
error += item->ob_type->tp_name;
throw Base::TypeError(error);
}
}
@@ -1333,7 +1231,7 @@ void PropertyFloatList::Save (Base::Writer &writer) const
}
else {
writer.Stream() << writer.ind() << "<FloatList file=\"" <<
writer.addFile(getName(), this) << "\"/>" << std::endl;
(getSize()?writer.addFile(getName(), this):"") << "\"/>" << std::endl;
}
}
@@ -1396,9 +1294,7 @@ Property *PropertyFloatList::Copy(void) const
void PropertyFloatList::Paste(const Property &from)
{
aboutToSetValue();
_lValueList = dynamic_cast<const PropertyFloatList&>(from)._lValueList;
hasSetValue();
setValues(dynamic_cast<const PropertyFloatList&>(from)._lValueList);
}
unsigned int PropertyFloatList::getMemSize (void) const
@@ -1679,37 +1575,13 @@ PropertyStringList::~PropertyStringList()
//**************************************************************************
// Base class implementer
void PropertyStringList::setSize(int newSize)
{
_lValueList.resize(newSize);
}
int PropertyStringList::getSize(void) const
{
return static_cast<int>(_lValueList.size());
}
void PropertyStringList::setValue(const std::string& lValue)
{
aboutToSetValue();
_lValueList.resize(1);
_lValueList[0]=lValue;
hasSetValue();
}
void PropertyStringList::setValues(const std::vector<std::string>& lValue)
{
aboutToSetValue();
_lValueList=lValue;
hasSetValue();
}
void PropertyStringList::setValues(const std::list<std::string>& lValue)
{
aboutToSetValue();
_lValueList.resize(lValue.size());
std::copy(lValue.begin(), lValue.end(), _lValueList.begin());
hasSetValue();
std::vector<std::string> vals;
vals.reserve(lValue.size());
for(const auto &v : lValue)
vals.push_back(v);
setValues(vals);
}
PyObject *PropertyStringList::getPyObject(void)
@@ -1728,60 +1600,28 @@ PyObject *PropertyStringList::getPyObject(void)
return list;
}
void PropertyStringList::setPyObject(PyObject *value)
{
#if PY_MAJOR_VERSION >=3
if (PyBytes_Check(value)) {
setValue(PyBytes_AsString(value));
}
#else
if (PyString_Check(value)) {
setValue(PyString_AsString(value));
}
#endif
else if (PyUnicode_Check(value)) {
std::string PropertyStringList::getPyValue(PyObject *item) const {
std::string ret;
if (PyUnicode_Check(item)) {
#if PY_MAJOR_VERSION >= 3
setValue(PyUnicode_AsUTF8(value));
}
ret = PyUnicode_AsUTF8(item);
#else
PyObject* unicode = PyUnicode_AsUTF8String(value);
setValue(PyString_AsString(unicode));
PyObject* unicode = PyUnicode_AsUTF8String(item);
ret = PyString_AsString(unicode);
Py_DECREF(unicode);
}
} else if (PyString_Check(item)) {
ret = PyString_AsString(item);
#endif
else if (PySequence_Check(value)) {
Py_ssize_t nSize = PySequence_Size(value);
std::vector<std::string> values;
values.resize(nSize);
for (Py_ssize_t i=0; i<nSize;++i) {
PyObject* item = PySequence_GetItem(value, i);
if (PyUnicode_Check(item)) {
#if PY_MAJOR_VERSION >= 3
values[i] = PyUnicode_AsUTF8(item);
#else
PyObject* unicode = PyUnicode_AsUTF8String(item);
values[i] = PyString_AsString(unicode);
Py_DECREF(unicode);
}
else if (PyString_Check(item)) {
values[i] = PyString_AsString(item);
} else if (PyBytes_Check(item)) {
ret = PyBytes_AsString(item);
#endif
}
else {
std::string error = std::string("type in list must be str or unicode, not ");
error += item->ob_type->tp_name;
throw Base::TypeError(error);
}
}
setValues(values);
}
else {
std::string error = std::string("type must be str or unicode or list of str or list of unicodes, not ");
error += value->ob_type->tp_name;
} else {
std::string error = std::string("type in list must be str or unicode, not ");
error += item->ob_type->tp_name;
throw Base::TypeError(error);
}
return ret;
}
unsigned int PropertyStringList::getMemSize (void) const
@@ -1832,9 +1672,7 @@ Property *PropertyStringList::Copy(void) const
void PropertyStringList::Paste(const Property &from)
{
aboutToSetValue();
_lValueList = dynamic_cast<const PropertyStringList&>(from)._lValueList;
hasSetValue();
setValues(dynamic_cast<const PropertyStringList&>(from)._lValueList);
}
@@ -1929,8 +1767,7 @@ void PropertyMap::setPyObject(PyObject *value)
PyObject* unicode = PyUnicode_AsUTF8String(key);
keyStr = PyString_AsString(unicode);
Py_DECREF(unicode);
}
if (PyString_Check(key)) {
}else if (PyString_Check(key)) {
keyStr = PyString_AsString(key);
#endif
}
@@ -2163,41 +2000,9 @@ PropertyBoolList::~PropertyBoolList()
}
void PropertyBoolList::setSize(int newSize)
{
_lValueList.resize(newSize);
}
int PropertyBoolList::getSize(void) const
{
return static_cast<int>(_lValueList.size());
}
//**************************************************************************
// Base class implementer
void PropertyBoolList::setValue(bool lValue)
{
aboutToSetValue();
_lValueList.resize(1);
_lValueList[0]=lValue;
hasSetValue();
}
void PropertyBoolList::set1Value(const int idx, bool value)
{
aboutToSetValue();
_lValueList[idx]=value;
hasSetValue();
}
void PropertyBoolList::setValues(const boost::dynamic_bitset<>& values)
{
aboutToSetValue();
_lValueList = values;
hasSetValue();
}
PyObject *PropertyBoolList::getPyObject(void)
{
PyObject* tuple = PyTuple_New(getSize());
@@ -2226,53 +2031,28 @@ void PropertyBoolList::setPyObject(PyObject *value)
Py_DECREF(unicode);
boost::dynamic_bitset<> values(str);
setValues(values);
}
if (PyString_Check(value)) {
} else if (PyString_Check(value)) {
str = PyString_AsString(value);
#endif
boost::dynamic_bitset<> values(str);
setValues(values);
}
else if (PySequence_Check(value)) {
Py_ssize_t nSize = PySequence_Size(value);
boost::dynamic_bitset<> values(nSize);
}else
inherited::setPyObject(value);
}
for (Py_ssize_t i=0; i<nSize;++i) {
PyObject* item = PySequence_GetItem(value, i);
if (PyBool_Check(item)) {
values[i] = (PyObject_IsTrue(item) ? true : false);
}
bool PropertyBoolList::getPyValue(PyObject *item) const {
if (PyBool_Check(item)) {
return (PyObject_IsTrue(item) ? true : false);
#if PY_MAJOR_VERSION < 3
else if (PyInt_Check(item)) {
values[i] = (PyInt_AsLong(item) ? true : false);
} else if (PyInt_Check(item)) {
return (PyInt_AsLong(item) ? true : false);
#else
else if (PyLong_Check(item)) {
values[i] = (PyLong_AsLong(item) ? true : false);
} else if (PyLong_Check(item)) {
return (PyLong_AsLong(item) ? true : false);
#endif
}
else {
std::string error = std::string("type in list must be bool or int, not ");
error += item->ob_type->tp_name;
throw Base::TypeError(error);
}
}
setValues(values);
}
else if (PyBool_Check(value)) {
setValue(PyObject_IsTrue(value) ? true : false);
}
#if PY_MAJOR_VERSION < 3
else if (PyInt_Check(value)) {
setValue(PyInt_AsLong(value) ? true : false);
#else
else if (PyLong_Check(value)) {
setValue(PyLong_AsLong(value) ? true : false);
#endif
}
else {
std::string error = std::string("type must be bool or a sequence of bool, not ");
error += value->ob_type->tp_name;
} else {
std::string error = std::string("type in list must be bool or int, not ");
error += item->ob_type->tp_name;
throw Base::TypeError(error);
}
}
@@ -2305,9 +2085,7 @@ Property *PropertyBoolList::Copy(void) const
void PropertyBoolList::Paste(const Property &from)
{
aboutToSetValue();
_lValueList = dynamic_cast<const PropertyBoolList&>(from)._lValueList;
hasSetValue();
setValues(dynamic_cast<const PropertyBoolList&>(from)._lValueList);
}
unsigned int PropertyBoolList::getMemSize (void) const
@@ -2487,31 +2265,6 @@ PropertyColorList::~PropertyColorList()
//**************************************************************************
// Base class implementer
void PropertyColorList::setSize(int newSize)
{
_lValueList.resize(newSize);
}
int PropertyColorList::getSize(void) const
{
return static_cast<int>(_lValueList.size());
}
void PropertyColorList::setValue(const Color& lValue)
{
aboutToSetValue();
_lValueList.resize(1);
_lValueList[0]=lValue;
hasSetValue();
}
void PropertyColorList::setValues (const std::vector<Color>& values)
{
aboutToSetValue();
_lValueList=values;
hasSetValue();
}
PyObject *PropertyColorList::getPyObject(void)
{
PyObject* list = PyList_New(getSize());
@@ -2534,43 +2287,17 @@ PyObject *PropertyColorList::getPyObject(void)
return list;
}
void PropertyColorList::setPyObject(PyObject *value)
{
if (PyList_Check(value)) {
Py_ssize_t nSize = PyList_Size(value);
std::vector<Color> values;
values.resize(nSize);
for (Py_ssize_t i=0; i<nSize;++i) {
PyObject* item = PyList_GetItem(value, i);
PropertyColor col;
col.setPyObject(item);
values[i] = col.getValue();
}
setValues(values);
}
else if (PyTuple_Check(value) && PyTuple_Size(value) == 3) {
PropertyColor col;
col.setPyObject( value );
setValue( col.getValue() );
}
else if (PyTuple_Check(value) && PyTuple_Size(value) == 4) {
PropertyColor col;
col.setPyObject( value );
setValue( col.getValue() );
}
else {
std::string error = std::string("not allowed type, ");
error += value->ob_type->tp_name;
throw Base::TypeError(error);
}
Color PropertyColorList::getPyValue(PyObject *item) const {
PropertyColor col;
col.setPyObject(item);
return col.getValue();
}
void PropertyColorList::Save (Base::Writer &writer) const
{
if (!writer.isForceXML()) {
writer.Stream() << writer.ind() << "<ColorList file=\"" << writer.addFile(getName(), this) << "\"/>" << std::endl;
writer.Stream() << writer.ind() << "<ColorList file=\"" <<
(getSize()?writer.addFile(getName(), this):"") << "\"/>" << std::endl;
}
}
@@ -2620,9 +2347,7 @@ Property *PropertyColorList::Copy(void) const
void PropertyColorList::Paste(const Property &from)
{
aboutToSetValue();
_lValueList = dynamic_cast<const PropertyColorList&>(from)._lValueList;
hasSetValue();
setValues(dynamic_cast<const PropertyColorList&>(from)._lValueList);
}
unsigned int PropertyColorList::getMemSize (void) const
@@ -2746,7 +2471,9 @@ void PropertyMaterial::Restore(Base::XMLReader &reader)
const char* PropertyMaterial::getEditorName(void) const
{
return "";// "Gui::PropertyEditor::PropertyMaterialItem";
if(testStatus(MaterialEdit))
return "Gui::PropertyEditor::PropertyMaterialItem";
return "";
}
Property *PropertyMaterial::Copy(void) const
@@ -2785,31 +2512,6 @@ PropertyMaterialList::~PropertyMaterialList()
//**************************************************************************
// Base class implementer
void PropertyMaterialList::setSize(int newSize)
{
_lValueList.resize(newSize);
}
int PropertyMaterialList::getSize(void) const
{
return static_cast<int>(_lValueList.size());
}
void PropertyMaterialList::setValue(const Material& lValue)
{
aboutToSetValue();
_lValueList.resize(1);
_lValueList[0] = lValue;
hasSetValue();
}
void PropertyMaterialList::setValues(const std::vector<Material>& values)
{
aboutToSetValue();
_lValueList = values;
hasSetValue();
}
PyObject *PropertyMaterialList::getPyObject(void)
{
Py::Tuple tuple(getSize());
@@ -2821,24 +2523,9 @@ PyObject *PropertyMaterialList::getPyObject(void)
return Py::new_reference_to(tuple);
}
void PropertyMaterialList::setPyObject(PyObject *value)
{
if (PyObject_TypeCheck(value, &(MaterialPy::Type))) {
setValue(*static_cast<MaterialPy*>(value)->getMaterialPtr());
}
else if (PyList_Check(value) || PyTuple_Check(value)) {
Py::Sequence list(value);
std::vector<Material> materials;
for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) {
if (PyObject_TypeCheck((*it).ptr(), &(MaterialPy::Type))) {
Material mat = *static_cast<MaterialPy*>((*it).ptr())->getMaterialPtr();
materials.push_back(mat);
}
}
setValues(materials);
}
Material PropertyMaterialList::getPyValue(PyObject *value) const {
if (PyObject_TypeCheck(value, &(MaterialPy::Type)))
return *static_cast<MaterialPy*>(value)->getMaterialPtr();
else {
std::string error = std::string("type must be 'Material', not ");
error += value->ob_type->tp_name;
@@ -2849,7 +2536,8 @@ void PropertyMaterialList::setPyObject(PyObject *value)
void PropertyMaterialList::Save(Base::Writer &writer) const
{
if (!writer.isForceXML()) {
writer.Stream() << writer.ind() << "<MaterialList file=\"" << writer.addFile(getName(), this) << "\"/>" << std::endl;
writer.Stream() << writer.ind() << "<MaterialList file=\"" <<
(getSize()?writer.addFile(getName(), this):"") << "\"/>" << std::endl;
}
}
@@ -2908,6 +2596,8 @@ void PropertyMaterialList::RestoreDocFile(Base::Reader &reader)
const char* PropertyMaterialList::getEditorName(void) const
{
if(testStatus(NoMaterialListEdit))
return "";
return "Gui::PropertyEditor::PropertyMaterialListItem";
}
@@ -2920,12 +2610,86 @@ Property *PropertyMaterialList::Copy(void) const
void PropertyMaterialList::Paste(const Property &from)
{
aboutToSetValue();
_lValueList = dynamic_cast<const PropertyMaterialList&>(from)._lValueList;
hasSetValue();
setValues(dynamic_cast<const PropertyMaterialList&>(from)._lValueList);
}
unsigned int PropertyMaterialList::getMemSize(void) const
{
return static_cast<unsigned int>(_lValueList.size() * sizeof(Material));
}
//**************************************************************************
// PropertyPersistentObject
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TYPESYSTEM_SOURCE(App::PropertyPersistentObject , App::PropertyString);
PyObject *PropertyPersistentObject::getPyObject(void){
if(_pObject)
return _pObject->getPyObject();
return inherited::getPyObject();
}
void PropertyPersistentObject::Save(Base::Writer &writer) const{
inherited::Save(writer);
#define ELEMENT_PERSISTENT_OBJ "PersistentObject"
writer.Stream() << writer.ind() << "<" ELEMENT_PERSISTENT_OBJ ">" << std::endl;
if(_pObject) {
writer.incInd();
_pObject->Save(writer);
writer.decInd();
}
writer.Stream() << writer.ind() << "</" ELEMENT_PERSISTENT_OBJ ">" << std::endl;
}
void PropertyPersistentObject::Restore(Base::XMLReader &reader){
inherited::Restore(reader);
reader.readElement(ELEMENT_PERSISTENT_OBJ);
if(_pObject)
_pObject->Restore(reader);
reader.readEndElement(ELEMENT_PERSISTENT_OBJ);
}
Property *PropertyPersistentObject::Copy(void) const{
auto *p= new PropertyPersistentObject();
p->_cValue = _cValue;
p->_pObject = _pObject;
return p;
}
void PropertyPersistentObject::Paste(const Property &from){
const auto &prop = dynamic_cast<const PropertyPersistentObject&>(from);
if(_cValue!=prop._cValue || _pObject!=prop._pObject) {
aboutToSetValue();
_cValue = prop._cValue;
_pObject = prop._pObject;
hasSetValue();
}
}
unsigned int PropertyPersistentObject::getMemSize (void) const{
auto size = inherited::getMemSize();
if(_pObject)
size += _pObject->getMemSize();
return size;
}
void PropertyPersistentObject::setValue(const char *type) {
if(!type) type = "";
if(type[0]) {
Base::Type::importModule(type);
Base::Type t = Base::Type::fromName(type);
if(t.isBad())
throw Base::TypeError("Invalid type");
if(!t.isDerivedFrom(Persistence::getClassTypeId()))
throw Base::TypeError("Type must be derived from Base::Persistence");
if(_pObject && _pObject->getTypeId()==t)
return;
}
aboutToSetValue();
_pObject.reset();
_cValue = type;
if(type[0])
_pObject.reset(static_cast<Base::Persistence*>(Base::Type::createInstanceByName(type)));
hasSetValue();
}

View File

@@ -27,6 +27,7 @@
// Std. configurations
#include <memory>
#include <string>
#include <list>
#include <vector>
@@ -300,7 +301,7 @@ public:
/** Integer list properties
*
*/
class AppExport PropertyIntegerList: public PropertyLists
class AppExport PropertyIntegerList: public PropertyListsT<long>
{
TYPESYSTEM_HEADER();
@@ -318,25 +319,10 @@ public:
*/
virtual ~PropertyIntegerList();
virtual void setSize(int newSize);
virtual int getSize(void) const;
/** Sets the property
*/
void setValue(long);
/// index operator
long operator[] (const int idx) const {return _lValueList.operator[] (idx);}
void set1Value (const int idx, long value){_lValueList.operator[] (idx) = value;}
void setValues (const std::vector<long>& values);
const std::vector<long> &getValues(void) const{return _lValueList;}
virtual const char* getEditorName(void) const
{ return "Gui::PropertyEditor::PropertyIntegerListItem"; }
virtual PyObject *getPyObject(void);
virtual void setPyObject(PyObject *);
virtual void Save (Base::Writer &writer) const;
virtual void Restore(Base::XMLReader &reader);
@@ -345,8 +331,8 @@ public:
virtual void Paste(const Property &from);
virtual unsigned int getMemSize (void) const;
private:
std::vector<long> _lValueList;
protected:
long getPyValue(PyObject *item) const override;
};
/** Integer list properties
@@ -594,7 +580,7 @@ public:
};
class AppExport PropertyFloatList: public PropertyLists
class AppExport PropertyFloatList: public PropertyListsT<double>
{
TYPESYSTEM_HEADER();
@@ -612,29 +598,10 @@ public:
*/
virtual ~PropertyFloatList();
virtual void setSize(int newSize);
virtual int getSize(void) const;
/** Sets the property
*/
void setValue(double);
void setValue (void){}
/// index operator
double operator[] (const int idx) const {return _lValueList.operator[] (idx);}
void set1Value (const int idx, double value){_lValueList.operator[] (idx) = value;}
void setValues (const std::vector<double>& values);
const std::vector<double> &getValues(void) const{return _lValueList;}
virtual const char* getEditorName(void) const
{ return "Gui::PropertyEditor::PropertyFloatListItem"; }
virtual PyObject *getPyObject(void);
virtual void setPyObject(PyObject *);
virtual void Save (Base::Writer &writer) const;
virtual void Restore(Base::XMLReader &reader);
@@ -646,8 +613,8 @@ public:
virtual void Paste(const Property &from);
virtual unsigned int getMemSize (void) const;
private:
std::vector<double> _lValueList;
protected:
double getPyValue(PyObject *item) const override;
};
@@ -672,7 +639,7 @@ public:
*/
virtual ~PropertyString();
void setValue(const char* sString);
virtual void setValue(const char* sString);
void setValue(const std::string &sString);
const char* getValue(void) const;
const std::string& getStrValue(void) const
@@ -754,9 +721,10 @@ public:
{ return "Gui::PropertyEditor::PropertyFontItem"; }
};
class AppExport PropertyStringList: public PropertyLists
class AppExport PropertyStringList: public PropertyListsT<std::string>
{
TYPESYSTEM_HEADER();
typedef PropertyListsT<std::string> inherited;
public:
@@ -772,27 +740,13 @@ public:
*/
virtual ~PropertyStringList();
virtual void setSize(int newSize);
virtual int getSize(void) const;
/** Sets the property
*/
void setValue(const std::string&);
void setValues(const std::vector<std::string>&);
void setValues(const std::list<std::string>&);
/// index operator
const std::string& operator[] (const int idx) const {return _lValueList.operator[] (idx);}
void set1Value (const int idx, const std::string& value){_lValueList.operator[] (idx) = value;}
const std::vector<std::string> &getValues(void) const{return _lValueList;}
using inherited::setValues;
virtual const char* getEditorName(void) const
{ return "Gui::PropertyEditor::PropertyStringListItem"; }
virtual PyObject *getPyObject(void);
virtual void setPyObject(PyObject *);
virtual void Save (Base::Writer &writer) const;
virtual void Restore(Base::XMLReader &reader);
@@ -802,9 +756,8 @@ public:
virtual unsigned int getMemSize (void) const;
private:
std::vector<std::string> _lValueList;
protected:
std::string getPyValue(PyObject *item) const override;
};
/** Bool properties
@@ -854,27 +807,15 @@ private:
/** Bool list properties
*
*/
class AppExport PropertyBoolList : public PropertyLists
class AppExport PropertyBoolList : public PropertyListsT<bool,boost::dynamic_bitset<> >
{
TYPESYSTEM_HEADER();
typedef PropertyListsT<bool,boost::dynamic_bitset<> > inherited;
public:
PropertyBoolList();
virtual ~PropertyBoolList();
virtual void setSize(int newSize);
virtual int getSize(void) const;
/** Sets the property
*/
void setValue(bool);
/// index operator
void set1Value (const int idx, bool value);
void setValues (const boost::dynamic_bitset<>& values);
const boost::dynamic_bitset<> &getValues(void) const{return _lValueList;}
virtual PyObject *getPyObject(void);
virtual void setPyObject(PyObject *);
@@ -885,8 +826,8 @@ public:
virtual void Paste(const Property &from);
virtual unsigned int getMemSize (void) const;
private:
boost::dynamic_bitset<> _lValueList;
protected:
bool getPyValue(PyObject *) const override;
};
@@ -938,7 +879,7 @@ private:
Color _cCol;
};
class AppExport PropertyColorList: public PropertyLists
class AppExport PropertyColorList: public PropertyListsT<Color>
{
TYPESYSTEM_HEADER();
@@ -956,23 +897,7 @@ public:
*/
virtual ~PropertyColorList();
virtual void setSize(int newSize);
virtual int getSize(void) const;
/** Sets the property
*/
void setValue(const Color&);
/// index operator
const Color& operator[] (const int idx) const {return _lValueList.operator[] (idx);}
void set1Value (const int idx, const Color& value){_lValueList.operator[] (idx) = value;}
void setValues (const std::vector<Color>& values);
const std::vector<Color> &getValues(void) const{return _lValueList;}
virtual PyObject *getPyObject(void);
virtual void setPyObject(PyObject *);
virtual void Save (Base::Writer &writer) const;
virtual void Restore(Base::XMLReader &reader);
@@ -983,9 +908,9 @@ public:
virtual Property *Copy(void) const;
virtual void Paste(const Property &from);
virtual unsigned int getMemSize (void) const;
private:
std::vector<Color> _lValueList;
protected:
Color getPyValue(PyObject *) const override;
};
/** Material properties
@@ -1042,7 +967,7 @@ private:
/** Material properties
*/
class AppExport PropertyMaterialList : public PropertyLists
class AppExport PropertyMaterialList : public PropertyListsT<Material>
{
TYPESYSTEM_HEADER();
@@ -1060,23 +985,7 @@ public:
*/
virtual ~PropertyMaterialList();
virtual void setSize(int newSize);
virtual int getSize(void) const;
/** Sets the property
*/
void setValue(const Material&);
/// index operator
const Material& operator[] (const int idx) const { return _lValueList.operator[] (idx); }
void set1Value(const int idx, const Material& value){ _lValueList.operator[] (idx) = value; }
void setValues(const std::vector<Material>& values);
const std::vector<Material> &getValues(void) const{ return _lValueList; }
virtual PyObject *getPyObject(void);
virtual void setPyObject(PyObject *);
virtual void Save(Base::Writer &writer) const;
virtual void Restore(Base::XMLReader &reader);
@@ -1090,11 +999,37 @@ public:
virtual void Paste(const Property &from);
virtual unsigned int getMemSize(void) const;
private:
std::vector<Material> _lValueList;
protected:
Material getPyValue(PyObject *) const override;
};
/** Property for dynamic creation of a FreeCAD persistent object
*
* In Python, this property can be assigned a type string to create a dynamic FreeCAD
* object, and then read back as the Python binding of the newly created object.
*/
class AppExport PropertyPersistentObject: public PropertyString {
TYPESYSTEM_HEADER();
typedef PropertyString inherited;
public:
virtual PyObject *getPyObject(void) override;
virtual void setValue(const char* type) override;
virtual void Save (Base::Writer &writer) const override;
virtual void Restore(Base::XMLReader &reader) override;
virtual Property *Copy(void) const override;
virtual void Paste(const Property &from) override;
virtual unsigned int getMemSize (void) const override;
std::shared_ptr<Base::Persistence> getObject() const {
return _pObject;
}
protected:
std::shared_ptr<Base::Persistence> _pObject;
};
} // namespace App

View File

@@ -48,6 +48,7 @@
#include "ViewProviderExtension.h"
#include <Gui/ViewProviderDocumentObjectPy.h>
FC_LOG_LEVEL_INIT("Gui",true,true)
using namespace Gui;
@@ -97,13 +98,30 @@ const char* ViewProviderDocumentObject::detachFromDocument()
return "";
}
void ViewProviderDocumentObject::onAboutToRemoveProperty(const char* prop)
bool ViewProviderDocumentObject::removeDynamicProperty(const char* name)
{
// transactions of view providers are also managed in App::Document.
App::DocumentObject* docobject = getObject();
App::Document* document = docobject ? docobject->getDocument() : nullptr;
if (document)
document->removePropertyOfObject(this, prop);
document->addOrRemovePropertyOfObject(this, prop, false);
return ViewProvider::removeDynamicProperty(name);
}
App::Property* ViewProviderDocumentObject::addDynamicProperty(
const char* type, const char* name, const char* group, const char* doc,
short attr, bool ro, bool hidden)
{
auto prop = ViewProvider::addDynamicProperty(type,name,group,doc,attr,ro,hidden);
if(prop) {
// transactions of view providers are also managed in App::Document.
App::DocumentObject* docobject = getObject();
App::Document* document = docobject ? docobject->getDocument() : nullptr;
if (document)
document->addOrRemovePropertyOfObject(this, prop, true);
}
return prop;
}
void ViewProviderDocumentObject::onBeforeChange(const App::Property* prop)
@@ -131,8 +149,11 @@ void ViewProviderDocumentObject::onChanged(const App::Property* prop)
}
}
if (pcDocument)
if (pcDocument && !pcDocument->isModified()) {
if(prop)
FC_LOG(prop->getFullName() << " changed");
pcDocument->setModified(true);
}
ViewProvider::onChanged(prop);
}
@@ -314,3 +335,17 @@ PyObject* ViewProviderDocumentObject::getPyObject()
pyViewObject->IncRef();
return pyViewObject;
}
void ViewProviderDocumentObject::onPropertyStatusChanged(
const App::Property &prop, unsigned long oldStatus)
{
(void)oldStatus;
if(!App::Document::isAnyRestoring() && pcObject && pcObject->getDocument())
pcObject->getDocument()->signalChangePropertyEditor(*pcObject->getDocument(),prop);
}
std::string ViewProviderDocumentObject::getFullName() const {
if(pcObject)
return pcObject->getFullName() + ".ViewObject";
return std::string();
}

View File

@@ -89,6 +89,15 @@ public:
virtual void finishRestoring();
//@}
virtual bool removeDynamicProperty(const char* prop) override;
virtual App::Property* addDynamicProperty(
const char* type, const char* name=0,
const char* group=0, const char* doc=0,
short attr=0, bool ro=false, bool hidden=false) override;
virtual std::string getFullName() const override;
protected:
/*! Get the active mdi view of the document this view provider is part of.
@note The returned mdi view doesn't need to be a 3d view but can be e.g.
@@ -127,10 +136,12 @@ protected:
/** @name Transaction handling
*/
//@{
/// \internal get called when removing a property of name \a prop
void onAboutToRemoveProperty(const char* prop);
virtual bool isAttachedToDocument() const;
virtual const char* detachFromDocument();
/// get called when a property status has changed
virtual void onPropertyStatusChanged(const App::Property &prop, unsigned long oldStatus) override;
//@}
protected:
@@ -140,6 +151,7 @@ protected:
private:
std::vector<const char*> aDisplayEnumsArray;
std::vector<std::string> aDisplayModesArray;
bool _UpdatingView;
friend class Document;
};

View File

@@ -112,12 +112,10 @@ public:
ViewProviderPythonFeatureT() : _attached(false) {
ADD_PROPERTY(Proxy,(Py::Object()));
imp = new ViewProviderPythonFeatureImp(this);
props = new App::DynamicProperty(this);
}
/// destructor.
virtual ~ViewProviderPythonFeatureT() {
delete imp;
delete props;
}
// Returns the icon
@@ -292,78 +290,6 @@ public:
}
//@}
/** @name Access properties */
//@{
App::Property* addDynamicProperty(
const char* type, const char* name=0,
const char* group=0, const char* doc=0,
short attr=0, bool ro=false, bool hidden=false) {
return props->addDynamicProperty(type, name, group, doc, attr, ro, hidden);
}
virtual bool removeDynamicProperty(const char* name) {
ViewProviderT::onAboutToRemoveProperty(name);
return props->removeDynamicProperty(name);
}
std::vector<std::string> getDynamicPropertyNames() const {
return props->getDynamicPropertyNames();
}
App::Property *getDynamicPropertyByName(const char* name) const {
return props->getDynamicPropertyByName(name);
}
virtual void addDynamicProperties(const App::PropertyContainer* cont) {
return props->addDynamicProperties(cont);
}
/// get all properties of the class (including parent)
virtual void getPropertyMap(std::map<std::string,App::Property*> &Map) const {
return props->getPropertyMap(Map);
}
/// find a property by its name
virtual App::Property *getPropertyByName(const char* name) const {
return props->getPropertyByName(name);
}
/// get the name of a property
virtual const char* getPropertyName(const App::Property* prop) const {
return props->getPropertyName(prop);
}
//@}
/** @name Property attributes */
//@{
/// get the Type of a Property
short getPropertyType(const App::Property* prop) const {
return props->getPropertyType(prop);
}
/// get the Type of a named Property
short getPropertyType(const char *name) const {
return props->getPropertyType(name);
}
/// get the Group of a Property
const char* getPropertyGroup(const App::Property* prop) const {
return props->getPropertyGroup(prop);
}
/// get the Group of a named Property
const char* getPropertyGroup(const char *name) const {
return props->getPropertyGroup(name);
}
/// get the Group of a Property
const char* getPropertyDocumentation(const App::Property* prop) const {
return props->getPropertyDocumentation(prop);
}
/// get the Group of a named Property
const char* getPropertyDocumentation(const char *name) const {
return props->getPropertyDocumentation(name);
}
//@}
/** @name Property serialization */
//@{
void Save (Base::Writer &writer) const {
props->Save(writer);
}
void Restore(Base::XMLReader &reader) {
props->Restore(reader);
}
//@}
PyObject* getPyObject() {
return ViewProviderT::getPyObject();
@@ -440,7 +366,6 @@ protected:
private:
ViewProviderPythonFeatureImp* imp;
App::DynamicProperty *props;
App::PropertyPythonObject Proxy;
std::string viewerMode;
bool _attached;