diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 624f11f296..48732257cf 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -416,6 +416,8 @@ Document* Application::newDocument(const char * Name, const char * UserName) // connect the signals to the application for the new document + _pActiveDoc->signalBeforeChange.connect(boost::bind(&App::Application::slotBeforeChangeDoc, this, _1, _2)); + _pActiveDoc->signalChanged.connect(boost::bind(&App::Application::slotChangedDoc, this, _1, _2)); _pActiveDoc->signalNewObject.connect(boost::bind(&App::Application::slotNewObject, this, _1)); _pActiveDoc->signalDeletedObject.connect(boost::bind(&App::Application::slotDeletedObject, this, _1)); _pActiveDoc->signalBeforeChangeObject.connect(boost::bind(&App::Application::slotBeforeChangeObject, this, _1, _2)); @@ -978,6 +980,16 @@ std::map Application::getExportFilters(void) const //************************************************************************** // signaling +void Application::slotBeforeChangeDoc(const App::Document& doc, const Property& prop) +{ + this->signalBeforeChangeDoc(doc, prop); +} + +void Application::slotChangedDoc(const App::Document& doc, const Property& prop) +{ + this->signalChangedDoc(doc, prop); +} + void Application::slotNewObject(const App::DocumentObject&O) { this->signalNewObject(O); diff --git a/src/App/Application.h b/src/App/Application.h index 544e363c5c..73c98ce451 100644 --- a/src/App/Application.h +++ b/src/App/Application.h @@ -128,6 +128,10 @@ public: * the signal of a special document connect to the document itself */ //@{ + /// signal before change of doc property + boost::signal signalBeforeChangeDoc; + /// signal on changed doc proeprty + boost::signal signalChangedDoc; /// signal on new Object boost::signal signalNewObject; //boost::signal m_sig; @@ -276,6 +280,8 @@ protected: * This slot get connected to all App::Documents created */ //@{ + void slotBeforeChangeDoc(const App::Document&, const App::Property&); + void slotChangedDoc(const App::Document&, const App::Property&); void slotNewObject(const App::DocumentObject&); void slotDeletedObject(const App::DocumentObject&); void slotBeforeChangeObject(const App::DocumentObject&, const App::Property& Prop); diff --git a/src/App/Document.cpp b/src/App/Document.cpp index 533ba01fdf..cb6db954dc 100644 --- a/src/App/Document.cpp +++ b/src/App/Document.cpp @@ -1098,8 +1098,15 @@ unsigned int Document::getMaxUndoStackSize(void)const return d->UndoMaxStackSize; } +void Document::onBeforeChange(const Property* prop) { + + signalBeforeChange(*this, *prop); +} + void Document::onChanged(const Property* prop) { + signalChanged(*this, *prop); + // the Name property is a label for display purposes if (prop == &Label) { App::GetApplication().signalRelabelDocument(*this); diff --git a/src/App/Document.h b/src/App/Document.h index 18fd49941f..d2f2fdeb67 100644 --- a/src/App/Document.h +++ b/src/App/Document.h @@ -112,6 +112,10 @@ public: /** @name Signals of the document */ //@{ + /// signal before changing an doc property + boost::signal signalBeforeChange; + /// signal on changed doc property + boost::signal signalChanged; /// signal on new Object boost::signal signalNewObject; //boost::signal m_sig; @@ -375,6 +379,7 @@ protected: void writeObjects(const std::vector&, Base::Writer &writer) const; bool saveToFile(const char* filename) const; + void onBeforeChange(const Property* prop); void onChanged(const Property* prop); /// callback from the Document objects before property will be changed void onBeforeChangeProperty(const TransactionalObject *Who, const Property *What); diff --git a/src/App/DocumentObserverPython.cpp b/src/App/DocumentObserverPython.cpp index 9f127b5e0a..27cddbebc5 100644 --- a/src/App/DocumentObserverPython.cpp +++ b/src/App/DocumentObserverPython.cpp @@ -72,6 +72,10 @@ DocumentObserverPython::DocumentObserverPython(const Py::Object& obj) : inst(obj this->connectApplicationRedoDocument = App::GetApplication().signalRedoDocument.connect(boost::bind (&DocumentObserverPython::slotRedoDocument, this, _1)); + this->connectDocumentBeforeChange = App::GetApplication().signalBeforeChangeDoc.connect(boost::bind + (&DocumentObserverPython::slotBeforeChangeDocument, this, _1, _2)); + this->connectDocumentChanged = App::GetApplication().signalChangedDoc.connect(boost::bind + (&DocumentObserverPython::slotChangedDocument, this, _1, _2)); this->connectDocumentCreatedObject = App::GetApplication().signalNewObject.connect(boost::bind (&DocumentObserverPython::slotCreatedObject, this, _1)); this->connectDocumentDeletedObject = App::GetApplication().signalDeletedObject.connect(boost::bind @@ -103,6 +107,8 @@ DocumentObserverPython::~DocumentObserverPython() this->connectApplicationUndoDocument.disconnect(); this->connectApplicationRedoDocument.disconnect(); + this->connectDocumentBeforeChange.disconnect(); + this->connectDocumentChanged.disconnect(); this->connectDocumentCreatedObject.disconnect(); this->connectDocumentDeletedObject.disconnect(); this->connectDocumentBeforeChangeObject.disconnect(); @@ -216,6 +222,52 @@ void DocumentObserverPython::slotRedoDocument(const App::Document& Doc) } } +void DocumentObserverPython::slotBeforeChangeDocument(const App::Document& Doc, const App::Property& Prop) +{ + Base::PyGILStateLocker lock; + try { + if (this->inst.hasAttr(std::string("slotBeforeChangeDocument"))) { + Py::Callable method(this->inst.getAttr(std::string("slotBeforeChangeDocument"))); + Py::Tuple args(2); + args.setItem(0, Py::Object(const_cast(Doc).getPyObject(), true)); + // If a property is touched but not part of a document object then its name is null. + // In this case the slot function must not be called. + const char* prop_name = Doc.getPropertyName(&Prop); + if (prop_name) { + args.setItem(1, Py::String(prop_name)); + method.apply(args); + } + } + } + catch (Py::Exception&) { + Base::PyException e; // extract the Python error text + e.ReportException(); + } +} + +void DocumentObserverPython::slotChangedDocument(const App::Document& Doc, const App::Property& Prop) +{ + Base::PyGILStateLocker lock; + try { + if (this->inst.hasAttr(std::string("slotChangedDocument"))) { + Py::Callable method(this->inst.getAttr(std::string("slotChangedDocument"))); + Py::Tuple args(2); + args.setItem(0, Py::Object(const_cast(Doc).getPyObject(), true)); + // If a property is touched but not part of a document object then its name is null. + // In this case the slot function must not be called. + const char* prop_name = Doc.getPropertyName(&Prop); + if (prop_name) { + args.setItem(1, Py::String(prop_name)); + method.apply(args); + } + } + } + catch (Py::Exception&) { + Base::PyException e; // extract the Python error text + e.ReportException(); + } +} + void DocumentObserverPython::slotCreatedObject(const App::DocumentObject& Obj) { Base::PyGILStateLocker lock; diff --git a/src/App/DocumentObserverPython.h b/src/App/DocumentObserverPython.h index e6a0a772e1..455f3a7625 100644 --- a/src/App/DocumentObserverPython.h +++ b/src/App/DocumentObserverPython.h @@ -59,6 +59,10 @@ private: void slotRelabelDocument(const App::Document& Doc); /** Checks if the given document is activated */ void slotActivateDocument(const App::Document& Doc); + /** The property of an observed document has changed */ + void slotBeforeChangeDocument(const App::Document& Obj, const App::Property& Prop); + /** The property of an observed document has changed */ + void slotChangedDocument(const App::Document& Obj, const App::Property& Prop); /** Checks if a new object was added. */ void slotCreatedObject(const App::DocumentObject& Obj); /** Checks if the given object is about to be removed. */ @@ -93,6 +97,8 @@ private: Connection connectApplicationActivateDocument; Connection connectApplicationUndoDocument; Connection connectApplicationRedoDocument; + Connection connectDocumentBeforeChange; + Connection connectDocumentChanged; Connection connectDocumentCreatedObject; Connection connectDocumentDeletedObject; Connection connectDocumentBeforeChangeObject; diff --git a/src/Mod/Test/Document.py b/src/Mod/Test/Document.py index 8eaff2721a..9be012ee1e 100644 --- a/src/Mod/Test/Document.py +++ b/src/Mod/Test/Document.py @@ -1359,6 +1359,16 @@ class DocumentObserverCases(unittest.TestCase): def slotAbortTransaction(self, doc): self.signal.append('DocAbortTransaction'); self.parameter = doc; + + def slotBeforeChangeDocument(self, doc, prop): + self.signal.append('DocBeforeChange') + self.parameter = doc + self.parameter2 = prop + + def slotChangedDocument(self, doc, prop): + self.signal.append('DocChanged') + self.parameter = doc + self.parameter2 = prop def slotCreatedObject(self, obj): self.signal.append('ObjCreated'); @@ -1393,6 +1403,8 @@ class DocumentObserverCases(unittest.TestCase): self.Doc1 = FreeCAD.newDocument("Observer1"); self.failUnless(self.Obs.signal.pop(0) == 'DocActivated') self.failUnless(self.Obs.signal.pop(0) == 'DocCreated') + self.failUnless(self.Obs.signal.pop(0) == 'DocBeforeChange') + self.failUnless(self.Obs.signal.pop(0) == 'DocChanged') self.failUnless(self.Obs.signal.pop(0) == 'DocRelabled') self.failUnless(self.Obs.parameter is self.Doc1) self.failUnless(not self.Obs.signal) @@ -1400,6 +1412,8 @@ class DocumentObserverCases(unittest.TestCase): self.Doc2 = FreeCAD.newDocument("Observer2"); self.failUnless(self.Obs.signal.pop(0) == 'DocActivated') self.failUnless(self.Obs.signal.pop(0) == 'DocCreated') + self.failUnless(self.Obs.signal.pop(0) == 'DocBeforeChange') + self.failUnless(self.Obs.signal.pop(0) == 'DocChanged') self.failUnless(self.Obs.signal.pop(0) == 'DocRelabled') self.failUnless(self.Obs.parameter is self.Doc2) self.failUnless(not self.Obs.signal) @@ -1440,6 +1454,12 @@ class DocumentObserverCases(unittest.TestCase): self.failUnless(self.Obs.parameter is self.Doc2) self.failUnless(not self.Obs.signal) + self.Doc1.Comment = 'test comment' + self.failUnless(self.Obs.signal.pop(0) == 'DocBeforeChange') + self.failUnless(self.Obs.signal.pop(0) == 'DocChanged') + self.failUnless(self.Obs.parameter is self.Doc1) + self.failUnless(self.Obs.parameter2 == 'Comment') + FreeCAD.closeDocument('Observer2') self.failUnless(self.Obs.signal.pop() == 'DocDeleted') self.failUnless(self.Obs.parameter is self.Doc2)