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:
@@ -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();
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -73,6 +73,8 @@
|
||||
#include <bitset>
|
||||
#include <exception>
|
||||
#include <random>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
|
||||
// Boost
|
||||
#include <boost/signals2.hpp>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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_) \
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user