]> SALOME platform Git repositories - modules/shaper.git/blob - src/Model/Model_Document.cxx
Salome HOME
Implementation of features cashing and undo/redo functionality in document.
[modules/shaper.git] / src / Model / Model_Document.cxx
1 // File:        Model_Document.cxx
2 // Created:     28 Feb 2014
3 // Author:      Mikhail PONIKAROV
4
5 #include <Model_Document.h>
6 #include <ModelAPI_Feature.h>
7 #include <Model_Object.h>
8 #include <Model_Application.h>
9 #include <Model_PluginManager.h>
10 #include <Model_Iterator.h>
11 #include <Event_Loop.h>
12
13 #include <TDataStd_Integer.hxx>
14 #include <TDataStd_Comment.hxx>
15 #include <TDF_ChildIDIterator.hxx>
16
17 static const int UNDO_LIMIT = 10; // number of possible undo operations
18
19 static const int TAG_GENERAL = 1; // general properties tag
20 static const int TAG_OBJECTS = 2; // tag of the objects sub-tree (Root for Model_ObjectsMgr)
21 static const int TAG_HISTORY = 3; // tag of the history sub-tree (Root for Model_History)
22
23 using namespace std;
24
25 bool Model_Document::load(const char* theFileName)
26 {
27   bool myIsError = Standard_False;
28   /*
29    TCollection_ExtendedString aPath ((const Standard_CString)theFileName);
30    PCDM_ReaderStatus aStatus = (PCDM_ReaderStatus) -1;
31    try
32    {
33    Handle(TDocStd_Document) aDoc = this;
34    aStatus = Model_Application::GetApplication()->Open(aPath, aDoc);
35    }
36    catch (Standard_Failure)
37    {}
38    myIsError = aStatus != PCDM_RS_OK;
39    if (myIsError)
40    {
41    switch (aStatus)
42    {
43    case PCDM_RS_UnknownDocument: cout<<"OCAFApp_Appl_RUnknownDocument"<<endl; break;
44    case PCDM_RS_AlreadyRetrieved: cout<<"OCAFApp_Appl_RAlreadyRetrieved"<<endl; break;
45    case PCDM_RS_AlreadyRetrievedAndModified: cout<<"OCAFApp_Appl_RAlreadyRetrievedAndModified"<<endl; break;
46    case PCDM_RS_NoDriver: cout<<"OCAFApp_Appl_RNoDriver"<<endl; break;
47    case PCDM_RS_UnknownFileDriver: cout<<"OCAFApp_Appl_RNoDriver"<<endl; break;
48    case PCDM_RS_OpenError: cout<<"OCAFApp_Appl_ROpenError"<<endl; break;
49    case PCDM_RS_NoVersion: cout<<"OCAFApp_Appl_RNoVersion"<<endl; break;
50    case PCDM_RS_NoModel: cout<<"OCAFApp_Appl_RNoModel"<<endl; break;
51    case PCDM_RS_NoDocument: cout<<"OCAFApp_Appl_RNoDocument"<<endl; break;
52    case PCDM_RS_FormatFailure: cout<<"OCAFApp_Appl_RFormatFailure"<<endl; break;
53    case PCDM_RS_TypeNotFoundInSchema: cout<<"OCAFApp_Appl_RTypeNotFound"<<endl; break;
54    case PCDM_RS_UnrecognizedFileFormat: cout<<"OCAFApp_Appl_RBadFileFormat"<<endl; break;
55    case PCDM_RS_MakeFailure: cout<<"OCAFApp_Appl_RMakeFailure"<<endl; break;
56    case PCDM_RS_PermissionDenied: cout<<"OCAFApp_Appl_RPermissionDenied"<<endl; break;
57    case PCDM_RS_DriverFailure: cout<<"OCAFApp_Appl_RDriverFailure"<<endl; break;
58    default: cout<<"OCAFApp_Appl_RUnknownFail"<<endl; break;
59    }
60    }
61    SetUndoLimit(UNDO_LIMIT);
62    */
63   return !myIsError;
64 }
65
66 bool Model_Document::save(const char* theFileName)
67 {
68   bool myIsError = true;
69   /*
70    TCollection_ExtendedString aPath ((const Standard_CString)theFileName);
71    PCDM_StoreStatus aStatus;
72    try {
73    Handle(TDocStd_Document) aDoc = this;
74    aStatus = Model_Application::GetApplication()->SaveAs (aDoc, aPath);
75    }
76    catch (Standard_Failure) {
77    Handle(Standard_Failure) aFail = Standard_Failure::Caught();
78    cout<<"OCAFApp_Engine:save Error: "<<aFail->GetMessageString()<<endl;
79    return false;
80    }
81    myIsError = aStatus != PCDM_SS_OK;
82    if (myIsError)
83    {
84    switch (aStatus)
85    {
86    case PCDM_SS_DriverFailure:
87    cout<<"OCAFApp_Appl_SDriverFailure"<<endl;
88    break;
89    case PCDM_SS_WriteFailure:
90    cout<<"OCAFApp_Appl_SWriteFailure"<<endl;
91    break;
92    case PCDM_SS_Failure:
93    default:
94    cout<<"OCAFApp_Appl_SUnknownFailure"<<endl;
95    break;
96    }
97    }
98    myTransactionsAfterSave = 0;
99    Standard::Purge(); // Release free memory
100    */
101   return !myIsError;
102 }
103
104 void Model_Document::close()
105 {
106   // close all subs
107   set<string>::iterator aSubIter = mySubs.begin();
108   for(; aSubIter != mySubs.end(); aSubIter++)
109     subDocument(*aSubIter)->close();
110   mySubs.clear();
111   // close this
112   myDoc->Close();
113   Model_Application::getApplication()->deleteDocument(myID);
114 }
115
116 void Model_Document::startOperation()
117 {
118   // new command for this
119   myDoc->NewCommand();
120   // new command for all subs
121   set<string>::iterator aSubIter = mySubs.begin();
122   for(; aSubIter != mySubs.end(); aSubIter++)
123     subDocument(*aSubIter)->startOperation();
124 }
125
126 void Model_Document::finishOperation()
127 {
128   // returns false if delta is empty and no transaction was made
129   myIsEmptyTr[myTransactionsAfterSave] = !myDoc->CommitCommand();
130   myTransactionsAfterSave++;
131   // finish for all subs
132   set<string>::iterator aSubIter = mySubs.begin();
133   for(; aSubIter != mySubs.end(); aSubIter++)
134     subDocument(*aSubIter)->finishOperation();
135 }
136
137 void Model_Document::abortOperation()
138 {
139   myDoc->AbortCommand();
140   // abort for all subs
141   set<string>::iterator aSubIter = mySubs.begin();
142   for(; aSubIter != mySubs.end(); aSubIter++)
143     subDocument(*aSubIter)->abortOperation();
144 }
145
146 bool Model_Document::isOperation()
147 {
148   // operation is opened for all documents: no need to check subs
149   return myDoc->HasOpenCommand() == Standard_True ;
150 }
151
152 bool Model_Document::isModified()
153 {
154   // is modified if at least one operation was commited and not undoed
155   return myTransactionsAfterSave > 0;
156 }
157
158 bool Model_Document::canUndo()
159 {
160   if (myDoc->GetAvailableUndos() > 0)
161     return true;
162   // check other subs contains operation that can be undoed
163   set<string>::iterator aSubIter = mySubs.begin();
164   for(; aSubIter != mySubs.end(); aSubIter++)
165     if (subDocument(*aSubIter)->canUndo())
166       return true;
167   return false;
168 }
169
170 void Model_Document::undo()
171 {
172   myTransactionsAfterSave--;
173   if (!myIsEmptyTr[myTransactionsAfterSave])
174     myDoc->Undo();
175   synchronizeFeatures();
176   // undo for all subs
177   set<string>::iterator aSubIter = mySubs.begin();
178   for(; aSubIter != mySubs.end(); aSubIter++)
179     subDocument(*aSubIter)->undo();
180 }
181
182 bool Model_Document::canRedo()
183 {
184   if (myDoc->GetAvailableRedos() > 0)
185     return true;
186   // check other subs contains operation that can be redoed
187   set<string>::iterator aSubIter = mySubs.begin();
188   for(; aSubIter != mySubs.end(); aSubIter++)
189     if (subDocument(*aSubIter)->canRedo())
190       return true;
191   return false;
192 }
193
194 void Model_Document::redo()
195 {
196   if (!myIsEmptyTr[myTransactionsAfterSave])
197     myDoc->Redo();
198   myTransactionsAfterSave++;
199   synchronizeFeatures();
200   // redo for all subs
201   set<string>::iterator aSubIter = mySubs.begin();
202   for(; aSubIter != mySubs.end(); aSubIter++)
203     subDocument(*aSubIter)->redo();
204 }
205
206 shared_ptr<ModelAPI_Feature> Model_Document::addFeature(string theID)
207 {
208   shared_ptr<ModelAPI_Feature> aFeature = ModelAPI_PluginManager::get()->createFeature(theID);
209   if (aFeature) {
210     dynamic_pointer_cast<Model_Document>(aFeature->documentToAdd())->addFeature(aFeature);
211   } else {
212     // TODO: generate error that feature is not created
213   }
214   return aFeature;
215 }
216
217 void Model_Document::addFeature(const std::shared_ptr<ModelAPI_Feature> theFeature)
218 {
219   const std::string& aGroup = theFeature->getGroup();
220   TDF_Label aGroupLab = groupLabel(aGroup);
221   TDF_Label anObjLab = aGroupLab.NewChild();
222   std::shared_ptr<Model_Object> aData(new Model_Object);
223   aData->setLabel(anObjLab);
224   aData->setDocument(Model_Application::getApplication()->getDocument(myID));
225   theFeature->setData(aData);
226   setUniqueName(theFeature);
227   theFeature->initAttributes();
228   // keep the feature ID to restore document later correctly
229   TDataStd_Comment::Set(anObjLab, theFeature->getKind().c_str());
230   // put index of the feature in the group in the tree
231   TDataStd_Integer::Set(anObjLab, myFeatures[aGroup].size());
232   myFeatures[aGroup].push_back(theFeature);
233
234   // event: model is updated
235   static Event_ID anEvent = Event_Loop::eventByName(EVENT_FEATURE_UPDATED);
236   ModelAPI_FeatureUpdatedMessage aMsg(theFeature);
237   Event_Loop::loop()->send(aMsg);
238 }
239
240 shared_ptr<ModelAPI_Feature> Model_Document::feature(TDF_Label& theLabel)
241 {
242   Handle(TDataStd_Integer) aFeatureIndex;
243   if (theLabel.FindAttribute(TDataStd_Integer::GetID(), aFeatureIndex)) {
244     Handle(TDataStd_Comment) aGroupID;
245     if (theLabel.Father().FindAttribute(TDataStd_Comment::GetID(), aGroupID)) {
246       string aGroup = TCollection_AsciiString(aGroupID->Get()).ToCString();
247       return myFeatures[aGroup][aFeatureIndex->Get()];
248     }
249   }
250   return std::shared_ptr<ModelAPI_Feature>(); // not found
251 }
252
253 int Model_Document::featureIndex(shared_ptr<ModelAPI_Feature> theFeature)
254 {
255   if (theFeature->data()->document().get() != this) {
256     return theFeature->data()->document()->featureIndex(theFeature);
257   }
258   shared_ptr<Model_Object> aData = dynamic_pointer_cast<Model_Object>(theFeature->data());
259   Handle(TDataStd_Integer) aFeatureIndex;
260   if (aData->label().FindAttribute(TDataStd_Integer::GetID(), aFeatureIndex)) {
261     return aFeatureIndex->Get();
262   }
263   return -1; // not found
264 }
265
266 shared_ptr<ModelAPI_Document> Model_Document::subDocument(string theDocID)
267 {
268   // just store sub-document identifier here to manage it later
269   if (mySubs.find(theDocID) == mySubs.end())
270     mySubs.insert(theDocID);
271   return Model_Application::getApplication()->getDocument(theDocID);
272 }
273
274 shared_ptr<ModelAPI_Iterator> Model_Document::featuresIterator(const string theGroup)
275 {
276   shared_ptr<Model_Document> aThis(Model_Application::getApplication()->getDocument(myID));
277   // create an empty iterator for not existing group 
278   // (to avoidance of attributes management outside the transaction)
279   if (myGroups.find(theGroup) == myGroups.end())
280     return shared_ptr<ModelAPI_Iterator>(new Model_Iterator());
281   return shared_ptr<ModelAPI_Iterator>(new Model_Iterator(aThis, groupLabel(theGroup)));
282 }
283
284 shared_ptr<ModelAPI_Feature> Model_Document::feature(const string& theGroupID, const int theIndex)
285 {
286   // TODO: optimize this method
287   shared_ptr<ModelAPI_Iterator>  anIter = featuresIterator(theGroupID);
288   for(int a = 0; a != theIndex && anIter->more(); anIter->next()) a++;
289   return anIter->more() ? anIter->current() : shared_ptr<ModelAPI_Feature>();
290 }
291
292 const vector<string>& Model_Document::getGroups() const
293 {
294   return myGroupsNames;
295 }
296
297 Model_Document::Model_Document(const std::string theID)
298     : myID(theID), myDoc(new TDocStd_Document("BinOcaf")) // binary OCAF format
299 {
300   myDoc->SetUndoLimit(UNDO_LIMIT);
301   myTransactionsAfterSave = 0;
302   // to avoid creation of tag outside of the transaction (by iterator, for example)
303   /*
304   if (!myDoc->Main().FindChild(TAG_OBJECTS).IsAttribute(TDF_TagSource::GetID()))
305     TDataStd_Comment::Set(myDoc->Main().FindChild(TAG_OBJECTS).NewChild(), "");
306     */
307 }
308
309 TDF_Label Model_Document::groupLabel(const string theGroup)
310 {
311   if (myGroups.find(theGroup) == myGroups.end()) {
312     myGroups[theGroup] = myDoc->Main().FindChild(TAG_OBJECTS).NewChild();
313     myGroupsNames.push_back(theGroup);
314     // set to the group label the group idntifier to restore on "open"
315     TDataStd_Comment::Set(myGroups[theGroup], theGroup.c_str());
316     myFeatures[theGroup] = vector<shared_ptr<ModelAPI_Feature> >();
317   }
318   return myGroups[theGroup];
319 }
320
321 void Model_Document::setUniqueName(
322   shared_ptr<ModelAPI_Feature> theFeature)
323 {
324   // first count all objects of such kind to start with index = count + 1
325   int aNumObjects = 0;
326   shared_ptr<ModelAPI_Iterator> anIter = featuresIterator(theFeature->getGroup());
327   for(; anIter->more(); anIter->next()) {
328     if (anIter->currentKind() == theFeature->getKind())
329       aNumObjects++;
330   }
331   // generate candidate name
332   stringstream aNameStream;
333   aNameStream<<theFeature->getKind()<<"_"<<aNumObjects + 1;
334   string aName = aNameStream.str();
335   // check this is unique, if not, increase index by 1
336   for(anIter = featuresIterator(theFeature->getGroup()); anIter->more();) {
337     if (anIter->currentName() == aName) {
338       aNumObjects++;
339       stringstream aNameStream;
340       aNameStream<<theFeature->getKind()<<"_"<<aNumObjects + 1;
341       // reinitialize iterator to make sure a new name is unique
342       anIter = featuresIterator(theFeature->getGroup());
343     } else anIter->next();
344   }
345
346   theFeature->data()->setName(aName);
347 }
348
349 void Model_Document::synchronizeFeatures()
350 {
351   // iterate groups labels
352   TDF_ChildIDIterator aGroupsIter(myDoc->Main().FindChild(TAG_OBJECTS),
353     TDataStd_Comment::GetID(), Standard_False);
354   vector<string>::iterator aGroupNamesIter = myGroupsNames.begin();
355   for(; aGroupsIter.More() && aGroupNamesIter != myGroupsNames.end();
356         aGroupsIter.Next(), aGroupNamesIter++) {
357     string aGroupName = TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(
358       aGroupsIter.Value())->Get()).ToCString();
359     if (*aGroupNamesIter != aGroupName) 
360       break; // all since there is a change this must be recreated from scratch
361   }
362   // delete all groups left after the data model groups iteration
363   while(aGroupNamesIter != myGroupsNames.end()) {
364     myFeatures.erase(*aGroupNamesIter);
365     myGroups.erase(*aGroupNamesIter);
366     aGroupNamesIter = myGroupsNames.erase(aGroupNamesIter);
367   }
368   // create new groups basing on the following data model update
369   for(; aGroupsIter.More(); aGroupsIter.Next()) {
370     string aGroupName = TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(
371       aGroupsIter.Value())->Get()).ToCString();
372     myGroupsNames.push_back(aGroupName);
373     myGroups[aGroupName] = aGroupsIter.Value()->Label();
374     myFeatures[aGroupName] = vector<shared_ptr<ModelAPI_Feature> >();
375   }
376   // update features group by group
377   aGroupsIter.Initialize(myDoc->Main().FindChild(TAG_OBJECTS),
378     TDataStd_Comment::GetID(), Standard_False);
379   for(; aGroupsIter.More(); aGroupsIter.Next()) {
380     string aGroupName = TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(
381       aGroupsIter.Value())->Get()).ToCString();
382     // iterate features in internal container
383     vector<shared_ptr<ModelAPI_Feature> >& aFeatures = myFeatures[aGroupName];
384     vector<shared_ptr<ModelAPI_Feature> >::iterator aFIter = aFeatures.begin();
385     // and in parallel iterate labels of features
386     TDF_ChildIDIterator aFLabIter(
387       aGroupsIter.Value()->Label(), TDataStd_Comment::GetID(), Standard_False);
388     while(aFIter != aFeatures.end() || aFLabIter.More()) {
389       static const int INFINITE_TAG = MAXINT; // no label means that it exists somwhere in infinite
390       int aFeatureTag = INFINITE_TAG; 
391       if (aFIter != aFeatures.end()) { // existing tag for feature
392         shared_ptr<Model_Object> aData = dynamic_pointer_cast<Model_Object>((*aFIter)->data());
393         aFeatureTag = aData->label().Tag();
394       }
395       int aDSTag = INFINITE_TAG; 
396       if (aFLabIter.More()) { // next label in DS is existing
397         aDSTag = aFLabIter.Value()->Label().Tag();
398       }
399       if (aDSTag > aFeatureTag) { // feature is removed
400         aFIter = aFeatures.erase(aFIter);
401       } else if (aDSTag < aFeatureTag) { // a new feature is inserted
402         // create a feature
403         shared_ptr<ModelAPI_Feature> aFeature = ModelAPI_PluginManager::get()->createFeature(
404           TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(
405           aFLabIter.Value())->Get()).ToCString());
406
407         std::shared_ptr<Model_Object> aData(new Model_Object);
408         TDF_Label aLab = aFLabIter.Value()->Label();
409         aData->setLabel(aLab);
410         aData->setDocument(Model_Application::getApplication()->getDocument(myID));
411         aFeature->setData(aData);
412         aFeature->initAttributes();
413         // event: model is updated
414         static Event_ID anEvent = Event_Loop::eventByName(EVENT_FEATURE_UPDATED);
415         ModelAPI_FeatureUpdatedMessage aMsg(aFeature);
416         Event_Loop::loop()->send(aMsg);
417
418         if (aFIter == aFeatures.end()) {
419           aFeatures.push_back(aFeature);
420           aFIter = aFeatures.end();
421         } else {
422           aFIter++;
423           aFeatures.insert(aFIter, aFeature);
424         }
425         // feature for this label is added, so go to the next label
426         aFLabIter.Next();
427       } else { // nothing is changed, both iterators are incremented
428         aFIter++;
429         aFLabIter.Next();
430       }
431     }
432   }
433 }
434
435 ModelAPI_FeatureUpdatedMessage::ModelAPI_FeatureUpdatedMessage(
436   shared_ptr<ModelAPI_Feature> theFeature)
437   : Event_Message(messageId(), 0), myFeature(theFeature)
438 {}
439
440 const Event_ID ModelAPI_FeatureUpdatedMessage::messageId()
441 {
442   static Event_ID MY_ID = Event_Loop::eventByName("FeatureUpdated");
443   return MY_ID;
444 }
445
446 shared_ptr<ModelAPI_Feature> ModelAPI_FeatureUpdatedMessage::feature()
447 {
448   return myFeature;
449 }