From 4c47fb952502718b756331e39747c92c82de48a6 Mon Sep 17 00:00:00 2001 From: mpv Date: Thu, 10 Apr 2014 13:21:32 +0400 Subject: [PATCH] Implementation of features cashing and undo/redo functionality in document. --- src/Model/Model_Application.cxx | 9 +- src/Model/Model_Application.h | 2 + src/Model/Model_Document.cxx | 198 ++++++++++++++++++++++++++++---- src/Model/Model_Document.h | 40 ++++--- src/Model/Model_Iterator.cxx | 9 +- src/Model/Model_Iterator.h | 18 +-- src/Model/Model_Object.h | 18 +-- src/ModelAPI/ModelAPI_Feature.h | 16 +-- 8 files changed, 244 insertions(+), 66 deletions(-) diff --git a/src/Model/Model_Application.cxx b/src/Model/Model_Application.cxx index aab352e75..260db7d44 100644 --- a/src/Model/Model_Application.cxx +++ b/src/Model/Model_Application.cxx @@ -8,6 +8,8 @@ IMPLEMENT_STANDARD_HANDLE(Model_Application, TDocStd_Application) IMPLEMENT_STANDARD_RTTIEXT(Model_Application, TDocStd_Application) +using namespace std; + static Handle_Model_Application TheApplication = new Model_Application; //======================================================================= @@ -23,7 +25,7 @@ Handle(Model_Application) Model_Application::getApplication() //function : getDocument //purpose : //======================================================================= -std::shared_ptr Model_Application::getDocument(std::string theDocID) +std::shared_ptr Model_Application::getDocument(string theDocID) { if (myDocs.find(theDocID) != myDocs.end()) return myDocs[theDocID]; @@ -33,6 +35,11 @@ std::shared_ptr Model_Application::getDocument(std::string theDo return aNew; } +void Model_Application::deleteDocument(string theDocID) +{ + myDocs.erase(theDocID); +} + //======================================================================= //function : OCAFApp_Application //purpose : diff --git a/src/Model/Model_Application.h b/src/Model/Model_Application.h index 75aa172af..998aba08b 100644 --- a/src/Model/Model_Application.h +++ b/src/Model/Model_Application.h @@ -31,6 +31,8 @@ public: MODEL_EXPORT static Handle_Model_Application getApplication(); //! Returns the main document (on first call creates it) by the string identifier MODEL_EXPORT std::shared_ptr getDocument(std::string theDocID); + //! Deletes the document from the application + MODEL_EXPORT void deleteDocument(std::string theDocID); public: // Redefined OCAF methods diff --git a/src/Model/Model_Document.cxx b/src/Model/Model_Document.cxx index b7f195242..9eebe15d1 100644 --- a/src/Model/Model_Document.cxx +++ b/src/Model/Model_Document.cxx @@ -12,6 +12,7 @@ #include #include +#include static const int UNDO_LIMIT = 10; // number of possible undo operations @@ -102,55 +103,104 @@ bool Model_Document::save(const char* theFileName) void Model_Document::close() { + // close all subs + set::iterator aSubIter = mySubs.begin(); + for(; aSubIter != mySubs.end(); aSubIter++) + subDocument(*aSubIter)->close(); + mySubs.clear(); + // close this myDoc->Close(); + Model_Application::getApplication()->deleteDocument(myID); } void Model_Document::startOperation() { + // new command for this myDoc->NewCommand(); + // new command for all subs + set::iterator aSubIter = mySubs.begin(); + for(; aSubIter != mySubs.end(); aSubIter++) + subDocument(*aSubIter)->startOperation(); } void Model_Document::finishOperation() { - myDoc->CommitCommand(); + // returns false if delta is empty and no transaction was made + myIsEmptyTr[myTransactionsAfterSave] = !myDoc->CommitCommand(); myTransactionsAfterSave++; + // finish for all subs + set::iterator aSubIter = mySubs.begin(); + for(; aSubIter != mySubs.end(); aSubIter++) + subDocument(*aSubIter)->finishOperation(); } void Model_Document::abortOperation() { myDoc->AbortCommand(); + // abort for all subs + set::iterator aSubIter = mySubs.begin(); + for(; aSubIter != mySubs.end(); aSubIter++) + subDocument(*aSubIter)->abortOperation(); } bool Model_Document::isOperation() { + // operation is opened for all documents: no need to check subs return myDoc->HasOpenCommand() == Standard_True ; } bool Model_Document::isModified() { - return myTransactionsAfterSave != 0; + // is modified if at least one operation was commited and not undoed + return myTransactionsAfterSave > 0; } bool Model_Document::canUndo() { - return myDoc->GetAvailableUndos() > 0; + if (myDoc->GetAvailableUndos() > 0) + return true; + // check other subs contains operation that can be undoed + set::iterator aSubIter = mySubs.begin(); + for(; aSubIter != mySubs.end(); aSubIter++) + if (subDocument(*aSubIter)->canUndo()) + return true; + return false; } void Model_Document::undo() { - myDoc->Undo(); myTransactionsAfterSave--; + if (!myIsEmptyTr[myTransactionsAfterSave]) + myDoc->Undo(); + synchronizeFeatures(); + // undo for all subs + set::iterator aSubIter = mySubs.begin(); + for(; aSubIter != mySubs.end(); aSubIter++) + subDocument(*aSubIter)->undo(); } bool Model_Document::canRedo() { - return myDoc->GetAvailableRedos() > 0; + if (myDoc->GetAvailableRedos() > 0) + return true; + // check other subs contains operation that can be redoed + set::iterator aSubIter = mySubs.begin(); + for(; aSubIter != mySubs.end(); aSubIter++) + if (subDocument(*aSubIter)->canRedo()) + return true; + return false; } void Model_Document::redo() { - myDoc->Redo(); + if (!myIsEmptyTr[myTransactionsAfterSave]) + myDoc->Redo(); myTransactionsAfterSave++; + synchronizeFeatures(); + // redo for all subs + set::iterator aSubIter = mySubs.begin(); + for(; aSubIter != mySubs.end(); aSubIter++) + subDocument(*aSubIter)->redo(); } shared_ptr Model_Document::addFeature(string theID) @@ -166,7 +216,8 @@ shared_ptr Model_Document::addFeature(string theID) void Model_Document::addFeature(const std::shared_ptr theFeature) { - TDF_Label aGroupLab = groupLabel(theFeature->getGroup()); + const std::string& aGroup = theFeature->getGroup(); + TDF_Label aGroupLab = groupLabel(aGroup); TDF_Label anObjLab = aGroupLab.NewChild(); std::shared_ptr aData(new Model_Object); aData->setLabel(anObjLab); @@ -176,6 +227,9 @@ void Model_Document::addFeature(const std::shared_ptr theFeatu theFeature->initAttributes(); // keep the feature ID to restore document later correctly TDataStd_Comment::Set(anObjLab, theFeature->getKind().c_str()); + // put index of the feature in the group in the tree + TDataStd_Integer::Set(anObjLab, myFeatures[aGroup].size()); + myFeatures[aGroup].push_back(theFeature); // event: model is updated static Event_ID anEvent = Event_Loop::eventByName(EVENT_FEATURE_UPDATED); @@ -185,40 +239,45 @@ void Model_Document::addFeature(const std::shared_ptr theFeatu shared_ptr Model_Document::feature(TDF_Label& theLabel) { - Handle(TDataStd_Comment) aFeatureID; - if (theLabel.FindAttribute(TDataStd_Comment::GetID(), aFeatureID)) { - string anID(TCollection_AsciiString(aFeatureID->Get()).ToCString()); - std::shared_ptr aResult = - Model_PluginManager::get()->createFeature(anID); - std::shared_ptr aData(new Model_Object); - aData->setLabel(theLabel); - aData->setDocument(Model_Application::getApplication()->getDocument(myID)); - aResult->setData(aData); - aResult->initAttributes(); - return aResult; + Handle(TDataStd_Integer) aFeatureIndex; + if (theLabel.FindAttribute(TDataStd_Integer::GetID(), aFeatureIndex)) { + Handle(TDataStd_Comment) aGroupID; + if (theLabel.Father().FindAttribute(TDataStd_Comment::GetID(), aGroupID)) { + string aGroup = TCollection_AsciiString(aGroupID->Get()).ToCString(); + return myFeatures[aGroup][aFeatureIndex->Get()]; + } } return std::shared_ptr(); // not found } int Model_Document::featureIndex(shared_ptr theFeature) { - if (theFeature->data()->document().get() != this) + if (theFeature->data()->document().get() != this) { return theFeature->data()->document()->featureIndex(theFeature); - shared_ptr anIter(featuresIterator(theFeature->getGroup())); - for(int anIndex = 0; anIter->more(); anIter->next(), anIndex++) - if (anIter->is(theFeature)) - return anIndex; + } + shared_ptr aData = dynamic_pointer_cast(theFeature->data()); + Handle(TDataStd_Integer) aFeatureIndex; + if (aData->label().FindAttribute(TDataStd_Integer::GetID(), aFeatureIndex)) { + return aFeatureIndex->Get(); + } return -1; // not found } shared_ptr Model_Document::subDocument(string theDocID) { + // just store sub-document identifier here to manage it later + if (mySubs.find(theDocID) == mySubs.end()) + mySubs.insert(theDocID); return Model_Application::getApplication()->getDocument(theDocID); } shared_ptr Model_Document::featuresIterator(const string theGroup) { shared_ptr aThis(Model_Application::getApplication()->getDocument(myID)); + // create an empty iterator for not existing group + // (to avoidance of attributes management outside the transaction) + if (myGroups.find(theGroup) == myGroups.end()) + return shared_ptr(new Model_Iterator()); return shared_ptr(new Model_Iterator(aThis, groupLabel(theGroup))); } @@ -240,6 +299,11 @@ Model_Document::Model_Document(const std::string theID) { myDoc->SetUndoLimit(UNDO_LIMIT); myTransactionsAfterSave = 0; + // to avoid creation of tag outside of the transaction (by iterator, for example) + /* + if (!myDoc->Main().FindChild(TAG_OBJECTS).IsAttribute(TDF_TagSource::GetID())) + TDataStd_Comment::Set(myDoc->Main().FindChild(TAG_OBJECTS).NewChild(), ""); + */ } TDF_Label Model_Document::groupLabel(const string theGroup) @@ -247,6 +311,9 @@ TDF_Label Model_Document::groupLabel(const string theGroup) if (myGroups.find(theGroup) == myGroups.end()) { myGroups[theGroup] = myDoc->Main().FindChild(TAG_OBJECTS).NewChild(); myGroupsNames.push_back(theGroup); + // set to the group label the group idntifier to restore on "open" + TDataStd_Comment::Set(myGroups[theGroup], theGroup.c_str()); + myFeatures[theGroup] = vector >(); } return myGroups[theGroup]; } @@ -279,6 +346,91 @@ void Model_Document::setUniqueName( theFeature->data()->setName(aName); } +void Model_Document::synchronizeFeatures() +{ + // iterate groups labels + TDF_ChildIDIterator aGroupsIter(myDoc->Main().FindChild(TAG_OBJECTS), + TDataStd_Comment::GetID(), Standard_False); + vector::iterator aGroupNamesIter = myGroupsNames.begin(); + for(; aGroupsIter.More() && aGroupNamesIter != myGroupsNames.end(); + aGroupsIter.Next(), aGroupNamesIter++) { + string aGroupName = TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast( + aGroupsIter.Value())->Get()).ToCString(); + if (*aGroupNamesIter != aGroupName) + break; // all since there is a change this must be recreated from scratch + } + // delete all groups left after the data model groups iteration + while(aGroupNamesIter != myGroupsNames.end()) { + myFeatures.erase(*aGroupNamesIter); + myGroups.erase(*aGroupNamesIter); + aGroupNamesIter = myGroupsNames.erase(aGroupNamesIter); + } + // create new groups basing on the following data model update + for(; aGroupsIter.More(); aGroupsIter.Next()) { + string aGroupName = TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast( + aGroupsIter.Value())->Get()).ToCString(); + myGroupsNames.push_back(aGroupName); + myGroups[aGroupName] = aGroupsIter.Value()->Label(); + myFeatures[aGroupName] = vector >(); + } + // update features group by group + aGroupsIter.Initialize(myDoc->Main().FindChild(TAG_OBJECTS), + TDataStd_Comment::GetID(), Standard_False); + for(; aGroupsIter.More(); aGroupsIter.Next()) { + string aGroupName = TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast( + aGroupsIter.Value())->Get()).ToCString(); + // iterate features in internal container + vector >& aFeatures = myFeatures[aGroupName]; + vector >::iterator aFIter = aFeatures.begin(); + // and in parallel iterate labels of features + TDF_ChildIDIterator aFLabIter( + aGroupsIter.Value()->Label(), TDataStd_Comment::GetID(), Standard_False); + while(aFIter != aFeatures.end() || aFLabIter.More()) { + static const int INFINITE_TAG = MAXINT; // no label means that it exists somwhere in infinite + int aFeatureTag = INFINITE_TAG; + if (aFIter != aFeatures.end()) { // existing tag for feature + shared_ptr aData = dynamic_pointer_cast((*aFIter)->data()); + aFeatureTag = aData->label().Tag(); + } + int aDSTag = INFINITE_TAG; + if (aFLabIter.More()) { // next label in DS is existing + aDSTag = aFLabIter.Value()->Label().Tag(); + } + if (aDSTag > aFeatureTag) { // feature is removed + aFIter = aFeatures.erase(aFIter); + } else if (aDSTag < aFeatureTag) { // a new feature is inserted + // create a feature + shared_ptr aFeature = ModelAPI_PluginManager::get()->createFeature( + TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast( + aFLabIter.Value())->Get()).ToCString()); + + std::shared_ptr aData(new Model_Object); + TDF_Label aLab = aFLabIter.Value()->Label(); + aData->setLabel(aLab); + aData->setDocument(Model_Application::getApplication()->getDocument(myID)); + aFeature->setData(aData); + aFeature->initAttributes(); + // event: model is updated + static Event_ID anEvent = Event_Loop::eventByName(EVENT_FEATURE_UPDATED); + ModelAPI_FeatureUpdatedMessage aMsg(aFeature); + Event_Loop::loop()->send(aMsg); + + if (aFIter == aFeatures.end()) { + aFeatures.push_back(aFeature); + aFIter = aFeatures.end(); + } else { + aFIter++; + aFeatures.insert(aFIter, aFeature); + } + // feature for this label is added, so go to the next label + aFLabIter.Next(); + } else { // nothing is changed, both iterators are incremented + aFIter++; + aFLabIter.Next(); + } + } + } +} ModelAPI_FeatureUpdatedMessage::ModelAPI_FeatureUpdatedMessage( shared_ptr theFeature) diff --git a/src/Model/Model_Document.h b/src/Model/Model_Document.h index 6dd9e4396..ec5de4b9f 100644 --- a/src/Model/Model_Document.h +++ b/src/Model/Model_Document.h @@ -11,6 +11,7 @@ #include #include +#include class Handle_Model_Document; @@ -31,35 +32,36 @@ public: //! \param theFileName full name of the file to load //! \param theStudyID identifier of the SALOME study to associate with loaded file //! \returns true if file was loaded successfully - MODEL_EXPORT bool load(const char* theFileName); + MODEL_EXPORT virtual bool load(const char* theFileName); //! Saves the OCAF document to the file. //! \param theFileName full name of the file to store //! \returns true if file was stored successfully - MODEL_EXPORT bool save(const char* theFileName); + MODEL_EXPORT virtual bool save(const char* theFileName); //! Removes document data - MODEL_EXPORT void close(); + MODEL_EXPORT virtual void close(); //! Starts a new operation (opens a tansaction) - MODEL_EXPORT void startOperation(); + MODEL_EXPORT virtual void startOperation(); //! Finishes the previously started operation (closes the transaction) - MODEL_EXPORT void finishOperation(); + //! Returns true if transaction in this document is not empty and really was performed + MODEL_EXPORT virtual void finishOperation(); //! Aborts the operation - MODEL_EXPORT void abortOperation(); + MODEL_EXPORT virtual void abortOperation(); //! Returns true if operation has been started, but not yet finished or aborted - MODEL_EXPORT bool isOperation(); + MODEL_EXPORT virtual bool isOperation(); //! Returns true if document was modified (since creation/opening) - MODEL_EXPORT bool isModified(); + MODEL_EXPORT virtual bool isModified(); //! Returns True if there are available Undos - MODEL_EXPORT bool canUndo(); + MODEL_EXPORT virtual bool canUndo(); //! Undoes last operation - MODEL_EXPORT void undo(); + MODEL_EXPORT virtual void undo(); //! Returns True if there are available Redos - MODEL_EXPORT bool canRedo(); + MODEL_EXPORT virtual bool canRedo(); //! Redoes last operation - MODEL_EXPORT void redo(); + MODEL_EXPORT virtual void redo(); //! Adds to the document the new feature of the given feature id //! \param creates feature and puts it in the document @@ -101,6 +103,9 @@ protected: //! Adds to the document the new feature void addFeature(const std::shared_ptr theFeature); + //! Synchronizes myGroups, myGroupsNames, myFeatures and mySubs list with the updated document + void synchronizeFeatures(); + //! Creates new document with binary file format Model_Document(const std::string theID); @@ -109,9 +114,16 @@ protected: private: std::string myID; ///< identifier of the document in the application Handle_TDocStd_Document myDoc; ///< OCAF document - int myTransactionsAfterSave; ///< number of transactions after the last "save" call, used for "IsModified" method - std::map myGroups; ///< root labels of the features groups identified by names + /// number of transactions after the last "save" call, used for "IsModified" method + int myTransactionsAfterSave; + /// root labels of the features groups identified by names + std::map myGroups; std::vector myGroupsNames; ///< names of added groups to the document + /// Features managed by this document: by group name + std::map > > myFeatures; + std::set mySubs; ///< set of identifiers of sub-documents of this document + /// transaction indexes (related to myTransactionsAfterSave) which were empty in this doc + std::map myIsEmptyTr; }; /// Event ID that model is updated diff --git a/src/Model/Model_Iterator.cxx b/src/Model/Model_Iterator.cxx index 4e2cf0942..42ed7309f 100644 --- a/src/Model/Model_Iterator.cxx +++ b/src/Model/Model_Iterator.cxx @@ -18,7 +18,7 @@ void Model_Iterator::next() bool Model_Iterator::more() { - return myIter.More(); + return myIter.More() == Standard_True; } shared_ptr Model_Iterator::current() @@ -53,11 +53,14 @@ int Model_Iterator::numIterationsLeft() bool Model_Iterator::is(std::shared_ptr theFeature) { - return myIter.Value()->Label() == - dynamic_pointer_cast(theFeature->data())->label(); + return (myIter.Value()->Label() == + dynamic_pointer_cast(theFeature->data())->label()) == Standard_True; } +Model_Iterator::Model_Iterator() +{ +} Model_Iterator::Model_Iterator(std::shared_ptr theDoc, TDF_Label theLab) : myDoc(theDoc), myIter(theLab, TDataStd_Comment::GetID(), Standard_False) diff --git a/src/Model/Model_Iterator.h b/src/Model/Model_Iterator.h index 80fc76caf..b28423bcf 100644 --- a/src/Model/Model_Iterator.h +++ b/src/Model/Model_Iterator.h @@ -18,30 +18,32 @@ class Model_Document; * (see method featuresIterator). */ -class MODEL_EXPORT Model_Iterator : public ModelAPI_Iterator +class Model_Iterator : public ModelAPI_Iterator { std::shared_ptr myDoc; ///< the document of iterated objects TDF_ChildIDIterator myIter; ///< iterator of the features-labels public: /// Iterates to the next feature - virtual void next(); + MODEL_EXPORT virtual void next(); /// Returns true if the current iteration is valid and next iteration is possible - virtual bool more(); + MODEL_EXPORT virtual bool more(); /// Returns the currently iterated feature - virtual std::shared_ptr current(); + MODEL_EXPORT virtual std::shared_ptr current(); /// Returns the kind of the current feature (faster than Current()->getKind()) - virtual std::string currentKind(); + MODEL_EXPORT virtual std::string currentKind(); /// Returns the name of the current feature (faster than Current()->getName()) - virtual std::string currentName(); + MODEL_EXPORT virtual std::string currentName(); /// Don't changes the current position of iterator. Not fast: iterates the left items. /// \returns number of left iterations - virtual int numIterationsLeft(); + MODEL_EXPORT virtual int numIterationsLeft(); /// Compares the current feature with the given one /// \returns true if given feature equals to the current one - virtual bool is(std::shared_ptr theFeature); + MODEL_EXPORT virtual bool is(std::shared_ptr theFeature); protected: + /// Creates an empty iterator that alway returns More false + Model_Iterator(); /// Initializes iterator /// \param theDoc document where the iteration is performed /// \param theLab label of the features group to iterate diff --git a/src/Model/Model_Object.h b/src/Model/Model_Object.h index 6b1f785f4..9a778c99d 100644 --- a/src/Model/Model_Object.h +++ b/src/Model/Model_Object.h @@ -19,7 +19,7 @@ class ModelAPI_Attribute; * to get/set attributes from the document and compute result of an operation. */ -class MODEL_EXPORT Model_Object: public ModelAPI_Object +class Model_Object: public ModelAPI_Object { TDF_Label myLab; ///< label of the feature in the document /// All attributes of the object identified by the attribute ID @@ -36,28 +36,28 @@ class MODEL_EXPORT Model_Object: public ModelAPI_Object public: /// Returns the name of the feature visible by the user in the object browser - virtual std::string getName(); + MODEL_EXPORT virtual std::string getName(); /// Defines the name of the feature visible by the user in the object browser - virtual void setName(std::string theName); + MODEL_EXPORT virtual void setName(std::string theName); /// Returns the attribute that references to another document - virtual std::shared_ptr docRef(const std::string theID); + MODEL_EXPORT virtual std::shared_ptr docRef(const std::string theID); /// Returns the attribute that contains real value with double precision - virtual std::shared_ptr real(const std::string theID); + MODEL_EXPORT virtual std::shared_ptr real(const std::string theID); /// Initializes object by the attributes: must be called just after the object is created /// for each attribute of the object /// \param theID identifier of the attribute that can be referenced by this ID later /// \param theAttrType type of the created attribute (received from the type method) - virtual void addAttribute(std::string theID, std::string theAttrType); + MODEL_EXPORT virtual void addAttribute(std::string theID, std::string theAttrType); /// Returns the document of this data - virtual std::shared_ptr document() {return myDoc;} + MODEL_EXPORT virtual std::shared_ptr document() {return myDoc;} /// Puts feature to the document data sub-structure - void setLabel(TDF_Label& theLab); + MODEL_EXPORT void setLabel(TDF_Label& theLab); /// Sets the document of this data - virtual void setDocument(const std::shared_ptr& theDoc) {myDoc = theDoc;} + MODEL_EXPORT virtual void setDocument(const std::shared_ptr& theDoc) {myDoc = theDoc;} }; #endif diff --git a/src/ModelAPI/ModelAPI_Feature.h b/src/ModelAPI/ModelAPI_Feature.h index fd19221b0..1b166b600 100644 --- a/src/ModelAPI/ModelAPI_Feature.h +++ b/src/ModelAPI/ModelAPI_Feature.h @@ -19,29 +19,29 @@ class ModelAPI_Document; * \brief Functionality of the model object: to update result, * to initialize attributes, etc. */ -class MODELAPI_EXPORT ModelAPI_Feature +class ModelAPI_Feature { std::shared_ptr myData; ///< manager of the data model of a feature public: /// Returns the kind of a feature (like "Point") - virtual const std::string& getKind() = 0; + MODELAPI_EXPORT virtual const std::string& getKind() = 0; /// Returns to which group in the document must be added feature - virtual const std::string& getGroup() = 0; + MODELAPI_EXPORT virtual const std::string& getGroup() = 0; /// Request for initialization of data model of the feature: adding all attributes - virtual void initAttributes() = 0; + MODELAPI_EXPORT virtual void initAttributes() = 0; /// Computes or recomputes the result - virtual void execute() = 0; + MODELAPI_EXPORT virtual void execute() = 0; /// Returns the data manager of this feature - virtual std::shared_ptr data() {return myData;} + MODELAPI_EXPORT virtual std::shared_ptr data() {return myData;} /// Must return document where the new feature must be added to /// By default it is current document - virtual std::shared_ptr documentToAdd() + MODELAPI_EXPORT virtual std::shared_ptr documentToAdd() {return ModelAPI_PluginManager::get()->currentDocument();} protected: @@ -51,7 +51,7 @@ protected: {} /// Sets the data manager of an object (document does) - void setData(std::shared_ptr theData) {myData = theData;} + MODELAPI_EXPORT void setData(std::shared_ptr theData) {myData = theData;} friend class Model_Document; }; -- 2.39.2