diff --git a/src/App/Document.cpp b/src/App/Document.cpp index d3c60aab9..61368dec0 100644 --- a/src/App/Document.cpp +++ b/src/App/Document.cpp @@ -1191,6 +1191,7 @@ void Document::restore (void) // reset all touched for (std::map::iterator It= d->objectMap.begin();It!=d->objectMap.end();++It) { + It->second->connectRelabelSignals(); It->second->onDocumentRestored(); It->second->purgeTouched(); } @@ -1362,6 +1363,29 @@ Document::getDependencyList(const std::vector& objs) const return ary; } +/** + * @brief Signal that object identifiers, typically a property or document object has been renamed. + * + * This function iterates through all document object in the document, and calls its + * renameObjectIdentifiers functions. + * + * @param paths Map with current and new names + */ + +void Document::renameObjectIdentifiers(const std::map &paths) +{ + std::map extendedPaths; + + std::map::const_iterator it = paths.begin(); + while (it != paths.end()) { + extendedPaths[it->first.canonicalPath()] = it->second.canonicalPath(); + ++it; + } + + for (std::vector::iterator it = d->objectArray.begin(); it != d->objectArray.end(); ++it) + (*it)->renameObjectIdentifiers(extendedPaths); +} + void Document::_rebuildDependencyList(void) { d->VertexObjectList.clear(); diff --git a/src/App/Document.h b/src/App/Document.h index af66cbcfc..8f10cb4a6 100644 --- a/src/App/Document.h +++ b/src/App/Document.h @@ -279,6 +279,9 @@ public: //void setChanged(DocumentObject* change); //@} + /// Function called to signal that an object identifier has been renamed + void renameObjectIdentifiers(const std::map & paths); + virtual PyObject *getPyObject(void); friend class Application; diff --git a/src/App/DocumentObject.cpp b/src/App/DocumentObject.cpp index b9a0a969e..1c8a26fee 100644 --- a/src/App/DocumentObject.cpp +++ b/src/App/DocumentObject.cpp @@ -33,6 +33,9 @@ #include "DocumentObjectPy.h" #include "DocumentObjectGroup.h" #include "PropertyLinks.h" +#include "PropertyExpressionEngine.h" +#include +#include using namespace App; @@ -46,10 +49,11 @@ DocumentObjectExecReturn *DocumentObject::StdReturn = 0; //=========================================================================== DocumentObject::DocumentObject(void) - : _pDoc(0),pcNameInDocument(0) + : ExpressionEngine(),_pDoc(0),pcNameInDocument(0) { // define Label of type 'Output' to avoid being marked as touched after relabeling ADD_PROPERTY_TYPE(Label,("Unnamed"),"Base",Prop_Output,"User name of the object (UTF8)"); + ADD_PROPERTY_TYPE(ExpressionEngine,(),"Base",Prop_Hidden,"Property expressions"); } DocumentObject::~DocumentObject(void) @@ -88,7 +92,7 @@ App::DocumentObjectExecReturn *DocumentObject::recompute(void) DocumentObjectExecReturn *DocumentObject::execute(void) { - return DocumentObject::StdReturn; + return StdReturn; } short DocumentObject::mustExecute(void) const @@ -148,6 +152,10 @@ std::vector DocumentObject::getOutList(void) const ret.push_back(static_cast(*It)->getValue()); } } + + // Get document objects that this document object relies on + ExpressionEngine.getDocumentObjectDeps(ret); + return ret; } @@ -227,8 +235,87 @@ void DocumentObject::touch(void) StatusBits.set(0); } +/** + * @brief Check whether the document object is touched or not. + * @return true if document object is touched, false if not. + */ + +bool DocumentObject::isTouched() const +{ + return ExpressionEngine.isTouched() || StatusBits.test(0); +} + void DocumentObject::Save (Base::Writer &writer) const { writer.ObjectName = this->getNameInDocument(); App::PropertyContainer::Save(writer); } + +/** + * @brief Associate the expression \expr with the object identifier \a path in this document object. + * @param path Target object identifier for the result of the expression + * @param expr Expression tree + * @param comment Optional comment describing the expression + */ + +void DocumentObject::setExpression(const ObjectIdentifier &path, boost::shared_ptr expr, const char * comment) +{ + ExpressionEngine.setValue(path, expr, comment); + connectRelabelSignals(); +} + +/** + * @brief Get expression information associated with \a path. + * @param path Object identifier + * @return Expression info, containing expression and optional comment. + */ + +const PropertyExpressionEngine::ExpressionInfo DocumentObject::getExpression(const ObjectIdentifier &path) const +{ + boost::any value = ExpressionEngine.getValue(path); + + if (value.type() == typeid(PropertyExpressionEngine::ExpressionInfo)) + return boost::any_cast(value); + else + return PropertyExpressionEngine::ExpressionInfo(); +} + +/** + * @brief Invoke ExpressionEngine's renameObjectIdentifier, to possibly rewrite expressions using + * the \a paths map with current and new identifiers. + * + * @param paths + */ + +void DocumentObject::renameObjectIdentifiers(const std::map &paths) +{ + ExpressionEngine.renameObjectIdentifiers(paths); +} + +/** + * @brief Helper function that sets up a signal to track document object renames. + */ + +void DocumentObject::connectRelabelSignals() +{ + // Only keep signal if the ExpressionEngine has at least one expression + if (ExpressionEngine.numExpressions() > 0) { + + // Not already connected? + if (!onRelabledObjectConnection.connected()) + onRelabledObjectConnection = getDocument()->signalRenamedObject.connect(boost::bind(&PropertyExpressionEngine::slotObjectRenamed, &ExpressionEngine, _1)); + + try { + // Crude method to resolve all expression dependencies + ExpressionEngine.execute(); + } + catch (...) { + // Ignore any error + } + } + else { + // Disconnect signals; nothing to track now + onRelabledObjectConnection.disconnect(); + onRelabledDocumentConnection.disconnect(); + } +} diff --git a/src/App/DocumentObject.h b/src/App/DocumentObject.h index aa6c2ccdd..52973c30b 100644 --- a/src/App/DocumentObject.h +++ b/src/App/DocumentObject.h @@ -26,18 +26,20 @@ #include #include +#include #include #include #include - +#include namespace App { class Document; class DocumentObjectGroup; class DocumentObjectPy; +class Expression; enum ObjectStatus { Touch = 0, @@ -79,6 +81,7 @@ class AppExport DocumentObject: public App::PropertyContainer public: PropertyString Label; + PropertyExpressionEngine ExpressionEngine; /// returns the type name of the ViewProvider virtual const char* getViewProviderName(void) const { @@ -99,7 +102,7 @@ public: /// set this feature touched (cause recomputation on depndend features) void touch(void); /// test if this feature is touched - bool isTouched(void) const {return StatusBits.test(0);} + bool isTouched(void) const; /// reset this feature touched void purgeTouched(void){StatusBits.reset(0);setPropertyStatus(0,false);} /// set this feature to error @@ -160,6 +163,16 @@ public: virtual void Save (Base::Writer &writer) const; + /* Expression support */ + + virtual void setExpression(const ObjectIdentifier & path, boost::shared_ptr expr, const char *comment = 0); + + virtual const PropertyExpressionEngine::ExpressionInfo getExpression(const ObjectIdentifier &path) const; + + virtual void renameObjectIdentifiers(const std::map & paths); + + virtual void connectRelabelSignals(); + const std::string & getOldLabel() const { return oldLabel; } protected: @@ -207,6 +220,10 @@ protected: // attributes /// pointer to the document this object belongs to App::Document* _pDoc; + // Connections to track relabeling of document and document objects + boost::BOOST_SIGNALS_NAMESPACE::scoped_connection onRelabledDocumentConnection; + boost::BOOST_SIGNALS_NAMESPACE::scoped_connection onRelabledObjectConnection; + /// Old label; used for renaming expressions std::string oldLabel; diff --git a/src/App/DocumentObjectPy.xml b/src/App/DocumentObjectPy.xml index 66d6b052f..4d8b2e972 100644 --- a/src/App/DocumentObjectPy.xml +++ b/src/App/DocumentObjectPy.xml @@ -23,6 +23,11 @@ Mark the object as unchanged + + + Register an expression for a property + + A list of all objects this object links to. diff --git a/src/App/DocumentObjectPyImp.cpp b/src/App/DocumentObjectPyImp.cpp index bbdf351e0..be010e682 100644 --- a/src/App/DocumentObjectPyImp.cpp +++ b/src/App/DocumentObjectPyImp.cpp @@ -28,6 +28,7 @@ // inclusion of the generated files (generated out of DocumentObjectPy.xml) #include "DocumentObjectPy.h" #include "DocumentObjectPy.cpp" +#include "Expression.h" using namespace App; @@ -152,6 +153,31 @@ Py::List DocumentObjectPy::getOutList(void) const return ret; } +PyObject* DocumentObjectPy::setExpression(PyObject * args) +{ + char * path = NULL; + PyObject * expr; + char * comment = 0; + + if (!PyArg_ParseTuple(args, "sO|s", &path, &expr, &comment)) // convert args: Python->C + return NULL; // NULL triggers exception + + App::ObjectIdentifier p(ObjectIdentifier::parse(getDocumentObjectPtr(), path)); + + if (Py::Object(expr).isNone()) + getDocumentObjectPtr()->setExpression(p, boost::shared_ptr()); + else if (PyString_Check(expr)) { + const char * exprStr = PyString_AsString(expr); + boost::shared_ptr shared_expr(ExpressionParser::parse(getDocumentObjectPtr(), exprStr)); + + getDocumentObjectPtr()->setExpression(p, shared_expr, comment); + } + else + throw Py::TypeError("String or None expected."); + Py_Return; +} + + PyObject *DocumentObjectPy::getCustomAttributes(const char* /*attr*/) const { return 0;